From 14bc2fadb5c28a0df777f4c83945f743bc4ad44c Mon Sep 17 00:00:00 2001 From: Ashvin Sharma Date: Wed, 4 Jun 2025 15:48:29 +0530 Subject: [PATCH] Move desired config generator methods These all methods will eventually move to create/desired_config module, currently, it is kept in the same directory because there are files that will be moved with it. And these methods are temporarily used in desired_config. --- .../output/create_desired_config_generator.rb | 314 ++++++++++++++++++ .../output/desired_config_generator.rb | 288 +--------------- 2 files changed, 324 insertions(+), 278 deletions(-) create mode 100644 ee/lib/remote_development/workspace_operations/reconcile/output/create_desired_config_generator.rb diff --git a/ee/lib/remote_development/workspace_operations/reconcile/output/create_desired_config_generator.rb b/ee/lib/remote_development/workspace_operations/reconcile/output/create_desired_config_generator.rb new file mode 100644 index 00000000000000..6466b25f83a95e --- /dev/null +++ b/ee/lib/remote_development/workspace_operations/reconcile/output/create_desired_config_generator.rb @@ -0,0 +1,314 @@ +# frozen_string_literal: true + +module RemoteDevelopment + module WorkspaceOperations + module Reconcile + module Output + # TODO: This is file is to be moved to ../../create/desired_config/desired_config_generator.rb in this issue: + # It's not yet moved because there are multiple files to be moved. + # https://gitlab.com/gitlab-org/gitlab/-/issues/541907 + class CreateDesiredConfigGenerator + # @param [Array] desired_config + # @param [String] name + # @param [String] namespace + # @param [Hash] labels + # @param [Hash] annotations + # @return [void] + def self.append_inventory_config_map( + desired_config:, + name:, + namespace:, + labels:, + annotations: + ) + extra_labels = { "cli-utils.sigs.k8s.io/inventory-id": name } + + config_map = { + kind: "ConfigMap", + apiVersion: "v1", + metadata: { + name: name, + namespace: namespace, + labels: labels.merge(extra_labels), + annotations: annotations + } + } + + desired_config.append(config_map) + + nil + end + + # @param [Array] desired_config + # @param [String] name + # @param [String] namespace + # @param [Hash] labels + # @param [Hash] annotations + # @return [void] + def self.append_secret(desired_config:, name:, namespace:, labels:, annotations:) + secret = { + kind: "Secret", + apiVersion: "v1", + metadata: { + name: name, + namespace: namespace, + labels: labels, + annotations: annotations + }, + data: {} + } + + desired_config.append(secret) + + nil + end + + # @param [Array] desired_config + # @param [String] gitlab_workspaces_proxy_namespace + # @param [String] name + # @param [String] namespace + # @param [Boolean] network_policy_enabled + # @param [Array] network_policy_egress + # @param [Hash] labels + # @param [Hash] annotations + # @return [void] + def self.append_network_policy( + desired_config:, + name:, + namespace:, + gitlab_workspaces_proxy_namespace:, + network_policy_enabled:, + network_policy_egress:, + labels:, + annotations: + ) + return unless network_policy_enabled + + egress_ip_rules = network_policy_egress + + policy_types = %w[Ingress Egress] + + proxy_namespace_selector = { + matchLabels: { + "kubernetes.io/metadata.name": gitlab_workspaces_proxy_namespace + } + } + proxy_pod_selector = { + matchLabels: { + "app.kubernetes.io/name": "gitlab-workspaces-proxy" + } + } + ingress = [{ from: [{ namespaceSelector: proxy_namespace_selector, podSelector: proxy_pod_selector }] }] + + kube_system_namespace_selector = { + matchLabels: { + "kubernetes.io/metadata.name": "kube-system" + } + } + egress = [ + { + ports: [{ port: 53, protocol: "TCP" }, { port: 53, protocol: "UDP" }], + to: [{ namespaceSelector: kube_system_namespace_selector }] + } + ] + egress_ip_rules.each do |egress_rule| + egress.append( + { to: [{ ipBlock: { cidr: egress_rule[:allow], except: egress_rule[:except] } }] } + ) + end + + # Use the workspace_id as a pod selector if it is present + workspace_id = labels.fetch(:"workspaces.gitlab.com/id", nil) + pod_selector = {} + # TODO: Unconditionally add this pod selector in https://gitlab.com/gitlab-org/gitlab/-/issues/535197 + if workspace_id.present? + pod_selector[:matchLabels] = { + "workspaces.gitlab.com/id": workspace_id + } + end + + network_policy = { + apiVersion: "networking.k8s.io/v1", + kind: "NetworkPolicy", + metadata: { + annotations: annotations, + labels: labels, + name: name, + namespace: namespace + }, + spec: { + egress: egress, + ingress: ingress, + podSelector: pod_selector, + policyTypes: policy_types + } + } + + desired_config.append(network_policy) + + nil + end + + # @param [Array] desired_config + # @param [String] processed_devfile_yaml + # @param [String] name + # @param [String] namespace + # @param [Hash] labels + # @param [Hash] annotations + # @return [void] + def self.append_scripts_resources( + desired_config:, + processed_devfile_yaml:, + name:, + namespace:, + labels:, + annotations: + ) + desired_config => [ + *_, + { + kind: "Deployment", + spec: { + template: { + spec: { + containers: Array => containers, + volumes: Array => volumes + } + } + } + }, + *_ + ] + + processed_devfile = YAML.safe_load(processed_devfile_yaml).deep_symbolize_keys.to_h + + devfile_commands = processed_devfile.fetch(:commands) + devfile_events = processed_devfile.fetch(:events) + + # NOTE: This guard clause ensures we still support older running workspaces which were started before we + # added support for devfile postStart events. In that case, we don't want to add any resources + # related to the postStart script handling, because that would cause those existing workspaces + # to restart because the deployment would be updated. + return unless devfile_events[:postStart].present? + + ScriptsConfigmapAppender.append( + desired_config: desired_config, + name: name, + namespace: namespace, + labels: labels, + annotations: annotations, + devfile_commands: devfile_commands, + devfile_events: devfile_events + ) + + ScriptsVolumeInserter.insert( + configmap_name: name, + containers: containers, + volumes: volumes + ) + + KubernetesPoststartHookInserter.insert( + containers: containers, + devfile_commands: devfile_commands, + devfile_events: devfile_events + ) + + nil + end + + # @param [Array] desired_config + # @param [String] name + # @param [String] namespace + # @param [Hash] labels + # @param [Hash] annotations + # @param [Hash] max_resources_per_workspace + # @return [void] + def self.append_resource_quota( + desired_config:, + name:, + namespace:, + labels:, + annotations:, + max_resources_per_workspace:, + shared_namespace: + ) + return unless max_resources_per_workspace.present? + return if shared_namespace.present? + + max_resources_per_workspace => { + limits: { + cpu: limits_cpu, + memory: limits_memory + }, + requests: { + cpu: requests_cpu, + memory: requests_memory + } + } + + resource_quota = { + apiVersion: "v1", + kind: "ResourceQuota", + metadata: { + annotations: annotations, + labels: labels, + name: name, + namespace: namespace + }, + spec: { + hard: { + "limits.cpu": limits_cpu, + "limits.memory": limits_memory, + "requests.cpu": requests_cpu, + "requests.memory": requests_memory + } + } + } + + desired_config.append(resource_quota) + + nil + end + + # @param [Array] desired_config + # @param [String] name + # @param [String] namespace + # @param [Hash] labels + # @param [Hash] annotations + # @param [Array] image_pull_secrets + # @return [void] + def self.append_image_pull_secrets_service_account( + desired_config:, + name:, + namespace:, + labels:, + annotations:, + image_pull_secrets: + ) + image_pull_secrets_names = image_pull_secrets.map { |secret| { name: secret.fetch(:name) } } + + workspace_service_account_definition = { + apiVersion: "v1", + kind: "ServiceAccount", + metadata: { + name: name, + namespace: namespace, + annotations: annotations, + labels: labels + }, + automountServiceAccountToken: false, + imagePullSecrets: image_pull_secrets_names + } + + desired_config.append(workspace_service_account_definition) + + nil + end + + # TODO: Make all of these functions private by the end of https://gitlab.com/gitlab-org/gitlab/-/issues/541907 + # These are kept public for now to allow access to DesiredConfigGenerator + end + end + end + end +end diff --git a/ee/lib/remote_development/workspace_operations/reconcile/output/desired_config_generator.rb b/ee/lib/remote_development/workspace_operations/reconcile/output/desired_config_generator.rb index d05c6edac15000..a164afb8fe5ef2 100644 --- a/ee/lib/remote_development/workspace_operations/reconcile/output/desired_config_generator.rb +++ b/ee/lib/remote_development/workspace_operations/reconcile/output/desired_config_generator.rb @@ -40,7 +40,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) desired_config = [] - append_inventory_config_map( + CreateDesiredConfigGenerator.append_inventory_config_map( desired_config: desired_config, name: workspace_inventory_name, namespace: workspace.namespace, @@ -49,7 +49,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) ) if workspace.desired_state_terminated? - append_inventory_config_map( + CreateDesiredConfigGenerator.append_inventory_config_map( desired_config: desired_config, name: secrets_inventory_name, namespace: workspace.namespace, @@ -90,7 +90,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) desired_config.append(*resources_from_devfile_parser) - append_image_pull_secrets_service_account( + CreateDesiredConfigGenerator.append_image_pull_secrets_service_account( desired_config: desired_config, name: workspace.name, namespace: workspace.namespace, @@ -99,7 +99,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) annotations: workspace_inventory_annotations ) - append_network_policy( + CreateDesiredConfigGenerator.append_network_policy( desired_config: desired_config, name: workspace.name, namespace: workspace.namespace, @@ -110,7 +110,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) annotations: workspace_inventory_annotations ) - append_scripts_resources( + CreateDesiredConfigGenerator.append_scripts_resources( desired_config: desired_config, processed_devfile_yaml: processed_devfile_yaml, name: scripts_configmap_name, @@ -121,7 +121,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) return desired_config unless include_all_resources - append_inventory_config_map( + CreateDesiredConfigGenerator.append_inventory_config_map( desired_config: desired_config, name: secrets_inventory_name, namespace: workspace.namespace, @@ -129,7 +129,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) annotations: common_annotations ) - append_resource_quota( + CreateDesiredConfigGenerator.append_resource_quota( desired_config: desired_config, name: workspace.name, namespace: workspace.namespace, @@ -139,7 +139,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) shared_namespace: shared_namespace ) - append_secret( + CreateDesiredConfigGenerator.append_secret( desired_config: desired_config, name: env_secret_name, namespace: workspace.namespace, @@ -153,7 +153,7 @@ def self.generate_desired_config(workspace:, include_all_resources:, logger:) variables: workspace.workspace_variables.with_variable_type_environment ) - append_secret( + CreateDesiredConfigGenerator.append_secret( desired_config: desired_config, name: file_secret_name, namespace: workspace.namespace, @@ -207,30 +207,6 @@ def self.append_inventory_config_map( nil end - # @param [Array] desired_config - # @param [String] name - # @param [String] namespace - # @param [Hash] labels - # @param [Hash] annotations - # @return [void] - def self.append_secret(desired_config:, name:, namespace:, labels:, annotations:) - secret = { - kind: "Secret", - apiVersion: "v1", - metadata: { - name: name, - namespace: namespace, - labels: labels, - annotations: annotations - }, - data: {} - } - - desired_config.append(secret) - - nil - end - # @param [Array] desired_config # @param [String] secret_name # @param [ActiveRecord::Relation] variables @@ -273,251 +249,7 @@ def self.append_secret_data(desired_config:, secret_name:, data:) nil end - # @param [Array] desired_config - # @param [String] gitlab_workspaces_proxy_namespace - # @param [String] name - # @param [String] namespace - # @param [Boolean] network_policy_enabled - # @param [Array] network_policy_egress - # @param [Hash] labels - # @param [Hash] annotations - # @return [void] - def self.append_network_policy( - desired_config:, - name:, - namespace:, - gitlab_workspaces_proxy_namespace:, - network_policy_enabled:, - network_policy_egress:, - labels:, - annotations: - ) - return unless network_policy_enabled - - egress_ip_rules = network_policy_egress - - policy_types = %w[Ingress Egress] - - proxy_namespace_selector = { - matchLabels: { - "kubernetes.io/metadata.name": gitlab_workspaces_proxy_namespace - } - } - proxy_pod_selector = { - matchLabels: { - "app.kubernetes.io/name": "gitlab-workspaces-proxy" - } - } - ingress = [{ from: [{ namespaceSelector: proxy_namespace_selector, podSelector: proxy_pod_selector }] }] - - kube_system_namespace_selector = { - matchLabels: { - "kubernetes.io/metadata.name": "kube-system" - } - } - egress = [ - { - ports: [{ port: 53, protocol: "TCP" }, { port: 53, protocol: "UDP" }], - to: [{ namespaceSelector: kube_system_namespace_selector }] - } - ] - egress_ip_rules.each do |egress_rule| - egress.append( - { to: [{ ipBlock: { cidr: egress_rule[:allow], except: egress_rule[:except] } }] } - ) - end - - # Use the workspace_id as a pod selector if it is present - workspace_id = labels.fetch(:"workspaces.gitlab.com/id", nil) - pod_selector = {} - # TODO: Unconditionally add this pod selector in https://gitlab.com/gitlab-org/gitlab/-/issues/535197 - if workspace_id.present? - pod_selector[:matchLabels] = { - "workspaces.gitlab.com/id": workspace_id - } - end - - network_policy = { - apiVersion: "networking.k8s.io/v1", - kind: "NetworkPolicy", - metadata: { - annotations: annotations, - labels: labels, - name: name, - namespace: namespace - }, - spec: { - egress: egress, - ingress: ingress, - podSelector: pod_selector, - policyTypes: policy_types - } - } - - desired_config.append(network_policy) - - nil - end - - # @param [Array] desired_config - # @param [String] processed_devfile_yaml - # @param [String] name - # @param [String] namespace - # @param [Hash] labels - # @param [Hash] annotations - # @return [void] - def self.append_scripts_resources( - desired_config:, - processed_devfile_yaml:, - name:, - namespace:, - labels:, - annotations: - ) - desired_config => [ - *_, - { - kind: "Deployment", - spec: { - template: { - spec: { - containers: Array => containers, - volumes: Array => volumes - } - } - } - }, - *_ - ] - - processed_devfile = YAML.safe_load(processed_devfile_yaml).deep_symbolize_keys.to_h - - devfile_commands = processed_devfile.fetch(:commands) - devfile_events = processed_devfile.fetch(:events) - - # NOTE: This guard clause ensures we still support older running workspaces which were started before we - # added support for devfile postStart events. In that case, we don't want to add any resources - # related to the postStart script handling, because that would cause those existing workspaces - # to restart because the deployment would be updated. - return unless devfile_events[:postStart].present? - - ScriptsConfigmapAppender.append( - desired_config: desired_config, - name: name, - namespace: namespace, - labels: labels, - annotations: annotations, - devfile_commands: devfile_commands, - devfile_events: devfile_events - ) - - ScriptsVolumeInserter.insert( - configmap_name: name, - containers: containers, - volumes: volumes - ) - - KubernetesPoststartHookInserter.insert( - containers: containers, - devfile_commands: devfile_commands, - devfile_events: devfile_events - ) - - nil - end - - # @param [Array] desired_config - # @param [String] name - # @param [String] namespace - # @param [Hash] labels - # @param [Hash] annotations - # @param [Hash] max_resources_per_workspace - # @return [void] - def self.append_resource_quota( - desired_config:, - name:, - namespace:, - labels:, - annotations:, - max_resources_per_workspace:, - shared_namespace: - ) - return unless max_resources_per_workspace.present? - return if shared_namespace.present? - - max_resources_per_workspace => { - limits: { - cpu: limits_cpu, - memory: limits_memory - }, - requests: { - cpu: requests_cpu, - memory: requests_memory - } - } - - resource_quota = { - apiVersion: "v1", - kind: "ResourceQuota", - metadata: { - annotations: annotations, - labels: labels, - name: name, - namespace: namespace - }, - spec: { - hard: { - "limits.cpu": limits_cpu, - "limits.memory": limits_memory, - "requests.cpu": requests_cpu, - "requests.memory": requests_memory - } - } - } - - desired_config.append(resource_quota) - - nil - end - - # @param [Array] desired_config - # @param [String] name - # @param [String] namespace - # @param [Hash] labels - # @param [Hash] annotations - # @param [Array] image_pull_secrets - # @return [void] - def self.append_image_pull_secrets_service_account( - desired_config:, - name:, - namespace:, - labels:, - annotations:, - image_pull_secrets: - ) - image_pull_secrets_names = image_pull_secrets.map { |secret| { name: secret.fetch(:name) } } - - workspace_service_account_definition = { - apiVersion: "v1", - kind: "ServiceAccount", - metadata: { - name: name, - namespace: namespace, - annotations: annotations, - labels: labels - }, - automountServiceAccountToken: false, - imagePullSecrets: image_pull_secrets_names - } - - desired_config.append(workspace_service_account_definition) - - nil - end - - private_class_method :append_inventory_config_map, - :append_secret, :append_secret_data_from_variables, :append_secret_data, - :append_network_policy, :append_resource_quota, :append_image_pull_secrets_service_account + private_class_method :append_secret_data_from_variables, :append_secret_data end end end -- GitLab