diff --git a/db/migrate/20241009000001_add_workspace_delayed_termination_fields.rb b/db/migrate/20241009000001_add_workspace_delayed_termination_fields.rb new file mode 100644 index 0000000000000000000000000000000000000000..0e0b0bf2e847b5780ff7f40bae50e0eb3da23695 --- /dev/null +++ b/db/migrate/20241009000001_add_workspace_delayed_termination_fields.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class AddWorkspaceDelayedTerminationFields < Gitlab::Database::Migration[2.2] + milestone '17.6' + disable_ddl_transaction! + + def up + with_lock_retries do + # Default max_active_hours_before_stop to 1.5 days, so that workspace should usually + # stop outside of working hours, assuming it was last restarted during working hours. + add_column :workspaces_agent_configs, :max_active_hours_before_stop, :smallint, default: 36, null: false, + if_not_exists: true + # Default max_stopped_hours_before_termination to 1 month (31 days) + add_column :workspaces_agent_configs, :max_stopped_hours_before_termination, :smallint, default: 744, null: false, + if_not_exists: true + end + end + + def down + with_lock_retries do + remove_column :workspaces_agent_configs, :max_active_hours_before_stop, if_exists: true + remove_column :workspaces_agent_configs, :max_stopped_hours_before_termination, if_exists: true + end + end +end diff --git a/db/migrate/20241009000002_add_workspace_delayed_termination_constraints.rb b/db/migrate/20241009000002_add_workspace_delayed_termination_constraints.rb new file mode 100644 index 0000000000000000000000000000000000000000..9f0cb8fd4f8f582ad929b168456e195b572b9d3c --- /dev/null +++ b/db/migrate/20241009000002_add_workspace_delayed_termination_constraints.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class AddWorkspaceDelayedTerminationConstraints < Gitlab::Database::Migration[2.2] + milestone "17.6" + disable_ddl_transaction! + + TABLE_NAME = :workspaces_agent_configs + + def constraint_1_name + check_constraint_name TABLE_NAME, + "max_active_hours_before_stop and max_stopped_hours_before_termination", "max_total_size_1_year" + end + + def constraint_2_name + check_constraint_name TABLE_NAME, :max_active_hours_before_stop, "min_size_0" + end + + def constraint_3_name + check_constraint_name TABLE_NAME, :max_stopped_hours_before_termination, "min_size_0" + end + + def up + add_check_constraint TABLE_NAME, + "(max_active_hours_before_stop + max_stopped_hours_before_termination) <= 8760", constraint_1_name + add_check_constraint TABLE_NAME, "max_active_hours_before_stop > 0", constraint_2_name + add_check_constraint TABLE_NAME, "max_stopped_hours_before_termination > 0", constraint_3_name + end + + def down + remove_check_constraint TABLE_NAME, constraint_1_name + remove_check_constraint TABLE_NAME, constraint_2_name + remove_check_constraint TABLE_NAME, constraint_3_name + end +end diff --git a/db/schema_migrations/20241009000001 b/db/schema_migrations/20241009000001 new file mode 100644 index 0000000000000000000000000000000000000000..af64f2d823cfd6e4347c062e82896976752a728e --- /dev/null +++ b/db/schema_migrations/20241009000001 @@ -0,0 +1 @@ +c1f9b34f09c731c413ddd313e1b37dbc4fb23075f992b3872be421f07ec4ca27 \ No newline at end of file diff --git a/db/schema_migrations/20241009000002 b/db/schema_migrations/20241009000002 new file mode 100644 index 0000000000000000000000000000000000000000..193900d599f6e69db8dbfed3378b91daeaf96247 --- /dev/null +++ b/db/schema_migrations/20241009000002 @@ -0,0 +1 @@ +ef1526d594ef3f8e867fdcabcf14f407e682f6b7e04f2a4fd89ca2fc007f6d8c \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index aec532237c374926b9dcb1ea8545121075eee63e..6fe99675759671e285b664cec15bfcaff0e6f186 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -21986,9 +21986,14 @@ CREATE TABLE workspaces_agent_configs ( annotations jsonb DEFAULT '{}'::jsonb NOT NULL, labels jsonb DEFAULT '{}'::jsonb NOT NULL, image_pull_secrets jsonb DEFAULT '[]'::jsonb NOT NULL, + max_active_hours_before_stop smallint DEFAULT 36 NOT NULL, + max_stopped_hours_before_termination smallint DEFAULT 744 NOT NULL, + CONSTRAINT check_557e75a230 CHECK ((max_stopped_hours_before_termination > 0)), CONSTRAINT check_58759a890a CHECK ((char_length(dns_zone) <= 256)), + CONSTRAINT check_6d7baef494 CHECK (((max_active_hours_before_stop + max_stopped_hours_before_termination) <= 8760)), CONSTRAINT check_720388a28c CHECK ((char_length(default_runtime_class) <= 253)), CONSTRAINT check_dca877fba1 CHECK ((default_max_hours_before_termination <= 8760)), + CONSTRAINT check_df26c047a9 CHECK ((max_active_hours_before_stop > 0)), CONSTRAINT check_eab6e375ad CHECK ((max_hours_before_termination_limit <= 8760)), CONSTRAINT check_ee2464835c CHECK ((char_length(gitlab_workspaces_proxy_namespace) <= 63)) ); diff --git a/doc/user/workspace/gitlab_agent_configuration.md b/doc/user/workspace/gitlab_agent_configuration.md index 8c21ab5d9688068a158db476649ea1fe16984db9..e3530cd6ce19e09469e2e99ac9ee57b02002652f 100644 --- a/doc/user/workspace/gitlab_agent_configuration.md +++ b/doc/user/workspace/gitlab_agent_configuration.md @@ -112,6 +112,8 @@ you can use any configured agent in `top-level-group` and in any of its subgroup | [`allow_privilege_escalation`](#allow_privilege_escalation) | No | `false` | Allow privilege escalation. | | [`annotations`](#annotations) | No | `{}` | Annotations to apply to Kubernetes objects. | | [`labels`](#labels) | No | `{}` | Labels to apply to Kubernetes objects. | +| [`max_active_hours_before_stop`](#max_active_hours_before_stop) | No | `36` | Maximum number of hours a workspace can be active before it is stopped. | +| [`max_stopped_hours_before_termination`](#max_stopped_hours_before_termination) | No | `744` | Maximum number of hours a workspace can be stopped before it is terminated. | NOTE: If a setting has an invalid value, it's not possible to update any setting until you fix that value. @@ -464,6 +466,64 @@ A valid label value: For more information about `labels`, see [Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/). +### `max_active_hours_before_stop` + +> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/14910) in GitLab 17.6. + +Use this setting to automatically stop the agent's workspaces after the specified number of hours +have passed since the workspace last transitioned to an active state. +An "active state" is defined as any non-stopped or non-terminated state. + +The timer for this setting starts when the workspace is created, and is reset every time the +workspace is restarted. +It also applies even if the workspace is in an error or failure state. + +The default value is `36`, or one and a half days. This avoids stopping the workspace during +the user's normal working hours. + +**Example configuration:** + +```yaml +remote_development: + max_active_hours_before_stop: 60 +``` + +A valid value: + +- Is an integer. +- Is greater than or equal to `1`. +- Is less than or equal to `8760` (one year). +- `max_active_hours_before_stop` + `max_stopped_hours_before_termination` must be less than or equal to `8760`. + +The automatic stop is only triggered on a full reconciliation, which happens every hour. +This means that the workspace might be active for up to one hour longer than the configured value. + +### `max_stopped_hours_before_termination` + +> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/14910) in GitLab 17.6. + +Use this setting to automatically terminate the agent's workspaces after they have been in the stopped +state for the specified number of hours. + +The default value is `722`, or approximately one month. + +**Example configuration:** + +```yaml +remote_development: + max_stopped_hours_before_termination: 4332 +``` + +A valid value: + +- Is an integer. +- Is greater than or equal to `1`. +- Is less than or equal to `8760` (one year). +- `max_active_hours_before_stop` + `max_stopped_hours_before_termination` must be less than or equal to `8760`. + +The automatic termination is only triggered on a full reconciliation, which happens every hour. +This means that the workspace might be stopped for up to one hour longer than the configured value. + ## Configuring user access with remote development You can configure the `user_access` module to access the connected Kubernetes cluster with your GitLab credentials. diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md index ee7303d1ff6fd6a6fb1a8f58db2b8da559eb3f5d..0dc53a969a1e55860d6191f6d6eda20b2e8ae824 100644 --- a/doc/user/workspace/index.md +++ b/doc/user/workspace/index.md @@ -247,6 +247,13 @@ However, the volume provisioned for the workspace still exists. To delete the provisioned volume, you must terminate the workspace. +## Automatic workspace stop and termination + +1. A workspace will automatically stop 36 hours after it was last started or restarted. + For more details, see [the agent configuration setting documentation for `max_active_hours_before_stop`](gitlab_agent_configuration.md#max_active_hours_before_stop). +1. A workspace will automatically terminate 722 hours after it was last stopped. + For more details, see [the agent configuration setting documentation for `max_stopped_hours_before_termination`](gitlab_agent_configuration.md#max_stopped_hours_before_termination). + ## Arbitrary user IDs You can provide your own container image, which can run as any Linux user ID. diff --git a/ee/app/assets/javascripts/workspaces/common/components/workspaces_list/workspaces_table.vue b/ee/app/assets/javascripts/workspaces/common/components/workspaces_list/workspaces_table.vue index a4ead1ee31b78c6af4b87dd7b6c1d74e06a078d1..f373be423abd770b0e43c876bffcbc2a280e256f 100644 --- a/ee/app/assets/javascripts/workspaces/common/components/workspaces_list/workspaces_table.vue +++ b/ee/app/assets/javascripts/workspaces/common/components/workspaces_list/workspaces_table.vue @@ -1,5 +1,4 @@