diff --git a/db/docs/workspace_agentk_states.yml b/db/docs/workspace_agentk_states.yml new file mode 100644 index 0000000000000000000000000000000000000000..75518e2970a80507cb70c5a7006c3a2aca2d6b79 --- /dev/null +++ b/db/docs/workspace_agentk_states.yml @@ -0,0 +1,14 @@ +--- +table_name: workspace_agentk_states +classes: +- RemoteDevelopment::WorkspaceAgentkState +feature_categories: +- workspaces +description: Stores desired configuration for a workspace when used in conjunction + with agentk +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/190027 +milestone: '18.1' +gitlab_schema: gitlab_main_cell +sharding_key: + project_id: projects +table_size: small diff --git a/db/migrate/20250430125816_create_workspace_agentk_states_table.rb b/db/migrate/20250430125816_create_workspace_agentk_states_table.rb new file mode 100644 index 0000000000000000000000000000000000000000..814d71b76e4d84aa829b2f59b14a1ef478451a62 --- /dev/null +++ b/db/migrate/20250430125816_create_workspace_agentk_states_table.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class CreateWorkspaceAgentkStatesTable < Gitlab::Database::Migration[2.3] + milestone '18.1' + + def up + create_table :workspace_agentk_states do |t| + t.timestamps_with_timezone null: false + t.bigint :workspace_id, null: false, index: { unique: true } + t.bigint :project_id, null: false, index: true + t.jsonb :desired_config, null: false + end + end + + def down + drop_table :workspace_agentk_states + end +end diff --git a/db/migrate/20250502104400_add_project_id_workspace_agentk_state_foreign_key.rb b/db/migrate/20250502104400_add_project_id_workspace_agentk_state_foreign_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..8f28f1732b444d82933b0529757c42f31c8a0c1f --- /dev/null +++ b/db/migrate/20250502104400_add_project_id_workspace_agentk_state_foreign_key.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddProjectIdWorkspaceAgentkStateForeignKey < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + milestone '18.1' + + def up + add_concurrent_foreign_key :workspace_agentk_states, :projects, column: :project_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :workspace_agentk_states, column: :project_id + end + end +end diff --git a/db/migrate/20250507141312_add_workspace_id_workspace_agentk_state_foreign_key.rb b/db/migrate/20250507141312_add_workspace_id_workspace_agentk_state_foreign_key.rb new file mode 100644 index 0000000000000000000000000000000000000000..3fa9c692ca1e20abc4014b102d9396d78bb8e0ff --- /dev/null +++ b/db/migrate/20250507141312_add_workspace_id_workspace_agentk_state_foreign_key.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddWorkspaceIdWorkspaceAgentkStateForeignKey < Gitlab::Database::Migration[2.3] + disable_ddl_transaction! + milestone '18.1' + + def up + add_concurrent_foreign_key :workspace_agentk_states, :workspaces, column: :workspace_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :workspace_agentk_states, column: :workspace_id + end + end +end diff --git a/db/schema_migrations/20250430125816 b/db/schema_migrations/20250430125816 new file mode 100644 index 0000000000000000000000000000000000000000..26bed83f60a8c750581a90cf0f0e25d4a3b50610 --- /dev/null +++ b/db/schema_migrations/20250430125816 @@ -0,0 +1 @@ +4b501429153c6dc109b2e33b43e501f9bd4c0dfe6b535ab6f4e8269a3fff2456 \ No newline at end of file diff --git a/db/schema_migrations/20250502104400 b/db/schema_migrations/20250502104400 new file mode 100644 index 0000000000000000000000000000000000000000..76c4512bb17614935a67f650aef220064424b0a1 --- /dev/null +++ b/db/schema_migrations/20250502104400 @@ -0,0 +1 @@ +209572b9d0dc6a135afb3dc2b1e3716617c0af981ec6b9f9bbf0cd31a9b1fd69 \ No newline at end of file diff --git a/db/schema_migrations/20250507141312 b/db/schema_migrations/20250507141312 new file mode 100644 index 0000000000000000000000000000000000000000..647d833b3487840ebe88475594330ed29882922d --- /dev/null +++ b/db/schema_migrations/20250507141312 @@ -0,0 +1 @@ +28610c421b71e49edcec55037dd73c7597670a9c66f6b9d5abdd428deb6b6b02 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 0015154ce085d28eea42c0be8cbc8ad2bb3b5c99..33f9d42be01fcafc5389ce5524e03c5501563646 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -25827,6 +25827,24 @@ 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 workspace_agentk_states ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + workspace_id bigint NOT NULL, + project_id bigint NOT NULL, + desired_config jsonb NOT NULL +); + +CREATE SEQUENCE workspace_agentk_states_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE workspace_agentk_states_id_seq OWNED BY workspace_agentk_states.id; + CREATE TABLE workspace_variables ( id bigint NOT NULL, workspace_id bigint NOT NULL, @@ -28047,6 +28065,8 @@ ALTER TABLE ONLY work_item_type_user_preferences ALTER COLUMN id SET DEFAULT nex ALTER TABLE ONLY work_item_widget_definitions ALTER COLUMN id SET DEFAULT nextval('work_item_widget_definitions_id_seq'::regclass); +ALTER TABLE ONLY workspace_agentk_states ALTER COLUMN id SET DEFAULT nextval('workspace_agentk_states_id_seq'::regclass); + ALTER TABLE ONLY workspace_variables ALTER COLUMN id SET DEFAULT nextval('workspace_variables_id_seq'::regclass); ALTER TABLE ONLY workspaces ALTER COLUMN id SET DEFAULT nextval('workspaces_id_seq'::regclass); @@ -31272,6 +31292,9 @@ ALTER TABLE ONLY work_item_weights_sources ALTER TABLE ONLY work_item_widget_definitions ADD CONSTRAINT work_item_widget_definitions_pkey PRIMARY KEY (id); +ALTER TABLE ONLY workspace_agentk_states + ADD CONSTRAINT workspace_agentk_states_pkey PRIMARY KEY (id); + ALTER TABLE ONLY workspace_variables ADD CONSTRAINT workspace_variables_pkey PRIMARY KEY (id); @@ -38062,6 +38085,10 @@ CREATE UNIQUE INDEX index_work_item_widget_definitions_on_type_id_and_name ON wo 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_workspace_agentk_states_on_project_id ON workspace_agentk_states USING btree (project_id); + +CREATE UNIQUE INDEX index_workspace_agentk_states_on_workspace_id ON workspace_agentk_states USING btree (workspace_id); + CREATE INDEX index_workspace_variables_on_project_id ON workspace_variables USING btree (project_id); CREATE INDEX index_workspace_variables_on_workspace_id ON workspace_variables USING btree (workspace_id); @@ -42382,6 +42409,9 @@ ALTER TABLE ONLY catalog_resource_component_last_usages ALTER TABLE ONLY user_namespace_callouts ADD CONSTRAINT fk_4b1257f385 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspace_agentk_states + ADD CONSTRAINT fk_4b1428e43a FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE; + ALTER TABLE ONLY sbom_occurrences ADD CONSTRAINT fk_4b88e5b255 FOREIGN KEY (component_version_id) REFERENCES sbom_component_versions(id) ON DELETE CASCADE; @@ -43738,6 +43768,9 @@ ALTER TABLE ONLY cluster_agent_migrations ALTER TABLE ONLY events ADD CONSTRAINT fk_eea90e3209 FOREIGN KEY (personal_namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspace_agentk_states + ADD CONSTRAINT fk_eeddb6a618 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY coverage_fuzzing_corpuses ADD CONSTRAINT fk_ef5ebf339f FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; diff --git a/ee/app/models/ee/project.rb b/ee/app/models/ee/project.rb index 1f90cc52fc906c5e3dc3e8ec19cbdab38ff45dde..ae93595f11240ed41c5ddf3b1b40f08b29669d2a 100644 --- a/ee/app/models/ee/project.rb +++ b/ee/app/models/ee/project.rb @@ -117,6 +117,7 @@ def lock_for_confirmation!(id) has_many :vulnerability_archives, class_name: 'Vulnerabilities::Archive' has_many :workspaces, class_name: 'RemoteDevelopment::Workspace', inverse_of: :project + has_many :workspace_agentk_states, class_name: 'RemoteDevelopment::WorkspaceAgentkState', inverse_of: :project has_many :dast_site_profiles has_many :dast_site_tokens diff --git a/ee/app/models/remote_development/workspace.rb b/ee/app/models/remote_development/workspace.rb index d99e968e9bfd56faa314d43174f95ae8e3e0fd61..690afc94f916c1cc8a835bb9c7e6d3939aef4c33 100644 --- a/ee/app/models/remote_development/workspace.rb +++ b/ee/app/models/remote_development/workspace.rb @@ -26,6 +26,7 @@ class Workspace < ApplicationRecord has_many :user_provided_workspace_variables, -> { user_provided.with_variable_type_environment.order_id_desc }, class_name: "RemoteDevelopment::WorkspaceVariable", inverse_of: :workspace + has_one :agentk_state, inverse_of: :workspace, class_name: 'RemoteDevelopment::WorkspaceAgentkState' validates :user, presence: true validates :agent, presence: true diff --git a/ee/app/models/remote_development/workspace_agentk_state.rb b/ee/app/models/remote_development/workspace_agentk_state.rb new file mode 100644 index 0000000000000000000000000000000000000000..5b053d7baa5c124846e7961b31873474bc0ca9a7 --- /dev/null +++ b/ee/app/models/remote_development/workspace_agentk_state.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module RemoteDevelopment + class WorkspaceAgentkState < ApplicationRecord + belongs_to :workspace, inverse_of: :agentk_state + belongs_to :project, inverse_of: :workspace_agentk_states + + validates :workspace_id, presence: true + validates :project_id, presence: true + validates :desired_config, presence: true + end +end diff --git a/ee/spec/factories/remote_development/workspace_agentk_states.rb b/ee/spec/factories/remote_development/workspace_agentk_states.rb new file mode 100644 index 0000000000000000000000000000000000000000..5f81c1b5fba6cadfa56713bf1d2993aa11aeef19 --- /dev/null +++ b/ee/spec/factories/remote_development/workspace_agentk_states.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :workspace_agentk_state, class: 'RemoteDevelopment::WorkspaceAgentkState' do + # noinspection RailsParamDefResolve -- RubyMine flags this as requiring a hash, but a symbol is a valid option + association :project, :in_group + + workspace + + desired_config do + RemoteDevelopment::FixtureFileHelpers.read_fixture_file('example.desired_config.json') + end + end +end diff --git a/ee/spec/fixtures/remote_development/example.desired_config.json b/ee/spec/fixtures/remote_development/example.desired_config.json new file mode 100644 index 0000000000000000000000000000000000000000..8d9b5d32e0ae1a57acea3fe35084e7ef7407e42e --- /dev/null +++ b/ee/spec/fixtures/remote_development/example.desired_config.json @@ -0,0 +1,561 @@ +[ + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991", + "cli-utils.sigs.k8s.io/inventory-id": "workspace-991-990-fedcba-workspace-inventory" + }, + "name": "workspace-991-990-fedcba-workspace-inventory", + "namespace": "gl-rd-ns-991-990-fedcba" + } + }, + { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "creationTimestamp": null, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "spec": { + "replicas": 1, + "selector": { + "matchLabels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + } + }, + "strategy": { + "type": "Recreate" + }, + "template": { + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "creationTimestamp": null, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "spec": { + "containers": [ + { + "args": [ + "echo 'tooling container args'" + ], + "command": [ + "/bin/sh", + "-c" + ], + "env": [ + { + "name": "GL_ENV_NAME", + "value": "gl-env-value" + }, + { + "name": "PROJECTS_ROOT", + "value": "/projects" + }, + { + "name": "PROJECT_SOURCE", + "value": "/projects" + } + ], + "envFrom": [ + { + "secretRef": { + "name": "workspace-991-990-fedcba-env-var" + } + } + ], + "image": "quay.io/mloriedo/universal-developer-image:ubi8-dw-demo", + "imagePullPolicy": "Always", + "name": "tooling-container", + "ports": [ + { + "containerPort": 60001, + "name": "server", + "protocol": "TCP" + } + ], + "resources": { + "limits": { + "cpu": "1", + "memory": "1Gi" + }, + "requests": { + "cpu": "0.5", + "memory": "512Mi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "runAsNonRoot": true, + "runAsUser": 5001 + }, + "volumeMounts": [ + { + "mountPath": "/projects", + "name": "gl-workspace-data" + }, + { + "mountPath": "/.workspace-data/variables/file", + "name": "gl-workspace-variables" + }, + { + "name": "gl-workspace-scripts", + "mountPath": "/workspace-scripts" + } + ], + "lifecycle": { + "postStart": { + "exec": { + "command": [ + "/bin/sh", + "-c", + "mkdir -p \"${GL_WORKSPACE_LOGS_DIR}\"\nln -sf \"${GL_WORKSPACE_LOGS_DIR}\" /tmp\n\"/workspace-scripts/gl-run-poststart-commands.sh\" 1>>\"${GL_WORKSPACE_LOGS_DIR}/poststart-stdout.log\" 2>>\"${GL_WORKSPACE_LOGS_DIR}/poststart-stderr.log\" &\n" + ] + } + } + } + } + ], + "initContainers": [ + { + "args": [ + "echo 'project cloner container args'" + ], + "command": [ + "/bin/sh", + "-c" + ], + "env": [ + { + "name": "PROJECTS_ROOT", + "value": "/projects" + }, + { + "name": "PROJECT_SOURCE", + "value": "/projects" + } + ], + "envFrom": [ + { + "secretRef": { + "name": "workspace-991-990-fedcba-env-var" + } + } + ], + "image": "alpine/git:2.45.2", + "imagePullPolicy": "Always", + "name": "gl-project-cloner-gl-project-cloner-command-1", + "resources": { + "limits": { + "cpu": "500m", + "memory": "1000Mi" + }, + "requests": { + "cpu": "100m", + "memory": "500Mi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "runAsNonRoot": true, + "runAsUser": 5001 + }, + "volumeMounts": [ + { + "mountPath": "/projects", + "name": "gl-workspace-data" + }, + { + "mountPath": "/.workspace-data/variables/file", + "name": "gl-workspace-variables" + } + ] + } + ], + "runtimeClassName": "standard", + "securityContext": { + "fsGroup": 0, + "fsGroupChangePolicy": "OnRootMismatch", + "runAsNonRoot": true, + "runAsUser": 5001 + }, + "serviceAccountName": "workspace-991-990-fedcba", + "volumes": [ + { + "name": "gl-workspace-data", + "persistentVolumeClaim": { + "claimName": "workspace-991-990-fedcba-gl-workspace-data" + } + }, + { + "name": "gl-workspace-variables", + "projected": { + "defaultMode": 508, + "sources": [ + { + "secret": { + "name": "workspace-991-990-fedcba-file" + } + } + ] + } + }, + { + "name": "gl-workspace-scripts", + "projected": { + "defaultMode": 365, + "sources": [ + { + "configMap": { + "name": "workspace-991-990-fedcba-scripts-configmap" + } + } + ] + } + } + ] + } + } + }, + "status": {} + }, + { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "creationTimestamp": null, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "spec": { + "ports": [ + { + "name": "server", + "port": 60001, + "targetPort": 60001 + } + ], + "selector": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + } + }, + "status": { + "loadBalancer": {} + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "creationTimestamp": null, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba-gl-workspace-data", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "spec": { + "accessModes": [ + "ReadWriteOnce" + ], + "resources": { + "requests": { + "storage": "50Gi" + } + } + }, + "status": {} + }, + { + "apiVersion": "v1", + "automountServiceAccountToken": false, + "imagePullSecrets": [ + { + "name": "registry-secret" + } + ], + "kind": "ServiceAccount", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba", + "namespace": "gl-rd-ns-991-990-fedcba" + } + }, + { + "apiVersion": "networking.k8s.io/v1", + "kind": "NetworkPolicy", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "spec": { + "egress": [ + { + "ports": [ + { + "port": 53, + "protocol": "TCP" + }, + { + "port": 53, + "protocol": "UDP" + } + ], + "to": [ + { + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "kube-system" + } + } + } + ] + }, + { + "to": [ + { + "ipBlock": { + "cidr": "0.0.0.0/0", + "except": [ + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16" + ] + } + } + ] + } + ], + "ingress": [ + { + "from": [ + { + "namespaceSelector": { + "matchLabels": { + "kubernetes.io/metadata.name": "gitlab-workspaces" + } + }, + "podSelector": { + "matchLabels": { + "app.kubernetes.io/name": "gitlab-workspaces-proxy" + } + } + } + ] + } + ], + "podSelector": {}, + "policyTypes": [ + "Ingress", + "Egress" + ] + } + }, + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba-scripts-configmap", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "data": {} + }, + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991", + "cli-utils.sigs.k8s.io/inventory-id": "workspace-991-990-fedcba-secrets-inventory" + }, + "name": "workspace-991-990-fedcba-secrets-inventory", + "namespace": "gl-rd-ns-991-990-fedcba" + } + }, + { + "apiVersion": "v1", + "kind": "ResourceQuota", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-workspace-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba", + "namespace": "gl-rd-ns-991-990-fedcba" + }, + "spec": { + "hard": { + "limits.cpu": "2", + "limits.memory": "4Gi", + "requests.cpu": "1", + "requests.memory": "1Gi" + } + } + }, + { + "apiVersion": "v1", + "data": {}, + "kind": "Secret", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-secrets-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba-env-var", + "namespace": "gl-rd-ns-991-990-fedcba" + } + }, + { + "apiVersion": "v1", + "data": {}, + "kind": "Secret", + "metadata": { + "annotations": { + "environment": "production", + "team": "engineering", + "config.k8s.io/owning-inventory": "workspace-991-990-fedcba-secrets-inventory", + "workspaces.gitlab.com/host-template": "3000-workspace-991-990-fedcba.workspaces.localdev.me", + "workspaces.gitlab.com/id": "993", + "workspaces.gitlab.com/max-resources-per-workspace-sha256": "24aefc317e11db538ede450d1773e273966b9801b988d49e1219f2a9bf8e7f66" + }, + "labels": { + "app": "workspace", + "tier": "development", + "agent.gitlab.com/id": "991" + }, + "name": "workspace-991-990-fedcba-file", + "namespace": "gl-rd-ns-991-990-fedcba" + } + } +] diff --git a/ee/spec/lib/remote_development/agent_config_operations/main_integration_spec.rb b/ee/spec/lib/remote_development/agent_config_operations/main_integration_spec.rb index 483c3e5dd8a6bf382cf3ec64a79a2de5668c636a..3c6dd8fc52d84650b2c33a1862377a1430999598 100644 --- a/ee/spec/lib/remote_development/agent_config_operations/main_integration_spec.rb +++ b/ee/spec/lib/remote_development/agent_config_operations/main_integration_spec.rb @@ -10,7 +10,7 @@ let(:dns_zone) { 'my-awesome-domain.me' } let(:config) do - yaml = read_fixture_file_yaml("example.agent_config.yaml") + yaml = read_fixture_file("example.agent_config.yaml") yaml.gsub!("dns_zone: workspaces.dev.test", "dns_zone: #{dns_zone}") # NOTE: YAML.safe_load will ensure that all keys are strings, not symbols. YAML.safe_load(yaml) diff --git a/ee/spec/models/ee/project_spec.rb b/ee/spec/models/ee/project_spec.rb index 0febc32c668b7eb0b197e296cd5be7bebe1ef81f..8d0361b58d47acdbf76570c942e564f8aafb9f7d 100644 --- a/ee/spec/models/ee/project_spec.rb +++ b/ee/spec/models/ee/project_spec.rb @@ -110,6 +110,9 @@ it { is_expected.to have_many(:instance_runner_monthly_usages).class_name('Ci::Minutes::InstanceRunnerMonthlyUsage') } it { is_expected.to have_many(:hosted_runner_monthly_usages).class_name('Ci::Minutes::GitlabHostedRunnerMonthlyUsage') } + it { is_expected.to have_many(:workspaces).class_name('RemoteDevelopment::Workspace') } + it { is_expected.to have_many(:workspace_agentk_states).class_name('RemoteDevelopment::WorkspaceAgentkState') } + include_examples 'ci_cd_settings delegation' do let(:attributes_with_prefix) do { diff --git a/ee/spec/models/remote_development/workspace_agentk_state_spec.rb b/ee/spec/models/remote_development/workspace_agentk_state_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..8220898274bfd87834fddb5914bbc840a216da84 --- /dev/null +++ b/ee/spec/models/remote_development/workspace_agentk_state_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe RemoteDevelopment::WorkspaceAgentkState, feature_category: :workspaces do + let(:workspace) { create(:workspace) } + + # noinspection RubyArgCount -- RubyMine thinks this is a kernel create method instead of a factory + let_it_be(:project) { create(:project) } + + subject(:workspace_agentk_state) { build(:workspace_agentk_state, workspace: workspace, project: project) } + + describe "associations" do + context "for belongs_to" do + it { is_expected.to belong_to(:workspace) } + it { is_expected.to belong_to(:project) } + end + + context "when from factory" do + before do + workspace.save! + end + + it "has correct associations from factory" do + expect(workspace_agentk_state.workspace).to eq(workspace) + expect(workspace_agentk_state.project).to eq(project) + end + end + end + + describe "validations" do + it { is_expected.to validate_presence_of(:workspace_id) } + it { is_expected.to validate_presence_of(:project_id) } + it { is_expected.to validate_presence_of(:desired_config) } + end +end diff --git a/ee/spec/models/remote_development/workspace_spec.rb b/ee/spec/models/remote_development/workspace_spec.rb index 282268448438aaa9531fea5adf30a255af5837e6..ba22055b70096d1bb7d38e4b86322cd6bfe36e59 100644 --- a/ee/spec/models/remote_development/workspace_spec.rb +++ b/ee/spec/models/remote_development/workspace_spec.rb @@ -53,6 +53,10 @@ it { is_expected.to have_many(:workspace_variables) } end + context "for has_one" do + it { is_expected.to have_one(:agentk_state) } + end + context "for belongs_to" do it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:personal_access_token) } diff --git a/ee/spec/support/helpers/remote_development/fixture_file_helpers.rb b/ee/spec/support/helpers/remote_development/fixture_file_helpers.rb index 0793c856d9ce7497f72e5e7df6e68b5f2afdbe7a..7dc205ac427bcedd8820f9c0d5d90e139e4c7a61 100644 --- a/ee/spec/support/helpers/remote_development/fixture_file_helpers.rb +++ b/ee/spec/support/helpers/remote_development/fixture_file_helpers.rb @@ -14,7 +14,7 @@ module FixtureFileHelpers # @param [String] namespace_path # @return [String] def read_devfile_yaml(filename, project_name: "test-project", namespace_path: "test-group") - erb_devfile_contents = read_fixture_file_yaml(filename) + erb_devfile_contents = read_fixture_file(filename) fixture_file_binding = FixtureFileErbBinding.new.get_fixture_file_binding devfile_contents = ERB.new(erb_devfile_contents).result(fixture_file_binding) devfile_contents.gsub!('http://localhost/', root_url) @@ -28,7 +28,7 @@ def read_devfile_yaml(filename, project_name: "test-project", namespace_path: "t # @param [String] filename # @return [String] - def read_fixture_file_yaml(filename) + def read_fixture_file(filename) File.read(Rails.root.join('ee/spec/fixtures/remote_development', filename).to_s) end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index b01ded6f937aea1a50c95644c3d31661b5ed87ae..c0d6f5403675859cb733dc053494a1ecedb7477a 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -595,6 +595,7 @@ project: - ci_access_project_authorizations - cluster_project - workspaces +- workspace_agentk_states - creator - cycle_analytics_stages - value_streams