diff --git a/ee/app/models/remote_development/workspace.rb b/ee/app/models/remote_development/workspace.rb index c2a01565dfcf9e68d7773c48b49f9d97c0c2d40b..ae3b77e0763b1b25ea7ca423d34c45dfcd915c45 100644 --- a/ee/app/models/remote_development/workspace.rb +++ b/ee/app/models/remote_development/workspace.rb @@ -97,7 +97,7 @@ class Workspace < ApplicationRecord new_record? || actual_state_changed? end - before_save :delete_workspace_token, if: -> do + after_save :delete_workspace_token, if: -> do saved_change_to_desired_state? && !desired_state_running? end @@ -158,9 +158,12 @@ def desired_state_terminated_and_actual_state_not_terminated? # @return [String] def url - URI::HTTPS.build(host: "#{url_prefix}.#{workspaces_agent_config.dns_zone}", - path: "/", - query: url_query_string).to_s + RemoteDevelopment::WorkspaceOperations::WorkspaceUrlHelper.url( + url_prefix, + url_query_string, + workspaces_agent_config.dns_zone, + workspaces_agent_config.gitlab_workspaces_proxy_http_enabled + ) end # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/503465 - Remove in 19.0 diff --git a/ee/lib/remote_development/agent_config_operations/updater.rb b/ee/lib/remote_development/agent_config_operations/updater.rb index f58d64b008870d9d526555ec674c814f81b37e25..493e468fa8cc08f72d295ff83b15710af066bd78 100644 --- a/ee/lib/remote_development/agent_config_operations/updater.rb +++ b/ee/lib/remote_development/agent_config_operations/updater.rb @@ -51,8 +51,19 @@ def self.update_or_initialize_workspaces_agent_config(agent:, config_from_agent_ # it is a single `gitlab_workspaces_proxy_namespace`, not a jsonb field for `gitlab_workspaces_proxy`. # So, in order to do the `merge` below of config_from_agent_config_file into agent_config_settings, # we will make the config_from_agent_config_file match the single field name. + # The same is true for other fields nested under `gitlab_workspaces_proxy` - `http_enabled`, `ssh_enabled` proxy_namespace = normalized_config_from_file.dig(:gitlab_workspaces_proxy, :namespace) normalized_config_from_file[:gitlab_workspaces_proxy_namespace] = proxy_namespace if proxy_namespace + + proxy_http_enabled = normalized_config_from_file.dig(:gitlab_workspaces_proxy, :http_enabled) + unless proxy_http_enabled.nil? + normalized_config_from_file[:gitlab_workspaces_proxy_http_enabled] = proxy_http_enabled + end + + proxy_ssh_enabled = normalized_config_from_file.dig(:gitlab_workspaces_proxy, :ssh_enabled) + unless proxy_ssh_enabled.nil? + normalized_config_from_file[:gitlab_workspaces_proxy_ssh_enabled] = proxy_ssh_enabled + end # Same for `network_policy_enabled` and `network_policy_egress` db fields - rename them from the # network_policy field in the config_from_agent_config_file spec network_policy_enabled = normalized_config_from_file.dig(:network_policy, :enabled) @@ -74,7 +85,9 @@ def self.update_or_initialize_workspaces_agent_config(agent:, config_from_agent_ :annotations, :default_resources_per_workspace_container, :default_runtime_class, + :gitlab_workspaces_proxy_http_enabled, :gitlab_workspaces_proxy_namespace, + :gitlab_workspaces_proxy_ssh_enabled, :image_pull_secrets, :labels, :max_active_hours_before_stop, @@ -112,7 +125,9 @@ def self.set_attributes_on_agent_config_model_instance(agent_config_model:, valu default_runtime_class: values[:default_runtime_class], dns_zone: values[:dns_zone], enabled: values[:enabled], + gitlab_workspaces_proxy_http_enabled: values[:gitlab_workspaces_proxy_http_enabled], gitlab_workspaces_proxy_namespace: values[:gitlab_workspaces_proxy_namespace], + gitlab_workspaces_proxy_ssh_enabled: values[:gitlab_workspaces_proxy_ssh_enabled], image_pull_secrets: values[:image_pull_secrets], labels: values[:labels], max_active_hours_before_stop: values[:max_active_hours_before_stop], diff --git a/ee/lib/remote_development/files.rb b/ee/lib/remote_development/files.rb index 7d95e29dfde42476f46e428342cbcbddb2bb0086..0bd5be3e1382df1d4c13c304af2b50aceca8f314 100644 --- a/ee/lib/remote_development/files.rb +++ b/ee/lib/remote_development/files.rb @@ -45,6 +45,11 @@ def self.container_keepalive_command_args read_file("workspace_operations/create/container_keepalive_command_args.sh") end + # @return [String] content of the file + def self.internal_poststart_command_start_agentw_script + read_file("workspace_operations/create/internal_poststart_command_start_agentw.sh") + end + # @return [String] content of the file def self.internal_poststart_command_start_vscode_script read_file("workspace_operations/create/internal_poststart_command_start_vscode.sh") @@ -81,6 +86,7 @@ def self.internal_poststart_command_clone_unshallow_script GIT_CREDENTIAL_STORE_SCRIPT = git_credential_store_script INTERNAL_POSTSTART_COMMAND_CLONE_PROJECT_SCRIPT = internal_poststart_command_clone_project_script INTERNAL_POSTSTART_COMMAND_CLONE_UNSHALLOW_SCRIPT = internal_poststart_command_clone_unshallow_script + INTERNAL_POSTSTART_COMMAND_START_AGENTW_SCRIPT = internal_poststart_command_start_agentw_script INTERNAL_POSTSTART_COMMAND_START_VSCODE_SCRIPT = internal_poststart_command_start_vscode_script INTERNAL_POSTSTART_COMMAND_SLEEP_UNTIL_WORKSPACE_IS_RUNNING_SCRIPT = internal_poststart_command_sleep_until_workspace_is_running_script diff --git a/ee/lib/remote_development/workspace_operations/create/create_constants.rb b/ee/lib/remote_development/workspace_operations/create/create_constants.rb index 74f58ac1e4c80e98743076ef9de055a70a035a45..beac2f710a0fd9258e6b4629a3cb6578b8a60395 100644 --- a/ee/lib/remote_development/workspace_operations/create/create_constants.rb +++ b/ee/lib/remote_development/workspace_operations/create/create_constants.rb @@ -13,6 +13,9 @@ module CreateConstants include WorkspaceOperationsConstants # Please keep alphabetized + AGENTW_OBSERVABILITY_LISTEN_ADDRESS = ":60031" + AGENTW_TOKEN_FILE_NAME = "gl_agentw_token" + AGENTW_TOKEN_FILE_PATH = "#{VARIABLES_VOLUME_PATH}/#{AGENTW_TOKEN_FILE_NAME}".freeze GIT_CREDENTIAL_STORE_SCRIPT_FILE_NAME = "gl_git_credential_store.sh" GIT_CREDENTIAL_STORE_SCRIPT_FILE_PATH = "#{VARIABLES_VOLUME_PATH}/#{GIT_CREDENTIAL_STORE_SCRIPT_FILE_NAME}".freeze diff --git a/ee/lib/remote_development/workspace_operations/create/internal_poststart_command_start_agentw.sh b/ee/lib/remote_development/workspace_operations/create/internal_poststart_command_start_agentw.sh new file mode 100644 index 0000000000000000000000000000000000000000..81a084e070313a8e79c17dfe5937049b21cd3c4a --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/create/internal_poststart_command_start_agentw.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# Define log file path +LOG_FILE="${GL_WORKSPACE_LOGS_DIR}/start-agentw.log" + +echo "$(date -Iseconds): ----------------------------------------" +echo "$(date -Iseconds): Starting Agent for Workspace(agentw) in background with output written to ${LOG_FILE}..." + +mkdir -p "$(dirname "${LOG_FILE}")" + +echo "$(date -Iseconds): Agentw initialization started" + +# Start logging +exec 1>>"${LOG_FILE}" 2>&1 + +# This script starts Agent for Workspace(agentw). +# +# It uses the following environment variables +# $GL_TOOLS_DIR - directory where the tools are copied. +# $GL_GITLAB_AGENT_SERVER_ADDRESS - GitLab Agent Server(KAS) address used by Agent for Workspace(agentw) to connect to. +# $GL_AGENTW_TOKEN_FILE_PATH - the workspace token used by Agent for Workspace(agentw) to connect to GitLab Agent Server(KAS). +# $GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS - Observability listen address used by Agent for Workspace(agentw). + +if [ -z "${GL_TOOLS_DIR}" ]; then + echo "$(date -Iseconds): \$GL_TOOLS_DIR is not set" + exit 1 +fi + +if [ -z "${GL_GITLAB_AGENT_SERVER_ADDRESS}" ]; then + echo "$(date -Iseconds): \$GL_GITLAB_AGENT_SERVER_ADDRESS is not set" + exit 1 +fi + +if [ -z "${GL_AGENTW_TOKEN_FILE_PATH}" ]; then + echo "$(date -Iseconds): \$GL_AGENTW_TOKEN_FILE_PATH is not set" + exit 1 +fi + +if [ -z "${GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS}" ]; then + echo "$(date -Iseconds): \$GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS is not set" + exit 1 +fi + +echo "$(date -Iseconds): Starting Agent for Workspace(agentw):" +echo "$(date -Iseconds): - GitLab Agent Server(KAS) address: ${GL_GITLAB_AGENT_SERVER_ADDRESS}" +echo "$(date -Iseconds): - Token file: ${GL_AGENTW_TOKEN_FILE_PATH}" +echo "$(date -Iseconds): - Observability listen address: ${GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS}" + +# The server execution is backgrounded to allow for the rest of the internal init scripts to execute. +"${GL_TOOLS_DIR}/agentw" \ + --kas-address "${GL_GITLAB_AGENT_SERVER_ADDRESS}" \ + --token-file "${GL_AGENTW_TOKEN_FILE_PATH}" \ + --observability-listen-address "${GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS}" & + +echo "$(date -Iseconds): Finished starting Agent for Workspace(agentw) in background" +echo "$(date -Iseconds): ----------------------------------------" diff --git a/ee/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter.rb b/ee/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter.rb index c6dfc08a2a441c0f980e124ce8097ef8c5701cfa..21c3f3e010e4300063f6db67480bfee0c0259581 100644 --- a/ee/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter.rb +++ b/ee/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter.rb @@ -25,6 +25,7 @@ def self.insert(context) } }, params: { + agent: agent, project: project, project_ref: String => project_ref, }, @@ -33,6 +34,29 @@ def self.insert(context) }, } + # NOTE: We will always have exactly one main_component found, because we have already + # validated this in devfile processing + main_component = components.find do |component| + # NOTE: We can't use pattern matching here, because constants can't be used in pattern matching. + # Otherwise, we could do this all in a single pattern match. + component.dig(:attributes, MAIN_COMPONENT_INDICATOR_ATTRIBUTE.to_sym) + end + + main_component => { name: String => main_component_name } + + if start_agentw?(agent) + # Add the start_agentw event + start_agentw_command_id = "gl-start-agentw-command" + commands << { + id: start_agentw_command_id, + exec: { + commandLine: INTERNAL_POSTSTART_COMMAND_START_AGENTW_SCRIPT, + component: main_component_name, + label: INTERNAL_BLOCKING_COMMAND_LABEL + } + } + end + project_cloning_successful_file = "#{volume_path}/#{PROJECT_CLONING_SUCCESSFUL_FILE_NAME}" clone_dir = "#{volume_path}/#{project.path}" project_url = project.http_url_to_repo @@ -56,16 +80,6 @@ def self.insert(context) clone_depth_option: clone_depth_option ) - # NOTE: We will always have exactly one main_component found, because we have already - # validated this in devfile processing - main_component = components.find do |component| - # NOTE: We can't use pattern matching here, because constants can't be used in pattern matching. - # Otherwise, we could do this all in a single pattern match. - component.dig(:attributes, MAIN_COMPONENT_INDICATOR_ATTRIBUTE.to_sym) - end - - main_component => { name: String => main_component_name } - commands << { id: clone_project_command_id, exec: { @@ -149,12 +163,24 @@ def self.insert(context) # Insert the unshallow command after the clone command, if the FF is enabled and clone_depth_option is set. commands_to_prepend.insert(1, clone_unshallow_command_id) unless clone_depth_option.empty? + # Insert the start agentw command at the beginning, if the appropriate values are set. + commands_to_prepend.insert(0, start_agentw_command_id) if start_agentw?(agent) # Prepend internal commands so they are executed before any user-defined poststart events. poststart_events.prepend(*commands_to_prepend) context end + + # @param [Clusters::Agent] agent + # @return [TrueClass, FalseClass] + def self.start_agentw?(agent) + gitlab_workspaces_proxy_http_enabled = + agent.unversioned_latest_workspaces_agent_config.gitlab_workspaces_proxy_http_enabled + WorkspaceOperations::WorkspaceUrlHelper.common_workspace_host_suffix?(gitlab_workspaces_proxy_http_enabled) + end + + private_class_method :start_agentw? end end end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb b/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb index 3120b89058a172caf66174645d627c218d8a2957..4559d9865577404fea574048faee438d55b67fe9 100644 --- a/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb +++ b/ee/lib/remote_development/workspace_operations/create/workspace_creator.rb @@ -44,12 +44,8 @@ def self.create(context) workspace.devfile_path = devfile_path workspace.devfile = devfile_yaml workspace.processed_devfile = YAML.dump(processed_devfile.deep_stringify_keys) - - set_workspace_url( - workspace: workspace, - agent_dns_zone: agent.unversioned_latest_workspaces_agent_config.dns_zone, - project_dir: project_dir - ) + workspace.url_prefix = "#{WORKSPACE_EDITOR_PORT}-#{workspace.name}" + workspace.url_query_string = { folder: project_dir }.to_query # associations for workspace workspace.user = user @@ -71,24 +67,6 @@ def self.create(context) }) ) end - - # @param [Workspace] workspace - # @param [String] agent_dns_zone - # @param [String] project_dir - # @return [void] - def self.set_workspace_url(workspace:, agent_dns_zone:, project_dir:) - host = "#{WORKSPACE_EDITOR_PORT}-#{workspace.name}.#{agent_dns_zone}" - query = { folder: project_dir }.to_query - - # NOTE: Use URI builder to ensure that we are building a valid URI, then retrieve parts from it - - uri = URI::HTTPS.build(host: host, query: query) - - workspace.url_prefix = uri.hostname.gsub(".#{agent_dns_zone}", '') - # noinspection RubyMismatchedArgumentType - RubyMine thinks this is nilable for some reason - workspace.url_query_string = uri.query - end - private_class_method :set_workspace_url end end end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_variables_builder.rb b/ee/lib/remote_development/workspace_operations/create/workspace_variables_builder.rb index e028488b3199def9752db079e5e3471efc14f3ac..2df8af890a62badd8a1b3f134e43306666d801b1 100644 --- a/ee/lib/remote_development/workspace_operations/create/workspace_variables_builder.rb +++ b/ee/lib/remote_development/workspace_operations/create/workspace_variables_builder.rb @@ -8,19 +8,21 @@ class WorkspaceVariablesBuilder include Files include Enums::WorkspaceVariable - # @param [String] name - # @param [String] dns_zone + # rubocop:disable Metrics/ParameterLists -- Abstracting this further will not help. + # @param [String] domain_template + # @param [String] gitlab_kas_external_url # @param [String] personal_access_token_value # @param [String] user_name # @param [String] user_email # @param [Integer] workspace_id + # @param [String] workspace_token # @param [Integer] workspace_actual_state # @param [Hash] vscode_extension_marketplace # @param [Array] variables # @return [Array] def self.build( - name:, dns_zone:, personal_access_token_value:, user_name:, user_email:, workspace_id:, - vscode_extension_marketplace:, variables: + domain_template:, gitlab_kas_external_url:, personal_access_token_value:, user_name:, user_email:, + workspace_id:, workspace_token:, vscode_extension_marketplace:, variables: ) vscode_extension_marketplace => { service_url: String => vscode_extension_marketplace_service_url, @@ -115,7 +117,7 @@ def self.build( # the GDK in a workspce: `support/gitlab-remote-development/setup_workspace.rb` { key: "GL_WORKSPACE_DOMAIN_TEMPLATE", - value: "${PORT}-#{name}.#{dns_zone}", + value: domain_template, variable_type: ENVIRONMENT_TYPE, workspace_id: workspace_id }, @@ -157,6 +159,32 @@ def self.build( value: TOKEN_FILE_PATH, variable_type: ENVIRONMENT_TYPE, workspace_id: workspace_id + }, + #------------------------------------------------------------------- + # The workspace's token used by Agent for Workspace(agentw) to connect with GitLab Agent Server(KAS). + { + key: AGENTW_TOKEN_FILE_NAME, + value: workspace_token, + variable_type: FILE_TYPE, + workspace_id: workspace_id + }, + { + key: "GL_AGENTW_TOKEN_FILE_PATH", + value: AGENTW_TOKEN_FILE_PATH, + variable_type: ENVIRONMENT_TYPE, + workspace_id: workspace_id + }, + { + key: "GL_GITLAB_AGENT_SERVER_ADDRESS", + value: gitlab_kas_external_url, + variable_type: ENVIRONMENT_TYPE, + workspace_id: workspace_id + }, + { + key: "GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS", + value: AGENTW_OBSERVABILITY_LISTEN_ADDRESS, + variable_type: ENVIRONMENT_TYPE, + workspace_id: workspace_id } #------------------------------------------------------------------- ] @@ -173,6 +201,7 @@ def self.build( internal_variables + user_provided_variables end + # rubocop:enable Metrics/ParameterLists end end end diff --git a/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb b/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb index bf2069fe6c706e127331699c09e0d81253c6e965..23f17ab31b542afd08a8ad8a0e19da019861ae49 100644 --- a/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb +++ b/ee/lib/remote_development/workspace_operations/create/workspace_variables_creator.rb @@ -14,7 +14,10 @@ def self.create(context) personal_access_token: PersonalAccessToken => personal_access_token, user: User => user, vscode_extension_marketplace: Hash => vscode_extension_marketplace, - params: Hash => params + params: Hash => params, + settings: { + gitlab_kas_external_url: gitlab_kas_external_url + } } params => { variables: Array => user_provided_variables @@ -22,13 +25,29 @@ def self.create(context) # When we have the ability to define variables for workspaces # at project/group/instance level, add them here. variables = user_provided_variables + workspace_name = workspace.name + dns_zone = workspace.workspaces_agent_config.dns_zone + gitlab_workspaces_proxy_http_enabled = workspace.workspaces_agent_config.gitlab_workspaces_proxy_http_enabled + domain_template = RemoteDevelopment::WorkspaceOperations::WorkspaceUrlHelper.url_template( + workspace_name, + dns_zone, + gitlab_workspaces_proxy_http_enabled + ) + # To keep things simple, we always inject the workspace token. It's value will only be set if required. + # Else, it will be an empty string. + workspace_token = "" + if WorkspaceOperations::WorkspaceUrlHelper.common_workspace_host_suffix?(gitlab_workspaces_proxy_http_enabled) + workspace_token = workspace.workspace_token.token + end + workspace_variables = WorkspaceVariablesBuilder.build( - name: workspace.name, - dns_zone: workspace.workspaces_agent_config.dns_zone, + domain_template: domain_template, + gitlab_kas_external_url: gitlab_kas_external_url, personal_access_token_value: personal_access_token.token, user_name: user.name, user_email: user.email, workspace_id: workspace.id, + workspace_token: workspace_token, vscode_extension_marketplace: vscode_extension_marketplace, variables: variables ) diff --git a/ee/lib/remote_development/workspace_operations/workspace_operations_constants.rb b/ee/lib/remote_development/workspace_operations/workspace_operations_constants.rb index 64afb6c5c6c226ed98277ad42d011c0444bed306..16dd6bc204d1b25912b7178a147a3c0d76182dd8 100644 --- a/ee/lib/remote_development/workspace_operations/workspace_operations_constants.rb +++ b/ee/lib/remote_development/workspace_operations/workspace_operations_constants.rb @@ -34,7 +34,7 @@ module WorkspaceOperationsConstants "#{VARIABLES_VOLUME_PATH}/#{WORKSPACE_RECONCILED_ACTUAL_STATE_FILE_NAME}".freeze # Image digest used to avoid arm64 compatibility issues in local development # See https://gitlab.com/gitlab-org/gitlab/-/issues/550128 for tracking arm64 support - WORKSPACE_TOOLS_IMAGE = "registry.gitlab.com/gitlab-org/gitlab-build-images:20250627091546-workspaces-tools@sha256:9bf96edd6a7e64ee898d774f55e153f78b85e2a911e565158e374efdd2def2c5" # rubocop:disable Layout/LineLength, Lint/RedundantCopDisableDirective -- Docker image should not be in multi-lines + WORKSPACE_TOOLS_IMAGE = "registry.gitlab.com/gitlab-org/gitlab-build-images:20250903132715-workspaces-tools@sha256:0f92a034804994de1cd5fa3b77833f3f167b652ae9bc89f478d1e74319378f0e" # rubocop:disable Layout/LineLength, Lint/RedundantCopDisableDirective -- Docker image should not be in multi-lines end end end diff --git a/ee/lib/remote_development/workspace_operations/workspace_url_helper.rb b/ee/lib/remote_development/workspace_operations/workspace_url_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..b9113d8f54e1ec266db39f195c38d97693ba3ef5 --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/workspace_url_helper.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module RemoteDevelopment + module WorkspaceOperations + class WorkspaceUrlHelper + # @return [String] + def self.url_template(name, dns_zone, gitlab_workspaces_proxy_http_enabled) + "${PORT}-#{name}.#{workspace_host_suffix(dns_zone, gitlab_workspaces_proxy_http_enabled)}" + end + + # @return [String] + def self.url(url_prefix, url_query_string, dns_zone, gitlab_workspaces_proxy_http_enabled) + host = "#{url_prefix}.#{workspace_host_suffix(dns_zone, gitlab_workspaces_proxy_http_enabled)}" + hostname, _, port = host.rpartition(":") + args = if Integer(port, exception: false).nil? + { host: host, query: url_query_string, path: "/" } + else + { host: hostname, port: port, query: url_query_string, path: "/" } + end + + URI::HTTPS.build(**args).to_s + end + + # @return [Boolean] + def self.common_workspace_host_suffix?(gitlab_workspaces_proxy_http_enabled) + gitlab_config_workspaces_enabled? && !gitlab_workspaces_proxy_http_enabled + end + + # @return [String] + def self.workspace_host_suffix(dns_zone, gitlab_workspaces_proxy_http_enabled) + if common_workspace_host_suffix?(gitlab_workspaces_proxy_http_enabled) + Gitlab.config["workspaces"]["host"] + else + dns_zone + end + end + + def self.gitlab_config_workspaces_enabled? + !!Gitlab.config["workspaces"]&.fetch("enabled", false) + end + + private_class_method :gitlab_config_workspaces_enabled? + end + end +end diff --git a/ee/spec/factories/remote_development/workspaces.rb b/ee/spec/factories/remote_development/workspaces.rb index cba08ed66e21155da2c1fd15a912be90cca105b0..10fc4e43955075bae636ef2010dc15bcb2c9267d 100644 --- a/ee/spec/factories/remote_development/workspaces.rb +++ b/ee/spec/factories/remote_development/workspaces.rb @@ -122,13 +122,34 @@ end unless evaluator.without_workspace_variables + workspace_name = workspace.name + dns_zone = workspace.workspaces_agent_config.dns_zone + gitlab_workspaces_proxy_http_enabled = workspace.workspaces_agent_config.gitlab_workspaces_proxy_http_enabled + domain_template = RemoteDevelopment::WorkspaceOperations::WorkspaceUrlHelper.url_template( + workspace_name, + dns_zone, + gitlab_workspaces_proxy_http_enabled + ) + common_host_prefix = + RemoteDevelopment::WorkspaceOperations::WorkspaceUrlHelper.common_workspace_host_suffix?( + gitlab_workspaces_proxy_http_enabled + ) + + workspace_token = "" + # The model's before_save and after_save ensures that the token is only associated for workspaces who are + # in the running state. + if common_host_prefix && desired_state == RemoteDevelopment::WorkspaceOperations::States::RUNNING + workspace_token = workspace.workspace_token.token + end + workspace_variables = RemoteDevelopment::WorkspaceOperations::Create::WorkspaceVariablesBuilder.build( - name: workspace.name, - dns_zone: workspace.workspaces_agent_config.dns_zone, + domain_template: domain_template, + gitlab_kas_external_url: RemoteDevelopment::Settings.get_single_setting(:gitlab_kas_external_url), personal_access_token_value: workspace.personal_access_token.token, user_name: workspace.user.name, user_email: workspace.user.email, workspace_id: workspace.id, + workspace_token: workspace_token, vscode_extension_marketplace: ::WebIde::ExtensionMarketplacePreset.open_vsx.values, variables: [] ) diff --git a/ee/spec/fixtures/remote_development/example.agent_config.yaml b/ee/spec/fixtures/remote_development/example.agent_config.yaml index 7dc257701efc664b1e2f5543d24edcdccd111e6b..bae2715d2dde02ad3350dbe20575a8b2f905c5b6 100644 --- a/ee/spec/fixtures/remote_development/example.agent_config.yaml +++ b/ee/spec/fixtures/remote_development/example.agent_config.yaml @@ -7,6 +7,8 @@ remote_development: dns_zone: workspaces.dev.test gitlab_workspaces_proxy: namespace: "gitlab-workspaces" + http_enabled: true + ssh_enabled: true network_policy: enabled: true diff --git a/ee/spec/fixtures/remote_development/example.internal-poststart-commands-inserted-devfile-with-agentw.yaml.erb b/ee/spec/fixtures/remote_development/example.internal-poststart-commands-inserted-devfile-with-agentw.yaml.erb new file mode 100644 index 0000000000000000000000000000000000000000..385367971d2fee8101ece91f1147d3a64a3de625 --- /dev/null +++ b/ee/spec/fixtures/remote_development/example.internal-poststart-commands-inserted-devfile-with-agentw.yaml.erb @@ -0,0 +1,139 @@ +--- +schemaVersion: 2.2.0 +metadata: {} +components: + - name: tooling-container + attributes: + gl/inject-editor: true + overrideCommand: true + container: + image: quay.io/mloriedo/universal-developer-image:ubi8-dw-demo + args: + - | + <%= indent_yaml_literal(CONTAINER_KEEPALIVE_COMMAND_ARGS, 10) %> + command: + - "/bin/sh" + - "-c" + env: + - name: GL_TOOLS_DIR + value: "/projects/.gl-tools" + - name: GL_VSCODE_LOG_LEVEL + value: "info" + - name: GL_VSCODE_PORT + value: "<%= WORKSPACE_EDITOR_PORT %>" + - name: GL_SSH_PORT + value: "<%= WORKSPACE_SSH_PORT %>" + - name: GL_VSCODE_ENABLE_MARKETPLACE + value: "false" + endpoints: + - name: editor-server + targetPort: <%= WORKSPACE_EDITOR_PORT %> + exposure: public + secure: true + protocol: https + - name: ssh-server + targetPort: <%= WORKSPACE_SSH_PORT %> + exposure: internal + secure: true + dedicatedPod: false + mountSources: true + - name: database-container + attributes: + overrideCommand: false + container: + image: mysql + env: + - name: MYSQL_ROOT_PASSWORD + value: "my-secret-pw" + dedicatedPod: false + mountSources: true + - name: user-defined-entrypoint-cmd-component + attributes: + overrideCommand: false + container: + image: quay.io/mloriedo/universal-developer-image:ubi8-dw-demo + command: ["echo"] + args: ["-n", "user-defined entrypoint command"] + dedicatedPod: false + mountSources: true + - name: gl-tools-injector + attributes: + overrideCommand: false + container: + image: <%= WORKSPACE_TOOLS_IMAGE %> + env: + - name: GL_TOOLS_DIR + value: "/projects/.gl-tools" + memoryLimit: 512Mi + memoryRequest: 256Mi + cpuLimit: 500m + cpuRequest: 100m +commands: + - id: user-defined-command + exec: + component: tooling-container + commandLine: echo 'user-defined postStart command' + hotReloadCapable: false + - id: gl-tools-injector-command + apply: + component: gl-tools-injector + - id: gl-start-agentw-command + exec: + commandLine: | + <%= indent_yaml_literal(INTERNAL_POSTSTART_COMMAND_START_AGENTW_SCRIPT, 8) %> + component: tooling-container + label: <%= INTERNAL_BLOCKING_COMMAND_LABEL %> + - id: gl-clone-project-command + exec: + commandLine: | + <%= + script = INTERNAL_POSTSTART_COMMAND_CLONE_PROJECT_SCRIPT + indent_yaml_literal(script, 8) + %> + component: tooling-container + label: <%= INTERNAL_BLOCKING_COMMAND_LABEL %> + - id: gl-clone-unshallow-command + exec: + commandLine: | + <%= + script = INTERNAL_POSTSTART_COMMAND_CLONE_UNSHALLOW_SCRIPT + indent_yaml_literal(script, 8) + %> + component: tooling-container + label: <%= INTERNAL_BLOCKING_COMMAND_LABEL %> + - id: gl-start-sshd-command + exec: + commandLine: | + <%= indent_yaml_literal(INTERNAL_POSTSTART_COMMAND_START_SSHD_SCRIPT, 8) %> + component: tooling-container + label: <%= INTERNAL_BLOCKING_COMMAND_LABEL %> + - id: gl-init-tools-command + exec: + commandLine: | + <%= indent_yaml_literal(INTERNAL_POSTSTART_COMMAND_START_VSCODE_SCRIPT, 8) %> + component: tooling-container + label: <%= INTERNAL_BLOCKING_COMMAND_LABEL %> + - id: gl-sleep-until-container-is-running-command + exec: + commandLine: | + <%= + script = format( + INTERNAL_POSTSTART_COMMAND_SLEEP_UNTIL_WORKSPACE_IS_RUNNING_SCRIPT, + workspace_reconciled_actual_state_file_path: WORKSPACE_RECONCILED_ACTUAL_STATE_FILE_PATH + ) + indent_yaml_literal(script, 8) + %> + component: tooling-container + label: <%= INTERNAL_COMMAND_LABEL %> +events: + preStart: + - gl-tools-injector-command + postStart: + - gl-start-agentw-command + - gl-clone-project-command + - gl-clone-unshallow-command + - gl-start-sshd-command + - gl-init-tools-command + - gl-sleep-until-container-is-running-command + - user-defined-command +variables: {} diff --git a/ee/spec/lib/remote_development/agent_config_operations/updater_spec.rb b/ee/spec/lib/remote_development/agent_config_operations/updater_spec.rb index f0760b8532137e4dc0a3f7d20e3b74e5345a97a4..1ce9a0db4f3845dd3e0ff390810b2c663e249962 100644 --- a/ee/spec/lib/remote_development/agent_config_operations/updater_spec.rb +++ b/ee/spec/lib/remote_development/agent_config_operations/updater_spec.rb @@ -38,8 +38,19 @@ let(:network_policy) { network_policy_without_egress } let(:gitlab_workspaces_proxy_present) { false } let(:gitlab_workspaces_proxy_namespace) { 'gitlab-workspaces' } - let(:gitlab_workspaces_proxy) { { namespace: gitlab_workspaces_proxy_namespace } } let(:gitlab_workspaces_proxy_namespace_present) { true } + let(:gitlab_workspaces_proxy_http_enabled) { true } + let(:gitlab_workspaces_proxy_http_enabled_present) { true } + let(:gitlab_workspaces_proxy_ssh_enabled) { true } + let(:gitlab_workspaces_proxy_ssh_enabled_present) { true } + let(:gitlab_workspaces_proxy) do + val = {} + val[:namespace] = gitlab_workspaces_proxy_namespace if gitlab_workspaces_proxy_namespace_present + val[:http_enabled] = gitlab_workspaces_proxy_http_enabled if gitlab_workspaces_proxy_http_enabled_present + val[:ssh_enabled] = gitlab_workspaces_proxy_ssh_enabled if gitlab_workspaces_proxy_ssh_enabled_present + + val + end let(:default_resources_per_workspace_container) { {} } let(:max_resources_per_workspace) { {} } @@ -71,14 +82,7 @@ remote_development_config['enabled'] = enabled if enabled_present # noinspection RubyMismatchedArgumentType - RubyMine is misinterpreting types for Hash values remote_development_config['network_policy'] = network_policy if network_policy_present - - remote_development_config['gitlab_workspaces_proxy'] = - if gitlab_workspaces_proxy_present && gitlab_workspaces_proxy_namespace_present - gitlab_workspaces_proxy - elsif gitlab_workspaces_proxy_present - {} - end - + remote_development_config['gitlab_workspaces_proxy'] = gitlab_workspaces_proxy if gitlab_workspaces_proxy_present remote_development_config['default_resources_per_workspace_container'] = default_resources_per_workspace_container remote_development_config['max_resources_per_workspace'] = max_resources_per_workspace @@ -252,6 +256,18 @@ it_behaves_like 'successful update' end + + context 'when gitlab_workspaces_proxy.http_enabled is explicitly specified in the config passed' do + let(:gitlab_workspaces_proxy_http_enabled) { false } + + it_behaves_like 'successful update' + end + + context 'when gitlab_workspaces_proxy.ssh_enabled is explicitly specified in the config passed' do + let(:gitlab_workspaces_proxy_ssh_enabled) { false } + + it_behaves_like 'successful update' + end end context 'when default_resources_per_workspace_container is present in the config passed' do diff --git a/ee/spec/lib/remote_development/settings/settings_initializer_spec.rb b/ee/spec/lib/remote_development/settings/settings_initializer_spec.rb index 161ed2db34df812a4a4b409f5545e9d03372312b..b7e0e3f9192f7cf9031786c59e2a20d5a85c4f54 100644 --- a/ee/spec/lib/remote_development/settings/settings_initializer_spec.rb +++ b/ee/spec/lib/remote_development/settings/settings_initializer_spec.rb @@ -92,10 +92,10 @@ default_resources_per_workspace_container: Hash, default_runtime_class: String, full_reconciliation_interval_seconds: Integer, - gitlab_workspaces_proxy_http_enabled: :Boolean, gitlab_kas_external_url: String, - gitlab_workspaces_proxy_ssh_enabled: :Boolean, + gitlab_workspaces_proxy_http_enabled: :Boolean, gitlab_workspaces_proxy_namespace: String, + gitlab_workspaces_proxy_ssh_enabled: :Boolean, image_pull_secrets: Array, labels: Hash, max_active_hours_before_stop: Integer, diff --git a/ee/spec/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter_spec.rb index 8e2767b7c81767d4c7e82126e5646f2fe603cb1e..9db97babe3c309628d7340d95e6cf91840434675 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/internal_poststart_commands_inserter_spec.rb @@ -17,10 +17,12 @@ http_url_to_repo = "#{root_url}test-group/#{project_path}.git" instance_double("Project", path: project_path, http_url_to_repo: http_url_to_repo) # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper end + let(:agent) { instance_double("Clusters::Agent") } # rubocop:disable RSpec/VerifiedDoubleReference -- We're using the quoted version so we can use fast_spec_helper let(:context) do { params: { + agent: agent, project: project, project_ref: "master" }, @@ -47,10 +49,18 @@ end end + let(:start_agentw) { false } + subject(:returned_value) do described_class.insert(context) end + before do + allow(described_class) + .to receive(:start_agentw?) + .and_return(start_agentw) + end + it "updates the devfile" do expect(returned_value[:processed_devfile]).to eq(expected_processed_devfile) end @@ -67,4 +77,15 @@ expect(command_line).to include("clone-unshallow.log") expect(command_line).to include("git rev-parse --is-shallow-repository") end + + context "when start_agentw? returns true" do + let(:start_agentw) { true } + let(:expected_processed_devfile_name) do + "example.internal-poststart-commands-inserted-devfile-with-agentw.yaml.erb" + end + + it "updates the devfile" do + expect(returned_value[:processed_devfile]).to eq(expected_processed_devfile) + end + end end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb index 4ee644a2b8a2ffaf92f449512988e3de2b7bc5f3..bb29a9e15a57e73c229341f14939baaca47a8bff 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/main_integration_spec.rb @@ -17,6 +17,7 @@ let(:project_ref) { 'master' } let(:devfile_path) { '.devfile.yaml' } let(:devfile_fixture_name) { 'example.devfile.yaml.erb' } + let(:gitlab_kas_external_url) { "ws://kas.example.com/-/external/namespace/path" } let(:devfile_yaml) { read_devfile_yaml(devfile_fixture_name) } let(:expected_processed_devfile) { example_processed_devfile } let(:logger) { instance_double(Logger) } @@ -74,7 +75,8 @@ { project_cloner_image: 'alpine/git:2.45.2', tools_injector_image: tools_injector_image_from_settings, - default_devfile_yaml: default_devfile_yaml + default_devfile_yaml: default_devfile_yaml, + gitlab_kas_external_url: gitlab_kas_external_url } end diff --git a/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_builder_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_builder_spec.rb index 3b5575422f3a5819f17bbc908e539da65556500d..ae76ee6e4d03ef017c3f1ff671d4ab1261ceb24c 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_builder_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_builder_spec.rb @@ -5,12 +5,13 @@ RSpec.describe ::RemoteDevelopment::WorkspaceOperations::Create::WorkspaceVariablesBuilder, feature_category: :workspaces do include_context "with constant modules" - let(:name) { "name" } - let(:dns_zone) { "example.dns.zone" } + let(:domain_template) { "${PORT}-name.example.host.suffix" } + let(:gitlab_kas_external_url) { "ws://kas.example.com/-/external/namespace/path" } let(:personal_access_token_value) { "example-pat-value" } let(:user_name) { "example.user.name" } let(:user_email) { "example@user.email" } let(:workspace_id) { 1 } + let(:workspace_token) { "example-agentw-token-value" } let(:vscode_extensions_gallery_service_url) { "https://open-vsx.org/vscode/gallery" } let(:vscode_extensions_gallery_item_url) { "https://open-vsx.org/vscode/item" } let(:vscode_extensions_gallery_resource_url_template) { "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{versionRaw}/{path}" } @@ -85,7 +86,7 @@ }, { key: "GL_WORKSPACE_DOMAIN_TEMPLATE", - value: "${PORT}-name.example.dns.zone", + value: domain_template, variable_type: RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE, workspace_id: workspace_id }, @@ -119,6 +120,30 @@ variable_type: RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE, workspace_id: workspace_id }, + { + key: create_constants_module::AGENTW_TOKEN_FILE_NAME, + value: "example-agentw-token-value", + variable_type: RemoteDevelopment::Enums::WorkspaceVariable::FILE_TYPE, + workspace_id: workspace_id + }, + { + key: "GL_AGENTW_TOKEN_FILE_PATH", + value: create_constants_module::AGENTW_TOKEN_FILE_PATH, + variable_type: RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE, + workspace_id: workspace_id + }, + { + key: "GL_GITLAB_AGENT_SERVER_ADDRESS", + value: gitlab_kas_external_url, + variable_type: RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE, + workspace_id: workspace_id + }, + { + key: "GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS", + value: create_constants_module::AGENTW_OBSERVABILITY_LISTEN_ADDRESS, + variable_type: RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE, + workspace_id: workspace_id + }, { key: "VAR1", value: "value 1", @@ -138,12 +163,13 @@ subject(:variables) do described_class.build( - name: name, - dns_zone: dns_zone, + domain_template: domain_template, + gitlab_kas_external_url: gitlab_kas_external_url, personal_access_token_value: personal_access_token_value, user_name: user_name, user_email: user_email, workspace_id: workspace_id, + workspace_token: workspace_token, vscode_extension_marketplace: { service_url: vscode_extensions_gallery_service_url, item_url: vscode_extensions_gallery_item_url, diff --git a/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_creator_spec.rb b/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_creator_spec.rb index 0035a98dc14cfab13e538ab843a91f5b8594fd33..2b05d8345295f9016423842dfb3c19191e13a83e 100644 --- a/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_creator_spec.rb +++ b/ee/spec/lib/remote_development/workspace_operations/create/workspace_variables_creator_spec.rb @@ -11,7 +11,11 @@ # noinspection RubyArgCount -- https://handbook.gitlab.com/handbook/tools-and-tips/editors-and-ides/jetbrains-ides/tracked-jetbrains-issues/#ruby-31542 let_it_be(:user) { create(:user) } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:workspace) { create(:workspace, user: user, personal_access_token: personal_access_token) } + # The desired state of the workspace is set to running so that a workspace token gets associated to it. + let_it_be(:workspace) do + create(:workspace, user: user, personal_access_token: personal_access_token, desired_state: states_module::RUNNING) + end + let(:vscode_extension_marketplace) do { service_url: "service_url", @@ -20,6 +24,7 @@ } end + let(:gitlab_kas_external_url) { "ws://kas.example.com/-/external/namespace/path" } let(:variable_type) { RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE } let(:user_provided_variables) do @@ -37,6 +42,9 @@ vscode_extension_marketplace: vscode_extension_marketplace, params: { variables: user_provided_variables + }, + settings: { + gitlab_kas_external_url: gitlab_kas_external_url } } end @@ -48,7 +56,7 @@ context "when workspace variables create is successful" do let(:valid_variable_type) { RemoteDevelopment::Enums::WorkspaceVariable::ENVIRONMENT_TYPE } let(:variable_type) { valid_variable_type } - let(:expected_number_of_records_saved) { 19 } + let(:expected_number_of_records_saved) { 23 } it "creates the workspace variable records and returns ok result containing original context" do expect { result }.to change { workspace.workspace_variables.count }.by(expected_number_of_records_saved) @@ -63,7 +71,7 @@ context "when workspace create fails" do let(:invalid_variable_type) { 9999999 } let(:variable_type) { invalid_variable_type } - let(:expected_number_of_records_saved) { 17 } + let(:expected_number_of_records_saved) { 21 } it "does not create the invalid workspace variable records and returns an error result with model errors" do # NOTE: Any valid records will be saved if they are first in the array before the invalid record, but that's OK, diff --git a/ee/spec/lib/remote_development/workspace_operations/workspace_url_helper_spec.rb b/ee/spec/lib/remote_development/workspace_operations/workspace_url_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..704b056b94e3160d9e383c10d962b3ad8266e0ed --- /dev/null +++ b/ee/spec/lib/remote_development/workspace_operations/workspace_url_helper_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require "fast_spec_helper" + +RSpec.describe RemoteDevelopment::WorkspaceOperations::WorkspaceUrlHelper, feature_category: :workspaces do + let(:name) { "name" } + let(:port) { 1234 } + let(:dns_zone) { "example.dns.zone" } + let(:gitlab_workspaces_proxy_http_enabled) { true } + let(:url_prefix) { "#{port}-#{name}" } + let(:url_query_string) { { example: "/value" }.to_query } + let(:host_suffix) { dns_zone } + let(:expected_url_template) { "${PORT}-name.example.dns.zone" } + let(:expected_url) { "https://1234-name.example.dns.zone/?example=%2Fvalue" } + + subject(:returned_value) do + url_template = described_class.url_template(name, dns_zone, gitlab_workspaces_proxy_http_enabled) + url = described_class.url(url_prefix, url_query_string, dns_zone, gitlab_workspaces_proxy_http_enabled) + common_workspace_host_suffix = described_class.common_workspace_host_suffix?(gitlab_workspaces_proxy_http_enabled) + workspace_host_suffix = described_class.workspace_host_suffix(dns_zone, gitlab_workspaces_proxy_http_enabled) + + { + url_template: url_template, + url: url, + common_workspace_host_suffix: common_workspace_host_suffix, + workspace_host_suffix: workspace_host_suffix + } + end + + it "uses dns_zone for workspace_url" do + expect(returned_value).to eq( + { + url_template: expected_url_template, + url: expected_url, + common_workspace_host_suffix: false, + workspace_host_suffix: dns_zone + } + ) + end + + describe "when gitlab_workspaces_proxy_http_enabled is set to false" do + let(:gitlab_workspaces_proxy_http_enabled) { false } + + before do + allow(Gitlab).to receive(:config).and_return(gitlab_config) + end + + context "when gitlab config is set correctly" do + let(:gitlab_config_workspace_host) { "config.workspaces.host:1234" } + let(:expected_url_template) { "${PORT}-name.config.workspaces.host:1234" } + let(:expected_url) { "https://1234-name.config.workspaces.host:1234/?example=%2Fvalue" } + let(:gitlab_config) { { "workspaces" => { "enabled" => true, "host" => gitlab_config_workspace_host } } } + + it "uses gitlab config workspace host for workspace_url" do + expect(returned_value).to eq( + { + url_template: expected_url_template, + url: expected_url, + common_workspace_host_suffix: true, + workspace_host_suffix: gitlab_config_workspace_host + } + ) + end + end + + context "when gitlab config is set incorrectly" do + let(:gitlab_config) { { "workspaces" => { "enabled" => false } } } + + it "uses dns_zone for workspace_url" do + expect(returned_value).to eq( + { + url_template: expected_url_template, + url: expected_url, + common_workspace_host_suffix: false, + workspace_host_suffix: dns_zone + } + ) + end + end + + context "when gitlab config is not set" do + let(:gitlab_config) { {} } + + it "uses dns_zone for workspace_url" do + expect(returned_value).to eq( + { + url_template: expected_url_template, + url: expected_url, + common_workspace_host_suffix: false, + workspace_host_suffix: dns_zone + } + ) + end + end + end +end diff --git a/ee/spec/models/remote_development/workspace_spec.rb b/ee/spec/models/remote_development/workspace_spec.rb index fb198e9cd35dec50ccea9e8d81159f9e75ecb07e..d4a545e0ff9d5b23212498474877fc88a028302b 100644 --- a/ee/spec/models/remote_development/workspace_spec.rb +++ b/ee/spec/models/remote_development/workspace_spec.rb @@ -144,47 +144,6 @@ } end end - - describe "WorkspaceToken deletion" do - before do - workspace.save! - end - - context "when changing from Running" do - using RSpec::Parameterized::TableSyntax - - let(:desired_state) { states_module::RUNNING } - - shared_examples "deletes the associated WorkspaceToken record if it exists" do - it "deletes the associated WorkspaceToken record if it exists when Stopped" do - expect { workspace.update!(desired_state: new_desired_state) }.to change { - RemoteDevelopment::WorkspaceToken.count - }.by(-1) - - expect(workspace.workspace_token).to be_nil - end - - it "does not raise an error when WorkspaceToken record does not exist" do - workspace.workspace_token.destroy! - workspace.reload - - expect { workspace.update!(desired_state: new_desired_state) }.not_to raise_error - end - end - - where(:new_desired_state) do - [ - states_module::RESTART_REQUESTED, - states_module::STOPPED, - states_module::TERMINATED - ] - end - - with_them do - it_behaves_like "deletes the associated WorkspaceToken record if it exists" - end - end - end end describe "before_validation" do @@ -317,6 +276,47 @@ end end end + + describe "WorkspaceToken deletion" do + before do + workspace.save! + end + + context "when changing from Running" do + using RSpec::Parameterized::TableSyntax + + let(:desired_state) { states_module::RUNNING } + + shared_examples "deletes the associated WorkspaceToken record if it exists" do + it "deletes the associated WorkspaceToken record if it exists when Stopped" do + expect { workspace.update!(desired_state: new_desired_state) }.to change { + RemoteDevelopment::WorkspaceToken.count + }.by(-1) + + expect(workspace.workspace_token).to be_nil + end + + it "does not raise an error when WorkspaceToken record does not exist" do + workspace.workspace_token.destroy! + workspace.reload + + expect { workspace.update!(desired_state: new_desired_state) }.not_to raise_error + end + end + + where(:new_desired_state) do + [ + states_module::RESTART_REQUESTED, + states_module::STOPPED, + states_module::TERMINATED + ] + end + + with_them do + it_behaves_like "deletes the associated WorkspaceToken record if it exists" + end + end + end end end diff --git a/ee/spec/requests/remote_development/integration_spec.rb b/ee/spec/requests/remote_development/integration_spec.rb index 21816cffd439119bd897d860426bca5c6af1432a..2cff6bffd0aa451fe8e6b4fbbc06980fe8eedd66 100644 --- a/ee/spec/requests/remote_development/integration_spec.rb +++ b/ee/spec/requests/remote_development/integration_spec.rb @@ -9,6 +9,10 @@ include RemoteDevelopment::IntegrationSpecHelpers include_context "with remote development shared fixtures" + let(:agentw_token) { "" } + let(:agentw_observability_listen_address) { create_constants_module::AGENTW_OBSERVABILITY_LISTEN_ADDRESS } + let(:agentw_token_file_path) { create_constants_module::AGENTW_TOKEN_FILE_PATH } + let(:gitlab_kas_external_url) { RemoteDevelopment::Settings.get_single_setting(:gitlab_kas_external_url) } let(:token_file_name) { create_constants_module::TOKEN_FILE_NAME } let(:token_file_path) { create_constants_module::TOKEN_FILE_PATH } let(:git_credential_store_script) { files_module::GIT_CREDENTIAL_STORE_SCRIPT } @@ -26,7 +30,6 @@ let(:jwt_secret) { SecureRandom.random_bytes(Gitlab::Kas::SECRET_LENGTH) } let(:agent_token) { create(:cluster_agent_token, agent: agent) } - let(:gitlab_workspaces_proxy_namespace) { "gitlab-workspaces" } let(:dns_zone) { "integration-spec-workspaces.localdev.me" } let(:workspaces_per_user_quota) { 20 } let(:workspaces_quota) { 100 } @@ -156,6 +159,10 @@ def expected_internal_variables(random_string:, user:) { key: "GIT_CONFIG_VALUE_0", type: :environment, value: git_credential_store_script_file_path }, { key: "GIT_CONFIG_VALUE_1", type: :environment, value: user.name }, { key: "GIT_CONFIG_VALUE_2", type: :environment, value: user.email }, + { key: "gl_agentw_token", type: :file, value: agentw_token }, + { key: "GL_AGENTW_TOKEN_FILE_PATH", type: :environment, value: agentw_token_file_path }, + { key: "GL_AGENTW_OBSERVABILITY_LISTEN_ADDRESS", type: :environment, value: agentw_observability_listen_address }, + { key: "GL_GITLAB_AGENT_SERVER_ADDRESS", type: :environment, value: gitlab_kas_external_url }, { key: "GL_VSCODE_EXTENSION_MARKETPLACE_ITEM_URL", type: :environment, value: "https://open-vsx.org/vscode/item" }, { key: "GL_VSCODE_EXTENSION_MARKETPLACE_RESOURCE_URL_TEMPLATE", type: :environment, value: "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{versionRaw}/{path}" }, { key: "GL_VSCODE_EXTENSION_MARKETPLACE_SERVICE_URL", type: :environment, value: "https://open-vsx.org/vscode/gallery" }, diff --git a/ee/spec/support/shared_contexts/remote_development/remote_development_shared_contexts.rb b/ee/spec/support/shared_contexts/remote_development/remote_development_shared_contexts.rb index f1cf2b5bc73a66fba2de3169420085705720989e..cf9fc9786d7cc8ac333b1c1db1ba55bf564b3f23 100644 --- a/ee/spec/support/shared_contexts/remote_development/remote_development_shared_contexts.rb +++ b/ee/spec/support/shared_contexts/remote_development/remote_development_shared_contexts.rb @@ -338,6 +338,7 @@ def create_config_to_apply_yaml_stream(workspace:, **args) # @param [Boolean] include_network_policy # @param [Boolean] include_all_resources # @param [String] dns_zone + # param [Boolean] gitlab_workspaces_proxy_http_enabled # @param [Array] egress_ip_rules # @param [Hash] max_resources_per_workspace # @param [Hash] default_resources_per_workspace_container @@ -367,6 +368,7 @@ def create_config_to_apply( include_network_policy: true, include_all_resources: false, dns_zone: 'workspaces.localdev.me', + gitlab_workspaces_proxy_http_enabled: true, egress_ip_rules: [{ allow: "0.0.0.0/0", except: %w[10.0.0.0/8 172.16.0.0/12 192.168.0.0/16] @@ -485,6 +487,7 @@ def create_config_to_apply( labels: labels, annotations: workspace_inventory_annotations, legacy_poststart_container_command: legacy_poststart_container_command, + gitlab_workspaces_proxy_http_enabled: gitlab_workspaces_proxy_http_enabled, user_defined_commands: user_defined_commands ) @@ -1318,6 +1321,7 @@ def sleep_until_container_is_running_script # @param [Hash] labels # @param [Hash] annotations # @param [Boolean] legacy_poststart_container_command + # @param [Boolean] gitlab_workspaces_proxy_http_enabled # @param [Array] user_defined_commands # @return [Hash] def scripts_configmap( @@ -1326,11 +1330,13 @@ def scripts_configmap( labels:, annotations:, legacy_poststart_container_command:, + gitlab_workspaces_proxy_http_enabled:, user_defined_commands: ) user_command_ids = user_defined_commands.pluck(:id) data = { + "gl-start-agentw-command": files_module::INTERNAL_POSTSTART_COMMAND_START_AGENTW_SCRIPT, "gl-clone-project-command": clone_project_script, "gl-clone-unshallow-command": clone_unshallow_script, "gl-init-tools-command": files_module::INTERNAL_POSTSTART_COMMAND_START_VSCODE_SCRIPT, @@ -1350,6 +1356,8 @@ def scripts_configmap( legacy_poststart_commands_script end + data.delete(:"gl-start-agentw-command") if gitlab_workspaces_proxy_http_enabled + # Add each user-defined command to the data hash user_defined_commands.each do |cmd| data[cmd[:id].to_sym] = cmd[:exec][:commandLine]