diff --git a/Gemfile b/Gemfile index ecc2da6f0547b3c213ece8df485451b5c2d3853f..32d64b19f7cee253edc1821f104d510dcb52853c 100644 --- a/Gemfile +++ b/Gemfile @@ -587,6 +587,9 @@ gem 'cvss-suite', '~> 3.0.1', require: 'cvss_suite' # Work with RPM packages gem 'arr-pm', '~> 0.0.12' +# Remote Development +gem 'devfile', '~> 0.0.15.pre.alpha1' + # Apple plist parsing gem 'CFPropertyList', '~> 3.0.0' gem 'app_store_connect' diff --git a/Gemfile.checksum b/Gemfile.checksum index 69b15f3b3688b38f57fdd2ecb2a881704bc691b8..d260ad61dcd4c98f57ceec7d6e5ad1c3cc7fa49b 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -110,6 +110,9 @@ {"name":"deprecation_toolkit","version":"1.5.1","platform":"ruby","checksum":"a8a1ab1a19ae40ea12560b65010e099f3459ebde390b76621ef0c21c516a04ba"}, {"name":"derailed_benchmarks","version":"2.1.2","platform":"ruby","checksum":"eaadc6206ceeb5538ff8f5e04a0023d54ebdd95d04f33e8960fb95a5f189a14f"}, {"name":"descendants_tracker","version":"0.0.4","platform":"ruby","checksum":"e9c41dd4cfbb85829a9301ea7e7c48c2a03b26f09319db230e6479ccdc780897"}, +{"name":"devfile","version":"0.0.15.pre.alpha1","platform":"arm64-darwin","checksum":"8e8234d552310925758dd5f214e1fc059ecc7255f8d692300d1adb955c370198"}, +{"name":"devfile","version":"0.0.15.pre.alpha1","platform":"ruby","checksum":"69a058d9edb44efe2fb78769e531b324f3bd75b21d45be15d8b0335b691d093e"}, +{"name":"devfile","version":"0.0.15.pre.alpha1","platform":"x86_64-linux","checksum":"460ddad57cc69a293661bb82ebc19ca44147201c74f78b36bb4861b402ecff24"}, {"name":"device_detector","version":"1.0.0","platform":"ruby","checksum":"b800fb3150b00c23e87b6768011808ac1771fffaae74c3238ebaf2b782947a7d"}, {"name":"devise","version":"4.8.1","platform":"ruby","checksum":"fdd48bbe79a89e7c1152236a70479842ede48bea4fa7f4f2d8da1f872559803e"}, {"name":"devise-two-factor","version":"4.0.2","platform":"ruby","checksum":"6548d2696ed090d27046f888f4fa7380f151e0f823902d46fd9b91e7d0cac511"}, diff --git a/Gemfile.lock b/Gemfile.lock index 2d2f2fa783845f114446a861ddf375c634036b75..1ca62011875b7105a2e9d6e9835fbec25637724a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -376,6 +376,7 @@ GEM thor (>= 0.19, < 2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) + devfile (0.0.15.pre.alpha1) device_detector (1.0.0) devise (4.8.1) bcrypt (~> 3.0) @@ -1713,6 +1714,7 @@ DEPENDENCIES declarative_policy (~> 1.1.0) deprecation_toolkit (~> 1.5.1) derailed_benchmarks + devfile (~> 0.0.15.pre.alpha1) device_detector devise (~> 4.8.1) devise-pbkdf2-encryptable (~> 0.0.0)! diff --git a/app/assets/javascripts/graphql_shared/constants.js b/app/assets/javascripts/graphql_shared/constants.js index 5ba4669749603dfc00db00380ee8a9fd8c46cb7b..0642f157b385acbaef37f4338f1ab6b3b6579a12 100644 --- a/app/assets/javascripts/graphql_shared/constants.js +++ b/app/assets/javascripts/graphql_shared/constants.js @@ -27,5 +27,6 @@ export const TYPENAME_USER = 'User'; export const TYPENAME_VULNERABILITIES_SCANNER = 'Vulnerabilities::Scanner'; export const TYPENAME_VULNERABILITY = 'Vulnerability'; export const TYPENAME_WORK_ITEM = 'WorkItem'; +export const TYPE_CLUSTERS_AGENT = 'Clusters::Agent'; export const TYPE_USERS_SAVED_REPLY = 'Users::SavedReply'; export const TYPE_WORKSPACE = 'RemoteDevelopment::Workspace'; diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index 267bd3f9506a37f9dc7e9d3e080355e18f176d56..08c4efce542ea172624f595ced9203731d8737b2 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -149,3 +149,7 @@ .gl-fill-orange-500 { fill: $orange-500; } + +.gl-fill-red-500 { + fill: $red-500; +} diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb index 83d2f3f830a81c9962b6a8390a0a52fa2a356b8b..64fc069b508950b86b24bf3071ac5fe299268031 100644 --- a/app/graphql/types/user_interface.rb +++ b/app/graphql/types/user_interface.rb @@ -175,3 +175,5 @@ def redacted_name end end end + +Types::UserInterface.prepend_mod diff --git a/db/docs/remote_development_agent_configs.yml b/db/docs/remote_development_agent_configs.yml new file mode 100644 index 0000000000000000000000000000000000000000..0de90d1777caf5653a0c63738116599bdb39f325 --- /dev/null +++ b/db/docs/remote_development_agent_configs.yml @@ -0,0 +1,10 @@ +--- +table_name: remote_development_agent_configs +classes: +- RemoteDevelopment::RemoteDevelopmentAgentConfig +feature_categories: +- remote_development +description: Remote Development Cluster Agent Configuration +introduced_by_url: +milestone: +gitlab_schema: gitlab_main diff --git a/db/docs/workspaces.yml b/db/docs/workspaces.yml new file mode 100644 index 0000000000000000000000000000000000000000..e9983416e3181abe19c77af9aebbe942538aabd4 --- /dev/null +++ b/db/docs/workspaces.yml @@ -0,0 +1,10 @@ +--- +table_name: workspaces +classes: +- RemoteDevelopment::Workspace +feature_categories: +- remote_development +description: Remote Development Workspaces +introduced_by_url: +milestone: +gitlab_schema: gitlab_main diff --git a/db/migrate/20221225010101_create_workspaces_table.rb b/db/migrate/20221225010101_create_workspaces_table.rb new file mode 100644 index 0000000000000000000000000000000000000000..4c8bc26bcf651fbb82559e2781b6711377f0e5e7 --- /dev/null +++ b/db/migrate/20221225010101_create_workspaces_table.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class CreateWorkspacesTable < Gitlab::Database::Migration[2.1] + def up + create_table :workspaces do |t| + t.timestamps_with_timezone null: false + # NOTE: All workspace foreign key references are currently `on_delete: :cascade`, because we have no support or + # testing around null values. However, in the future we may want to switch these to nullify, especially + # once we start introducing logging, metrics, billing, etc. around workspaces. + t.bigint :user_id, null: false, index: true + t.bigint :project_id, null: false, index: true + t.bigint :cluster_agent_id, null: false, index: true + t.datetime_with_timezone :desired_state_updated_at, null: false + t.datetime_with_timezone :responded_to_agent_at + t.integer :max_hours_before_termination, limit: 2, null: false + t.text :name, limit: 64, null: false, index: { unique: true } + t.text :namespace, limit: 64, null: false + t.text :desired_state, limit: 32, null: false + t.text :actual_state, limit: 32, null: false + t.text :editor, limit: 256, null: false + t.text :devfile_ref, limit: 256, null: false + t.text :devfile_path, limit: 2048, null: false + # NOTE: The limit on the devfile fields are arbitrary, and only added to avoid a rubocop + # Migration/AddLimitToTextColumns error. We expect the average devfile side to be small, perhaps ~0.5k for a + # devfile and ~2k for a processed_devfile, but to account for unexpected usage resulting in larger files, + # we have specified 65535, which allows for a YAML file with over 800 lines of an average 80-character + # length. + t.text :devfile, limit: 65535 + t.text :processed_devfile, limit: 65535 + t.text :url, limit: 1024, null: false + # NOTE: The resource version is currently backed by etcd's mod_revision. + # However, it's important to note that the application should not rely on the implementation details of + # the versioning system maintained by Kubernetes. We may change the implementation of resource version + # in the future, such as to change it to a timestamp or per-object counter. + # https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + # The limit of 64 is arbitrary. + t.text :deployment_resource_version, limit: 64 + end + end + + def down + drop_table :workspaces + end +end diff --git a/db/migrate/20221225010102_create_workspaces_user_foreign_key.rb b/db/migrate/20221225010102_create_workspaces_user_foreign_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..f6c38f289d62f42cc4fcc7ba4af415c3a452f382 --- /dev/null +++ b/db/migrate/20221225010102_create_workspaces_user_foreign_key.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateWorkspacesUserForeignKey < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + # NOTE: All workspace foreign key references are currently `on_delete: :cascade`, because we have no support or + # testing around null values. However, in the future we may want to switch these to nullify, especially + # once we start introducing logging, metrics, billing, etc. around workspaces. + add_concurrent_foreign_key :workspaces, :users, column: :user_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :workspaces, column: :user_id + end + end +end diff --git a/db/migrate/20221225010103_create_workspaces_project_foreign_key.rb b/db/migrate/20221225010103_create_workspaces_project_foreign_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..fe2b6eec2e01edee81584aba3aa7e4d4aa5b0351 --- /dev/null +++ b/db/migrate/20221225010103_create_workspaces_project_foreign_key.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateWorkspacesProjectForeignKey < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + # NOTE: All workspace foreign key references are currently `on_delete: :cascade`, because we have no support or + # testing around null values. However, in the future we may want to switch these to nullify, especially + # once we start introducing logging, metrics, billing, etc. around workspaces. + add_concurrent_foreign_key :workspaces, :projects, column: :project_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :workspaces, column: :project_id + end + end +end diff --git a/db/migrate/20221225010104_create_workspaces_cluster_agent_foreign_key.rb b/db/migrate/20221225010104_create_workspaces_cluster_agent_foreign_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..c7874349e861d93335c7b41b1404ffda0ddf71fd --- /dev/null +++ b/db/migrate/20221225010104_create_workspaces_cluster_agent_foreign_key.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateWorkspacesClusterAgentForeignKey < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + # NOTE: All workspace foreign key references are currently `on_delete: :cascade`, because we have no support or + # testing around null values. However, in the future we may want to switch these to nullify, especially + # once we start introducing logging, metrics, billing, etc. around workspaces. + add_concurrent_foreign_key :workspaces, :cluster_agents, column: :cluster_agent_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :workspaces, column: :cluster_agent_id + end + end +end diff --git a/db/migrate/20221225010105_create_remote_development_agent_configs_table.rb b/db/migrate/20221225010105_create_remote_development_agent_configs_table.rb new file mode 100644 index 0000000000000000000000000000000000000000..f375f78b616099cfcc31339d184e4b525f58ed53 --- /dev/null +++ b/db/migrate/20221225010105_create_remote_development_agent_configs_table.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateRemoteDevelopmentAgentConfigsTable < Gitlab::Database::Migration[2.1] + def up + create_table :remote_development_agent_configs do |t| + t.timestamps_with_timezone null: false + t.bigint :cluster_agent_id, null: false, index: true + t.boolean :enabled, null: false + t.text :dns_zone, null: false, limit: 256 + end + end + + def down + drop_table :remote_development_agent_configs + end +end diff --git a/db/migrate/20221225010106_create_remote_development_agent_config_agent_foreign_key.rb b/db/migrate/20221225010106_create_remote_development_agent_config_agent_foreign_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..b861f41716847824f541bb4716d7f2a89bf0397f --- /dev/null +++ b/db/migrate/20221225010106_create_remote_development_agent_config_agent_foreign_key.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateRemoteDevelopmentAgentConfigAgentForeignKey < Gitlab::Database::Migration[2.1] + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :remote_development_agent_configs, + :cluster_agents, column: :cluster_agent_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :remote_development_agent_configs, column: :cluster_agent_id + end + end +end diff --git a/db/schema_migrations/20221225010101 b/db/schema_migrations/20221225010101 new file mode 100644 index 0000000000000000000000000000000000000000..62d2d001438b771113c5af5a10eacca6091f70ff --- /dev/null +++ b/db/schema_migrations/20221225010101 @@ -0,0 +1 @@ +94810a223f2d37a673d690ba326577068c18d6353021a78a8f820cf8a95c756c \ No newline at end of file diff --git a/db/schema_migrations/20221225010102 b/db/schema_migrations/20221225010102 new file mode 100644 index 0000000000000000000000000000000000000000..8aacd082afcd12aabd447f86f6dfbd36029f5f38 --- /dev/null +++ b/db/schema_migrations/20221225010102 @@ -0,0 +1 @@ +74a3b48267b16dcd9d3374b01604a0ae7f55dd35e681e3bf6bf5386ea4f6bdc3 \ No newline at end of file diff --git a/db/schema_migrations/20221225010103 b/db/schema_migrations/20221225010103 new file mode 100644 index 0000000000000000000000000000000000000000..99590b1246f879fa421b34556fe171165817db8e --- /dev/null +++ b/db/schema_migrations/20221225010103 @@ -0,0 +1 @@ +bfa7df29a9f021b67db23127c6382161b131b77738f7a29dac5b64bc7431fd88 \ No newline at end of file diff --git a/db/schema_migrations/20221225010104 b/db/schema_migrations/20221225010104 new file mode 100644 index 0000000000000000000000000000000000000000..abbf974cda0dfc5c909daa14f0627fe2a9d6421b --- /dev/null +++ b/db/schema_migrations/20221225010104 @@ -0,0 +1 @@ +b2b2a169bb1d8581eec2706d03314d0675dcdf05b23b2787292b18ac1dfe7847 \ No newline at end of file diff --git a/db/schema_migrations/20221225010105 b/db/schema_migrations/20221225010105 new file mode 100644 index 0000000000000000000000000000000000000000..9f101f1aff31c054815e2f6468134265071e238b --- /dev/null +++ b/db/schema_migrations/20221225010105 @@ -0,0 +1 @@ +241ed02cdd479f06a5a4a817b2d27bfa970997167fbd67ddae1da8359830a2ea \ No newline at end of file diff --git a/db/schema_migrations/20221225010106 b/db/schema_migrations/20221225010106 new file mode 100644 index 0000000000000000000000000000000000000000..1499a3257eb5ff68ece1f4517d34ac6f1bdc1e39 --- /dev/null +++ b/db/schema_migrations/20221225010106 @@ -0,0 +1 @@ +08e0fd85bca9eff63f0fc5d1e34cca628ee191decddebcb90aaf98ce18f97147 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 695768fbfab1a0951a81781955d89d8e67f324c9..d47806f0c9bfe49ee421391d25799c3090acec6e 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -21783,6 +21783,25 @@ CREATE SEQUENCE releases_id_seq ALTER SEQUENCE releases_id_seq OWNED BY releases.id; +CREATE TABLE remote_development_agent_configs ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + cluster_agent_id bigint NOT NULL, + enabled boolean NOT NULL, + dns_zone text NOT NULL, + CONSTRAINT check_9f5cd54d1c CHECK ((char_length(dns_zone) <= 256)) +); + +CREATE SEQUENCE remote_development_agent_configs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE remote_development_agent_configs_id_seq OWNED BY remote_development_agent_configs.id; + CREATE TABLE remote_mirrors ( id integer NOT NULL, project_id integer, @@ -24557,6 +24576,49 @@ CREATE SEQUENCE work_item_widget_definitions_id_seq ALTER SEQUENCE work_item_widget_definitions_id_seq OWNED BY work_item_widget_definitions.id; +CREATE TABLE workspaces ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + user_id bigint NOT NULL, + project_id bigint NOT NULL, + cluster_agent_id bigint NOT NULL, + desired_state_updated_at timestamp with time zone NOT NULL, + responded_to_agent_at timestamp with time zone, + max_hours_before_termination smallint NOT NULL, + name text NOT NULL, + namespace text NOT NULL, + desired_state text NOT NULL, + actual_state text NOT NULL, + editor text NOT NULL, + devfile_ref text NOT NULL, + devfile_path text NOT NULL, + devfile text, + processed_devfile text, + url text NOT NULL, + deployment_resource_version text, + CONSTRAINT check_15543fb0fa CHECK ((char_length(name) <= 64)), + CONSTRAINT check_157d5f955c CHECK ((char_length(namespace) <= 64)), + CONSTRAINT check_2b401b0034 CHECK ((char_length(deployment_resource_version) <= 64)), + CONSTRAINT check_77d1a2ff50 CHECK ((char_length(processed_devfile) <= 65535)), + CONSTRAINT check_8e363ee3ad CHECK ((char_length(devfile_ref) <= 256)), + CONSTRAINT check_8e4db5ffc2 CHECK ((char_length(actual_state) <= 32)), + CONSTRAINT check_9e42558c35 CHECK ((char_length(url) <= 1024)), + CONSTRAINT check_b70eddcbc1 CHECK ((char_length(desired_state) <= 32)), + CONSTRAINT check_d7ed376e49 CHECK ((char_length(editor) <= 256)), + CONSTRAINT check_dc58d56169 CHECK ((char_length(devfile_path) <= 2048)), + CONSTRAINT check_eb32879a3d CHECK ((char_length(devfile) <= 65535)) +); + +CREATE SEQUENCE workspaces_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE workspaces_id_seq OWNED BY workspaces.id; + CREATE TABLE x509_certificates ( id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -25560,6 +25622,8 @@ ALTER TABLE ONLY release_links ALTER COLUMN id SET DEFAULT nextval('release_link ALTER TABLE ONLY releases ALTER COLUMN id SET DEFAULT nextval('releases_id_seq'::regclass); +ALTER TABLE ONLY remote_development_agent_configs ALTER COLUMN id SET DEFAULT nextval('remote_development_agent_configs_id_seq'::regclass); + ALTER TABLE ONLY remote_mirrors ALTER COLUMN id SET DEFAULT nextval('remote_mirrors_id_seq'::regclass); ALTER TABLE ONLY required_code_owners_sections ALTER COLUMN id SET DEFAULT nextval('required_code_owners_sections_id_seq'::regclass); @@ -25800,6 +25864,8 @@ ALTER TABLE ONLY work_item_types ALTER COLUMN id SET DEFAULT nextval('work_item_ ALTER TABLE ONLY work_item_widget_definitions ALTER COLUMN id SET DEFAULT nextval('work_item_widget_definitions_id_seq'::regclass); +ALTER TABLE ONLY workspaces ALTER COLUMN id SET DEFAULT nextval('workspaces_id_seq'::regclass); + ALTER TABLE ONLY x509_certificates ALTER COLUMN id SET DEFAULT nextval('x509_certificates_id_seq'::regclass); ALTER TABLE ONLY x509_commit_signatures ALTER COLUMN id SET DEFAULT nextval('x509_commit_signatures_id_seq'::regclass); @@ -27941,6 +28007,9 @@ ALTER TABLE releases ALTER TABLE ONLY releases ADD CONSTRAINT releases_pkey PRIMARY KEY (id); +ALTER TABLE ONLY remote_development_agent_configs + ADD CONSTRAINT remote_development_agent_configs_pkey PRIMARY KEY (id); + ALTER TABLE ONLY remote_mirrors ADD CONSTRAINT remote_mirrors_pkey PRIMARY KEY (id); @@ -28352,6 +28421,9 @@ ALTER TABLE ONLY work_item_types ALTER TABLE ONLY work_item_widget_definitions ADD CONSTRAINT work_item_widget_definitions_pkey PRIMARY KEY (id); +ALTER TABLE ONLY workspaces + ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); + ALTER TABLE ONLY x509_certificates ADD CONSTRAINT x509_certificates_pkey PRIMARY KEY (id); @@ -32167,6 +32239,8 @@ CREATE UNIQUE INDEX index_releases_on_project_tag_unique ON releases USING btree CREATE INDEX index_releases_on_released_at ON releases USING btree (released_at); +CREATE INDEX index_remote_development_agent_configs_on_cluster_agent_id ON remote_development_agent_configs USING btree (cluster_agent_id); + CREATE INDEX index_remote_mirrors_on_last_successful_update_at ON remote_mirrors USING btree (last_successful_update_at); CREATE INDEX index_remote_mirrors_on_project_id ON remote_mirrors USING btree (project_id); @@ -32975,6 +33049,14 @@ CREATE UNIQUE INDEX index_work_item_widget_definitions_on_namespace_type_and_nam CREATE INDEX index_work_item_widget_definitions_on_work_item_type_id ON work_item_widget_definitions USING btree (work_item_type_id); +CREATE INDEX index_workspaces_on_cluster_agent_id ON workspaces USING btree (cluster_agent_id); + +CREATE UNIQUE INDEX index_workspaces_on_name ON workspaces USING btree (name); + +CREATE INDEX index_workspaces_on_project_id ON workspaces USING btree (project_id); + +CREATE INDEX index_workspaces_on_user_id ON workspaces USING btree (user_id); + CREATE INDEX index_x509_certificates_on_subject_key_identifier ON x509_certificates USING btree (subject_key_identifier); CREATE INDEX index_x509_certificates_on_x509_issuer_id ON x509_certificates USING btree (x509_issuer_id); @@ -34601,6 +34683,9 @@ ALTER TABLE ONLY user_interacted_projects ALTER TABLE ONLY merge_request_assignment_events ADD CONSTRAINT fk_08f7602bfd FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; +ALTER TABLE ONLY remote_development_agent_configs + ADD CONSTRAINT fk_0a3c0ada56 FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE; + ALTER TABLE ONLY dast_sites ADD CONSTRAINT fk_0a57f2271b FOREIGN KEY (dast_site_validation_id) REFERENCES dast_site_validations(id) ON DELETE SET NULL; @@ -35282,6 +35367,9 @@ ALTER TABLE ONLY resource_link_events ALTER TABLE ONLY metrics_users_starred_dashboards ADD CONSTRAINT fk_bd6ae32fac FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspaces + ADD CONSTRAINT fk_bdb0b31131 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + ALTER TABLE ONLY project_compliance_framework_settings ADD CONSTRAINT fk_be413374a9 FOREIGN KEY (framework_id) REFERENCES compliance_management_frameworks(id) ON DELETE CASCADE; @@ -35429,6 +35517,9 @@ ALTER TABLE ONLY web_hooks ALTER TABLE ONLY security_scans ADD CONSTRAINT fk_dbc89265b9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspaces + ADD CONSTRAINT fk_dc7c316be1 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY epics ADD CONSTRAINT fk_dccd3f98fc FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL; @@ -35531,6 +35622,9 @@ ALTER TABLE ONLY user_project_callouts ALTER TABLE ONLY approval_merge_request_rules ADD CONSTRAINT fk_f726c79756 FOREIGN KEY (scan_result_policy_id) REFERENCES scan_result_policies(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspaces + ADD CONSTRAINT fk_f78aeddc77 FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE; + ALTER TABLE ONLY cluster_agents ADD CONSTRAINT fk_f7d43dee13 FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL; diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 8b8ac8a3d44601228011b2ddd6a49565d2344bb0..17243bf2f2cf36ddac8f3f7dc7d99e7e8a6cd5fa 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -760,6 +760,34 @@ Returns [`WorkItem`](#workitem). | ---- | ---- | ----------- | | `id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. | +### `Query.workspace` + +Find a workspace. + +Returns [`Workspace`](#workspace). + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `id` | [`RemoteDevelopmentWorkspaceID!`](#remotedevelopmentworkspaceid) | Find a workspace by its ID. | + +### `Query.workspaces` + +Find workspaces by their IDs. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + ## `Mutation` type The `Mutation` type contains all the mutations you can execute. @@ -6835,6 +6863,51 @@ Input type: `WorkItemUpdateTaskInput` | `task` | [`WorkItem`](#workitem) | Updated task. | | `workItem` | [`WorkItem`](#workitem) | Updated work item. | +### `Mutation.workspaceCreate` + +Input type: `WorkspaceCreateInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `clusterAgentId` | [`ClustersAgentID!`](#clustersagentid) | ID of the cluster agent the created workspace will be associated with. | +| `desiredState` | [`String!`](#string) | Desired state of the created workspace. | +| `devfilePath` | [`String!`](#string) | Project repo git path containing the devfile used to configure the workspace. | +| `devfileRef` | [`String!`](#string) | Project repo git ref containing the devfile used to configure the workspace. | +| `editor` | [`String!`](#string) | Editor to inject into the created workspace. Must match a configured template. | +| `maxHoursBeforeTermination` | [`Int!`](#int) | Maximum hours the workspace can exist before it is automatically terminated. | +| `projectId` | [`ProjectID!`](#projectid) | ID of the project that will provide the Devfile for the created workspace. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| `workspace` | [`Workspace`](#workspace) | Created workspace. | + +### `Mutation.workspaceUpdate` + +Input type: `WorkspaceUpdateInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `desiredState` | [`String!`](#string) | Desired state of the created workspace. | +| `id` | [`RemoteDevelopmentWorkspaceID!`](#remotedevelopmentworkspaceid) | Global ID of the workspace. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| `workspace` | [`Workspace`](#workspace) | Created workspace. | + ## Connections Some types in our schema are `Connection` types - they represent a paginated @@ -11109,6 +11182,29 @@ The edge type for [`WorkItemType`](#workitemtype). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`WorkItemType`](#workitemtype) | The item at the end of the edge. | +#### `WorkspaceConnection` + +The connection type for [`Workspace`](#workspace). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `edges` | [`[WorkspaceEdge]`](#workspaceedge) | A list of edges. | +| `nodes` | [`[Workspace]`](#workspace) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `WorkspaceEdge` + +The edge type for [`Workspace`](#workspace). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`Workspace`](#workspace) | The item at the end of the edge. | + ## Object types Object types represent the resources that the GitLab GraphQL API can return. @@ -16745,6 +16841,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | `state` | [`[TodoStateEnum!]`](#todostateenum) | State of the todo. | | `type` | [`[TodoTargetEnum!]`](#todotargetenum) | Type of the todo. | +##### `MergeRequestAssignee.workspaces` + +Workspaces owned by the user. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + ### `MergeRequestAuthor` The author of the merge request. @@ -16995,6 +17107,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | `state` | [`[TodoStateEnum!]`](#todostateenum) | State of the todo. | | `type` | [`[TodoTargetEnum!]`](#todotargetenum) | Type of the todo. | +##### `MergeRequestAuthor.workspaces` + +Workspaces owned by the user. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + ### `MergeRequestDiffRegistry` Represents the Geo sync and verification state of a Merge Request diff. @@ -17264,6 +17392,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | `state` | [`[TodoStateEnum!]`](#todostateenum) | State of the todo. | | `type` | [`[TodoTargetEnum!]`](#todotargetenum) | Type of the todo. | +##### `MergeRequestParticipant.workspaces` + +Workspaces owned by the user. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + ### `MergeRequestPermissions` Check permissions for the current user on a merge request. @@ -17533,6 +17677,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | `state` | [`[TodoStateEnum!]`](#todostateenum) | State of the todo. | | `type` | [`[TodoTargetEnum!]`](#todotargetenum) | Type of the todo. | +##### `MergeRequestReviewer.workspaces` + +Workspaces owned by the user. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + ### `Metadata` #### Fields @@ -21948,6 +22108,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | `state` | [`[TodoStateEnum!]`](#todostateenum) | State of the todo. | | `type` | [`[TodoTargetEnum!]`](#todotargetenum) | Type of the todo. | +##### `UserCore.workspaces` + +Workspaces owned by the user. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + ### `UserMergeRequestInteraction` Information about a merge request given a specific user. @@ -22932,6 +23108,35 @@ Represents a weight widget. | `type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. | | `weight` | [`Int`](#int) | Weight of the work item. | +### `Workspace` + +Represents a remote development workspace. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `actualState` | [`String!`](#string) | Actual state of the workspace. | +| `clusterAgent` | [`ClusterAgent!`](#clusteragent) | Kubernetes Agent associated with the workspace. | +| `createdAt` | [`Time!`](#time) | Timestamp of workspace creation. | +| `deploymentResourceVersion` | [`Int`](#int) | ResourceVersion of the Deployment resource for the workspace. | +| `desiredState` | [`String!`](#string) | Desired state of the workspace. | +| `desiredStateUpdatedAt` | [`Time!`](#time) | Timestamp of last update to desired state. | +| `devfile` | [`String!`](#string) | Source YAML of the devfile used to configure the workspace. | +| `devfilePath` | [`String!`](#string) | Project repo git path containing the devfile used to configure the workspace. | +| `devfileRef` | [`String!`](#string) | Project repo git ref containing the devfile used to configure the workspace. | +| `editor` | [`String!`](#string) | Editor used to configure the workspace. Must match a configured template. | +| `id` | [`RemoteDevelopmentWorkspaceID!`](#remotedevelopmentworkspaceid) | Global ID of the workspace. | +| `maxHoursBeforeTermination` | [`Int!`](#int) | Maximum hours the workspace can exist before it is automatically terminated. | +| `name` | [`String!`](#string) | Name of the workspace in Kubernetes. | +| `namespace` | [`String!`](#string) | Namespace of the workspace in Kubernetes. | +| `processedDevfile` | [`String!`](#string) | Processed YAML of the devfile used to configure the workspace. | +| `projectId` | [`ID!`](#id) | ID of the Project providing the Devfile for the workspace. | +| `respondedToAgentAt` | [`Time`](#time) | Timestamp of last response sent to GA4K for the workspace. | +| `updatedAt` | [`Time!`](#time) | Timestamp of last update to any mutable workspace property. | +| `url` | [`String!`](#string) | URL of the workspace. | +| `user` | [`UserCore!`](#usercore) | Owner of the workspace. | + ### `X509Certificate` Represents an X.509 certificate. @@ -25857,6 +26062,12 @@ A `ReleasesLinkID` is a global ID. It is encoded as a string. An example `ReleasesLinkID` is: `"gid://gitlab/Releases::Link/1"`. +### `RemoteDevelopmentWorkspaceID` + +A `RemoteDevelopmentWorkspaceID` is a global ID. It is encoded as a string. + +An example `RemoteDevelopmentWorkspaceID` is: `"gid://gitlab/RemoteDevelopment::Workspace/1"`. + ### `SecurityTrainingProviderID` A `SecurityTrainingProviderID` is a global ID. It is encoded as a string. @@ -26637,6 +26848,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | `state` | [`[TodoStateEnum!]`](#todostateenum) | State of the todo. | | `type` | [`[TodoTargetEnum!]`](#todotargetenum) | Type of the todo. | +###### `User.workspaces` + +Workspaces owned by the user. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, `last: Int`. + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Array of global workspace IDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | + #### `WorkItemWidget` Implementations: diff --git a/ee/app/assets/javascripts/remote_development/components/create/get_project_details_query.vue b/ee/app/assets/javascripts/remote_development/components/create/get_project_details_query.vue index 5c6725273cd6f3f3baa8a5bbe116cc28ac10690b..f14f20903c01550e5f5793412181046fe9998ddb 100644 --- a/ee/app/assets/javascripts/remote_development/components/create/get_project_details_query.vue +++ b/ee/app/assets/javascripts/remote_development/components/create/get_project_details_query.vue @@ -71,6 +71,7 @@ export default { } try { + // noinspection JSCheckFunctionSignatures const { data, error } = await this.$apollo.query({ query: getGroupClusterAgentsQuery, variables: { groupPath }, diff --git a/ee/app/assets/javascripts/remote_development/components/list/start_workspace_button.vue b/ee/app/assets/javascripts/remote_development/components/list/start_workspace_button.vue index f25f1c30542d86e3f243f52bb6a2154133cdeb6a..95989cad515b7bb8f03f19a3acbf161cc2f134c1 100644 --- a/ee/app/assets/javascripts/remote_development/components/list/start_workspace_button.vue +++ b/ee/app/assets/javascripts/remote_development/components/list/start_workspace_button.vue @@ -28,7 +28,11 @@ export default { computed: { isVisible() { return ( - [WORKSPACE_STATES.stopped, WORKSPACE_STATES.starting].includes(this.actualState) && + [ + WORKSPACE_STATES.creationRequested, + WORKSPACE_STATES.stopped, + WORKSPACE_STATES.starting, + ].includes(this.actualState) && [WORKSPACE_DESIRED_STATES.running, WORKSPACE_DESIRED_STATES.stopped].includes( this.desiredState, ) diff --git a/ee/app/assets/javascripts/remote_development/components/list/workspace_state_indicator.vue b/ee/app/assets/javascripts/remote_development/components/list/workspace_state_indicator.vue index 666db17191ed2977cda56d369094777afc1ad63d..07fc19502166272afd2c03d64be3a3fcde7fc17d 100644 --- a/ee/app/assets/javascripts/remote_development/components/list/workspace_state_indicator.vue +++ b/ee/app/assets/javascripts/remote_development/components/list/workspace_state_indicator.vue @@ -1,7 +1,12 @@