diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
commit | 43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch) | |
tree | dceebdc68925362117480a5d672bcff122fb625b /app/models/clusters | |
parent | 20c84b99005abd1c82101dfeff264ac50d2df211 (diff) |
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'app/models/clusters')
20 files changed, 281 insertions, 841 deletions
diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb index 3478bb69707..6980ec1c2d3 100644 --- a/app/models/clusters/agent.rb +++ b/app/models/clusters/agent.rb @@ -2,6 +2,8 @@ module Clusters class Agent < ApplicationRecord + include FromUnion + self.table_name = 'cluster_agents' INACTIVE_AFTER = 1.hour.freeze @@ -11,12 +13,19 @@ module Clusters belongs_to :project, class_name: '::Project' # Otherwise, it will load ::Clusters::Project has_many :agent_tokens, -> { order_last_used_at_desc }, class_name: 'Clusters::AgentToken', inverse_of: :agent + has_many :active_agent_tokens, -> { active.order_last_used_at_desc }, class_name: 'Clusters::AgentToken', inverse_of: :agent + + has_many :ci_access_group_authorizations, class_name: 'Clusters::Agents::Authorizations::CiAccess::GroupAuthorization' + has_many :ci_access_authorized_groups, class_name: '::Group', through: :ci_access_group_authorizations, source: :group - has_many :group_authorizations, class_name: 'Clusters::Agents::GroupAuthorization' - has_many :authorized_groups, class_name: '::Group', through: :group_authorizations, source: :group + has_many :ci_access_project_authorizations, class_name: 'Clusters::Agents::Authorizations::CiAccess::ProjectAuthorization' + has_many :ci_access_authorized_projects, class_name: '::Project', through: :ci_access_project_authorizations, source: :project - has_many :project_authorizations, class_name: 'Clusters::Agents::ProjectAuthorization' - has_many :authorized_projects, class_name: '::Project', through: :project_authorizations, source: :project + has_many :user_access_group_authorizations, class_name: 'Clusters::Agents::Authorizations::UserAccess::GroupAuthorization' + has_many :user_access_authorized_groups, class_name: '::Group', through: :user_access_group_authorizations, source: :group + + has_many :user_access_project_authorizations, class_name: 'Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization' + has_many :user_access_authorized_projects, class_name: '::Project', through: :user_access_project_authorizations, source: :project has_many :activity_events, -> { in_timeline_order }, class_name: 'Clusters::Agents::ActivityEvent', inverse_of: :agent @@ -51,6 +60,80 @@ module Clusters def to_ability_name :cluster end + + def ci_access_authorized_for?(user) + return false unless user + return false unless ::Feature.enabled?(:expose_authorized_cluster_agents, project) + + ::Project.from_union( + all_ci_access_authorized_projects_for(user).limit(1), + all_ci_access_authorized_namespaces_for(user).limit(1) + ).exists? + end + + def user_access_authorized_for?(user) + return false unless user + return false unless ::Feature.enabled?(:expose_authorized_cluster_agents, project) + + Clusters::Agents::Authorizations::UserAccess::Finder + .new(user, agent: self, preload: false, limit: 1).execute.any? + end + + # As of today, all config values of associated authorization rows have the same value. + # See `UserAccess::RefreshService` for more information. + def user_access_config + self.class.from_union( + user_access_project_authorizations.select('config').limit(1), + user_access_group_authorizations.select('config').limit(1) + ).compact.first&.config + end + + private + + def all_ci_access_authorized_projects_for(user) + ::Project.joins(:ci_access_project_authorizations) + .joins(:project_authorizations) + .where(agent_project_authorizations: { agent_id: id }) + .where(project_authorizations: { user_id: user.id, access_level: Gitlab::Access::DEVELOPER.. }) + end + + def all_ci_access_authorized_namespaces_for(user) + ::Project.with(root_namespace_cte.to_arel) + .with(all_ci_access_authorized_namespaces_cte.to_arel) + .joins('INNER JOIN all_authorized_namespaces ON all_authorized_namespaces.id = projects.namespace_id') + .joins(:project_authorizations) + .where(project_authorizations: { user_id: user.id, access_level: Gitlab::Access::DEVELOPER.. }) + end + + def root_namespace_cte + Gitlab::SQL::CTE.new(:root_namespace, root_namespace.to_sql) + end + + def all_ci_access_authorized_namespaces_cte + Gitlab::SQL::CTE.new(:all_authorized_namespaces, all_ci_access_authorized_namespaces.to_sql) + end + + def all_ci_access_authorized_namespaces + Namespace.select("traversal_ids[array_length(traversal_ids, 1)] AS id") + .joins("INNER JOIN root_namespace ON " \ + "namespaces.traversal_ids @> ARRAY[root_namespace.root_id]") + .joins("INNER JOIN agent_group_authorizations ON " \ + "namespaces.traversal_ids @> ARRAY[agent_group_authorizations.group_id::integer]") + .where(agent_group_authorizations: { agent_id: id }) + end + + def root_namespace + Namespace.select("traversal_ids[1] AS root_id") + .where("traversal_ids @> ARRAY(?)", project_namespace) + .limit(1) + end + + def project_namespace + ::Project.select('namespace_id') + .joins(:cluster_agents) + .where(cluster_agents: { id: id }) + .limit(1) + end end end diff --git a/app/models/clusters/agent_token.rb b/app/models/clusters/agent_token.rb index e2dcff13a69..b2b13f6cef7 100644 --- a/app/models/clusters/agent_token.rb +++ b/app/models/clusters/agent_token.rb @@ -20,6 +20,7 @@ module Clusters scope :order_last_used_at_desc, -> { order(arel_table[:last_used_at].desc.nulls_last) } scope :with_status, -> (status) { where(status: status) } + scope :active, -> { where(status: :active) } enum status: { active: 0, diff --git a/app/models/clusters/agents/authorizations/ci_access/group_authorization.rb b/app/models/clusters/agents/authorizations/ci_access/group_authorization.rb new file mode 100644 index 00000000000..4261fd6570f --- /dev/null +++ b/app/models/clusters/agents/authorizations/ci_access/group_authorization.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module CiAccess + class GroupAuthorization < ApplicationRecord + include ConfigScopes + + self.table_name = 'agent_group_authorizations' + + belongs_to :agent, class_name: 'Clusters::Agent', optional: false + belongs_to :group, class_name: '::Group', optional: false + + validates :config, json_schema: { filename: 'clusters_agents_authorizations_ci_access_config' } + + def config_project + agent.project + end + end + end + end + end +end diff --git a/app/models/clusters/agents/authorizations/ci_access/implicit_authorization.rb b/app/models/clusters/agents/authorizations/ci_access/implicit_authorization.rb new file mode 100644 index 00000000000..b996ae3f92b --- /dev/null +++ b/app/models/clusters/agents/authorizations/ci_access/implicit_authorization.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module CiAccess + class ImplicitAuthorization + attr_reader :agent + + delegate :id, to: :agent, prefix: true + + def initialize(agent:) + @agent = agent + end + + def config_project + agent.project + end + + def config + {} + end + end + end + end + end +end diff --git a/app/models/clusters/agents/authorizations/ci_access/project_authorization.rb b/app/models/clusters/agents/authorizations/ci_access/project_authorization.rb new file mode 100644 index 00000000000..7742d109cdb --- /dev/null +++ b/app/models/clusters/agents/authorizations/ci_access/project_authorization.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module CiAccess + class ProjectAuthorization < ApplicationRecord + include ConfigScopes + + self.table_name = 'agent_project_authorizations' + + belongs_to :agent, class_name: 'Clusters::Agent', optional: false + belongs_to :project, class_name: '::Project', optional: false + + validates :config, json_schema: { filename: 'clusters_agents_authorizations_ci_access_config' } + + def config_project + agent.project + end + end + end + end + end +end diff --git a/app/models/clusters/agents/authorizations/user_access/group_authorization.rb b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb new file mode 100644 index 00000000000..7027870855a --- /dev/null +++ b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module UserAccess + class GroupAuthorization < ApplicationRecord + include Scopes + + self.table_name = 'agent_user_access_group_authorizations' + + belongs_to :agent, class_name: 'Clusters::Agent', optional: false + belongs_to :group, class_name: '::Group', optional: false + + scope :for_user, ->(user) { + with(groups_with_direct_membership_cte(user).to_arel) + .with(all_groups_with_membership_cte.to_arel) + .joins('INNER JOIN all_groups_with_membership ON ' \ + 'all_groups_with_membership.id = agent_user_access_group_authorizations.group_id') + .select('DISTINCT ON (id) agent_user_access_group_authorizations.*, ' \ + 'all_groups_with_membership.access_level AS access_level') + .order('id, access_level DESC') + } + + scope :for_project, ->(project) { + where('all_groups_with_membership.traversal_ids @> ARRAY[?]', project.namespace_id) + } + + validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' } + + def config_project + agent.project + end + + class << self + def upsert_configs(configs) + upsert_all(configs, unique_by: [:agent_id, :group_id]) + end + + def delete_unlisted(group_ids) + where.not(group_id: group_ids).delete_all + end + + def all_groups_with_membership_cte + Gitlab::SQL::CTE.new(:all_groups_with_membership, all_groups_with_membership.to_sql) + end + + def all_groups_with_membership + ::Group.joins('INNER JOIN groups_with_direct_membership ON ' \ + 'namespaces.traversal_ids @> ARRAY[groups_with_direct_membership.id]') + .select('namespaces.id AS id, ' \ + 'namespaces.traversal_ids AS traversal_ids, ' \ + 'groups_with_direct_membership.access_level AS access_level') + end + + def groups_with_direct_membership_cte(user) + Gitlab::SQL::CTE.new(:groups_with_direct_membership, groups_with_direct_membership_for(user).to_sql) + end + + def groups_with_direct_membership_for(user) + ::Group.joins("INNER JOIN members ON " \ + "members.source_id = namespaces.id AND members.source_type = 'Namespace'") + .where(members: { user_id: user.id, access_level: Gitlab::Access::DEVELOPER.. }) + .select('namespaces.id AS id, members.access_level AS access_level') + end + end + end + end + end + end +end diff --git a/app/models/clusters/agents/authorizations/user_access/project_authorization.rb b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb new file mode 100644 index 00000000000..476666e3ad8 --- /dev/null +++ b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module UserAccess + class ProjectAuthorization < ApplicationRecord + include Scopes + + self.table_name = 'agent_user_access_project_authorizations' + + belongs_to :agent, class_name: 'Clusters::Agent', optional: false + belongs_to :project, class_name: '::Project', optional: false + + scope :for_user, ->(user) { + joins('INNER JOIN project_authorizations ON ' \ + 'project_authorizations.project_id = agent_user_access_project_authorizations.project_id') + .where(project_authorizations: { user_id: user.id, access_level: Gitlab::Access::DEVELOPER.. }) + .select('agent_user_access_project_authorizations.*, project_authorizations.access_level AS access_level') + } + + scope :for_project, ->(project) { where(project: project) } + + validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' } + + def config_project + agent.project + end + + class << self + def upsert_configs(configs) + upsert_all(configs, unique_by: [:agent_id, :project_id]) + end + + def delete_unlisted(project_ids) + where.not(project_id: project_ids).delete_all + end + end + end + end + end + end +end diff --git a/app/models/clusters/agents/group_authorization.rb b/app/models/clusters/agents/group_authorization.rb deleted file mode 100644 index 58ba874ab53..00000000000 --- a/app/models/clusters/agents/group_authorization.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Agents - class GroupAuthorization < ApplicationRecord - include ::Clusters::Agents::AuthorizationConfigScopes - - self.table_name = 'agent_group_authorizations' - - belongs_to :agent, class_name: 'Clusters::Agent', optional: false - belongs_to :group, class_name: '::Group', optional: false - - validates :config, json_schema: { filename: 'cluster_agent_authorization_configuration' } - - def config_project - agent.project - end - end - end -end diff --git a/app/models/clusters/agents/implicit_authorization.rb b/app/models/clusters/agents/implicit_authorization.rb deleted file mode 100644 index a365ccdc568..00000000000 --- a/app/models/clusters/agents/implicit_authorization.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Agents - class ImplicitAuthorization - attr_reader :agent - - delegate :id, to: :agent, prefix: true - - def initialize(agent:) - @agent = agent - end - - def config_project - agent.project - end - - def config - {} - end - end - end -end diff --git a/app/models/clusters/agents/project_authorization.rb b/app/models/clusters/agents/project_authorization.rb deleted file mode 100644 index b9b44741936..00000000000 --- a/app/models/clusters/agents/project_authorization.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Agents - class ProjectAuthorization < ApplicationRecord - include ::Clusters::Agents::AuthorizationConfigScopes - - self.table_name = 'agent_project_authorizations' - - belongs_to :agent, class_name: 'Clusters::Agent', optional: false - belongs_to :project, class_name: '::Project', optional: false - - validates :config, json_schema: { filename: 'cluster_agent_authorization_configuration' } - - def config_project - agent.project - end - end - end -end diff --git a/app/models/clusters/applications/crossplane.rb b/app/models/clusters/applications/crossplane.rb deleted file mode 100644 index a7b4fb57149..00000000000 --- a/app/models/clusters/applications/crossplane.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Applications - # DEPRECATED for removal in %14.0 - # See https://gitlab.com/groups/gitlab-org/-/epics/4280 - class Crossplane < ApplicationRecord - VERSION = '0.4.1' - - self.table_name = 'clusters_applications_crossplane' - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Clusters::Concerns::ApplicationVersion - include ::Clusters::Concerns::ApplicationData - - attribute :version, default: VERSION - attribute :stack, default: "" - - validates :stack, presence: true - - def chart - 'crossplane/crossplane' - end - - def repository - 'https://charts.crossplane.io/alpha' - end - - def install_command - helm_command_module::InstallCommand.new( - name: 'crossplane', - repository: repository, - version: VERSION, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files - ) - end - - def values - crossplane_values.to_yaml - end - - private - - def crossplane_values - { - "clusterStacks" => { - self.stack => { - "deploy" => true - } - } - } - end - end - end -end diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb deleted file mode 100644 index 9fac852ed5b..00000000000 --- a/app/models/clusters/applications/helm.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -require 'openssl' - -module Clusters - module Applications - # DEPRECATED: This model represents the Helm 2 Tiller server. - # It is being kept around to enable the cleanup of the unused Tiller server. - class Helm < ApplicationRecord - self.table_name = 'clusters_applications_helm' - - attr_encrypted :ca_key, - mode: :per_attribute_iv, - key: Settings.attr_encrypted_db_key_base_truncated, - algorithm: 'aes-256-cbc' - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Gitlab::Utils::StrongMemoize - - attribute :version, default: Gitlab::Kubernetes::Helm::V2::BaseCommand::HELM_VERSION - - before_create :create_keys_and_certs - - def issue_client_cert - ca_cert_obj.issue - end - - def set_initial_status - # The legacy Tiller server is not installable, which is the initial status of every app - end - - # DEPRECATED: This command is only for development and testing purposes, to simulate - # a Helm 2 cluster with an existing Tiller server. - def install_command - Gitlab::Kubernetes::Helm::V2::InitCommand.new( - name: name, - files: files, - rbac: cluster.platform_kubernetes_rbac? - ) - end - - def uninstall_command - Gitlab::Kubernetes::Helm::V2::ResetCommand.new( - name: name, - files: files, - rbac: cluster.platform_kubernetes_rbac? - ) - end - - def has_ssl? - ca_key.present? && ca_cert.present? - end - - private - - def files - { - 'ca.pem': ca_cert, - 'cert.pem': tiller_cert.cert_string, - 'key.pem': tiller_cert.key_string - } - end - - def create_keys_and_certs - ca_cert = Gitlab::Kubernetes::Helm::V2::Certificate.generate_root - self.ca_key = ca_cert.key_string - self.ca_cert = ca_cert.cert_string - end - - def tiller_cert - @tiller_cert ||= ca_cert_obj.issue(expires_in: Gitlab::Kubernetes::Helm::V2::Certificate::INFINITE_EXPIRY) - end - - def ca_cert_obj - return unless has_ssl? - - Gitlab::Kubernetes::Helm::V2::Certificate - .from_strings(ca_key, ca_cert) - end - end - end -end diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb deleted file mode 100644 index 034b178d67d..00000000000 --- a/app/models/clusters/applications/ingress.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Applications - # DEPRECATED for removal in %14.0 - # See https://gitlab.com/groups/gitlab-org/-/epics/4280 - class Ingress < ApplicationRecord - VERSION = '1.40.2' - INGRESS_CONTAINER_NAME = 'nginx-ingress-controller' - - self.table_name = 'clusters_applications_ingress' - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Clusters::Concerns::ApplicationVersion - include ::Clusters::Concerns::ApplicationData - include AfterCommitQueue - include UsageStatistics - - attribute :version, default: VERSION - - enum ingress_type: { - nginx: 1 - }, _default: :nginx - - FETCH_IP_ADDRESS_DELAY = 30.seconds - - state_machine :status do - after_transition any => [:installed] do |application| - application.run_after_commit do - ClusterWaitForIngressIpAddressWorker.perform_in( - FETCH_IP_ADDRESS_DELAY, application.name, application.id) - end - end - end - - def chart - "#{name}/nginx-ingress" - end - - def repository - 'https://gitlab-org.gitlab.io/cluster-integration/helm-stable-archive' - end - - def values - content_values.to_yaml - end - - def allowed_to_uninstall? - external_ip_or_hostname? && !application_jupyter_installed? - end - - def install_command - helm_command_module::InstallCommand.new( - name: name, - repository: repository, - version: VERSION, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files - ) - end - - def external_ip_or_hostname? - external_ip.present? || external_hostname.present? - end - - def schedule_status_update - return unless installed? - return if external_ip - return if external_hostname - - ClusterWaitForIngressIpAddressWorker.perform_async(name, id) - end - - def ingress_service - cluster.kubeclient.get_service("ingress-#{INGRESS_CONTAINER_NAME}", Gitlab::Kubernetes::Helm::NAMESPACE) - end - - private - - def content_values - YAML.load_file(chart_values_file) - end - - def application_jupyter_installed? - cluster.application_jupyter&.installed? - end - end - end -end diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb deleted file mode 100644 index 9c0e90d59ed..00000000000 --- a/app/models/clusters/applications/jupyter.rb +++ /dev/null @@ -1,128 +0,0 @@ -# frozen_string_literal: true - -require 'securerandom' - -module Clusters - module Applications - # DEPRECATED for removal in %14.0 - # See https://gitlab.com/groups/gitlab-org/-/epics/4280 - class Jupyter < ApplicationRecord - VERSION = '0.9.0' - - self.table_name = 'clusters_applications_jupyter' - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Clusters::Concerns::ApplicationVersion - include ::Clusters::Concerns::ApplicationData - - belongs_to :oauth_application, class_name: 'Doorkeeper::Application' - - attribute :version, default: VERSION - - def set_initial_status - return unless not_installable? - return unless cluster&.application_ingress_available? - - ingress = cluster.application_ingress - self.status = status_states[:installable] if ingress.external_ip_or_hostname? - end - - def chart - "#{name}/jupyterhub" - end - - def repository - 'https://jupyterhub.github.io/helm-chart/' - end - - def values - content_values.to_yaml - end - - def install_command - helm_command_module::InstallCommand.new( - name: name, - version: VERSION, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files, - repository: repository - ) - end - - def callback_url - "http://#{hostname}/hub/oauth_callback" - end - - def oauth_scopes - 'api read_repository write_repository' - end - - private - - def specification - { - "ingress" => { - "hosts" => [hostname], - "tls" => [{ - "hosts" => [hostname], - "secretName" => "jupyter-cert" - }] - }, - "hub" => { - "extraEnv" => { - "GITLAB_HOST" => gitlab_url - }, - "cookieSecret" => cookie_secret - }, - "proxy" => { - "secretToken" => secret_token - }, - "auth" => { - "state" => { - "cryptoKey" => crypto_key - }, - "gitlab" => { - "clientId" => oauth_application.uid, - "clientSecret" => oauth_application.secret, - "callbackUrl" => callback_url, - "gitlabProjectIdWhitelist" => cluster.projects.ids, - "gitlabGroupWhitelist" => cluster.groups.map(&:to_param) - } - }, - "singleuser" => { - "extraEnv" => { - "GITLAB_CLUSTER_ID" => cluster.id.to_s, - "GITLAB_HOST" => gitlab_host - } - } - } - end - - def crypto_key - @crypto_key ||= SecureRandom.hex(32) - end - - def gitlab_url - Gitlab.config.gitlab.url - end - - def gitlab_host - Gitlab.config.gitlab.host - end - - def content_values - YAML.load_file(chart_values_file).deep_merge!(specification) - end - - def secret_token - @secret_token ||= SecureRandom.hex(32) - end - - def cookie_secret - @cookie_secret ||= SecureRandom.hex(32) - end - end - end -end diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb deleted file mode 100644 index 64366594583..00000000000 --- a/app/models/clusters/applications/knative.rb +++ /dev/null @@ -1,156 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Applications - # DEPRECATED for removal in %14.0 - # See https://gitlab.com/groups/gitlab-org/-/epics/4280 - class Knative < ApplicationRecord - VERSION = '0.10.0' - REPOSITORY = 'https://charts.gitlab.io' - METRICS_CONFIG = 'https://gitlab.com/gitlab-org/charts/knative/-/raw/v0.9.0/vendor/istio-metrics.yml' - FETCH_IP_ADDRESS_DELAY = 30.seconds - API_GROUPS_PATH = 'config/knative/api_groups.yml' - - self.table_name = 'clusters_applications_knative' - - has_one :serverless_domain_cluster, class_name: '::Serverless::DomainCluster', foreign_key: 'clusters_applications_knative_id', inverse_of: :knative - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Clusters::Concerns::ApplicationVersion - include ::Clusters::Concerns::ApplicationData - include AfterCommitQueue - - alias_method :original_set_initial_status, :set_initial_status - def set_initial_status - return unless cluster&.platform_kubernetes_rbac? - - original_set_initial_status - end - - state_machine :status do - after_transition any => [:installed] do |application| - application.run_after_commit do - ClusterWaitForIngressIpAddressWorker.perform_in( - FETCH_IP_ADDRESS_DELAY, application.name, application.id) - end - end - - after_transition any => [:installed, :updated] do |application| - application.run_after_commit do - ClusterConfigureIstioWorker.perform_async(application.cluster_id) - end - end - end - - attribute :version, default: VERSION - - validates :hostname, presence: true, hostname: true - - scope :for_cluster, -> (cluster) { where(cluster: cluster) } - - has_one :pages_domain, through: :serverless_domain_cluster - - def chart - 'knative/knative' - end - - def values - { "domain" => hostname }.to_yaml - end - - def available_domains - PagesDomain.instance_serverless - end - - def find_available_domain(pages_domain_id) - available_domains.find_by(id: pages_domain_id) - end - - def allowed_to_uninstall? - !pre_installed? - end - - def install_command - helm_command_module::InstallCommand.new( - name: name, - version: VERSION, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files, - repository: REPOSITORY, - postinstall: install_knative_metrics - ) - end - - def schedule_status_update - return unless installed? - return if external_ip - return if external_hostname - - ClusterWaitForIngressIpAddressWorker.perform_async(name, id) - end - - def ingress_service - cluster.kubeclient.get_service('istio-ingressgateway', Clusters::Kubernetes::ISTIO_SYSTEM_NAMESPACE) - end - - def uninstall_command - helm_command_module::DeleteCommand.new( - name: name, - rbac: cluster.platform_kubernetes_rbac?, - files: files, - predelete: delete_knative_services_and_metrics, - postdelete: delete_knative_istio_leftovers - ) - end - - private - - def delete_knative_services_and_metrics - delete_knative_services + delete_knative_istio_metrics - end - - def delete_knative_services - cluster.kubernetes_namespaces.map do |kubernetes_namespace| - Gitlab::Kubernetes::KubectlCmd.delete("ksvc", "--all", "-n", kubernetes_namespace.namespace) - end - end - - def delete_knative_istio_leftovers - delete_knative_namespaces + delete_knative_and_istio_crds - end - - def delete_knative_namespaces - [ - Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "ns", "knative-serving"), - Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "ns", "knative-build") - ] - end - - def delete_knative_and_istio_crds - api_groups.map do |group| - Gitlab::Kubernetes::KubectlCmd.delete_crds_from_group(group) - end - end - - # returns an array of CRDs to be postdelete since helm does not - # manage the CRDs it creates. - def api_groups - @api_groups ||= YAML.safe_load(File.read(Rails.root.join(API_GROUPS_PATH))) - end - - def install_knative_metrics - return [] unless cluster.application_prometheus&.available? - - [Gitlab::Kubernetes::KubectlCmd.apply_file(METRICS_CONFIG)] - end - - def delete_knative_istio_metrics - return [] unless cluster.application_prometheus&.available? - - [Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "-f", METRICS_CONFIG)] - end - end - end -end diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb deleted file mode 100644 index a076c871824..00000000000 --- a/app/models/clusters/applications/prometheus.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Applications - class Prometheus < ApplicationRecord - include ::Clusters::Concerns::PrometheusClient - - VERSION = '10.4.1' - - self.table_name = 'clusters_applications_prometheus' - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Clusters::Concerns::ApplicationVersion - include ::Clusters::Concerns::ApplicationData - include AfterCommitQueue - - attribute :version, default: VERSION - - scope :preload_cluster_platform, -> { preload(cluster: [:platform_kubernetes]) } - - attr_encrypted :alert_manager_token, - mode: :per_attribute_iv, - key: Settings.attr_encrypted_db_key_base_32, - algorithm: 'aes-256-gcm' - - after_initialize :set_alert_manager_token, if: :new_record? - - after_destroy do - cluster.find_or_build_integration_prometheus.destroy - end - - state_machine :status do - after_transition any => [:installed, :externally_installed] do |application| - application.cluster.find_or_build_integration_prometheus.update(enabled: true, alert_manager_token: application.alert_manager_token) - end - - after_transition any => :updating do |application| - application.update(last_update_started_at: Time.current) - end - end - - def managed_prometheus? - !externally_installed? && !uninstalled? - end - - def updated_since?(timestamp) - last_update_started_at && - last_update_started_at > timestamp && - !update_errored? - end - - def chart - "#{name}/prometheus" - end - - def repository - 'https://gitlab-org.gitlab.io/cluster-integration/helm-stable-archive' - end - - def install_command - helm_command_module::InstallCommand.new( - name: name, - repository: repository, - version: VERSION, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files, - postinstall: install_knative_metrics - ) - end - - # Deprecated, to be removed in %14.0 as part of https://gitlab.com/groups/gitlab-org/-/epics/4280 - def patch_command(values) - helm_command_module::PatchCommand.new( - name: name, - repository: repository, - version: version, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files_with_replaced_values(values) - ) - end - - def uninstall_command - helm_command_module::DeleteCommand.new( - name: name, - rbac: cluster.platform_kubernetes_rbac?, - files: files, - predelete: delete_knative_istio_metrics - ) - end - - # Returns a copy of files where the values of 'values.yaml' - # are replaced by the argument. - # - # See #values for the data format required - def files_with_replaced_values(replaced_values) - files.merge('values.yaml': replaced_values) - end - - private - - def set_alert_manager_token - self.alert_manager_token = SecureRandom.hex - end - - def install_knative_metrics - return [] unless cluster.application_knative_available? - - [Gitlab::Kubernetes::KubectlCmd.apply_file(Clusters::Applications::Knative::METRICS_CONFIG)] - end - - def delete_knative_istio_metrics - return [] unless cluster.application_knative_available? - - [ - Gitlab::Kubernetes::KubectlCmd.delete( - "-f", Clusters::Applications::Knative::METRICS_CONFIG, - "--ignore-not-found" - ) - ] - end - end - end -end diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb deleted file mode 100644 index b8ed33828bc..00000000000 --- a/app/models/clusters/applications/runner.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -module Clusters - module Applications - class Runner < ApplicationRecord - VERSION = '0.42.1' - - self.table_name = 'clusters_applications_runners' - - include ::Clusters::Concerns::ApplicationCore - include ::Clusters::Concerns::ApplicationStatus - include ::Clusters::Concerns::ApplicationVersion - include ::Clusters::Concerns::ApplicationData - - belongs_to :runner, class_name: 'Ci::Runner', foreign_key: :runner_id - delegate :project, :group, to: :cluster - - attribute :version, default: VERSION - - def chart - "#{name}/gitlab-runner" - end - - def repository - 'https://charts.gitlab.io' - end - - def values - content_values.to_yaml - end - - def install_command - helm_command_module::InstallCommand.new( - name: name, - version: VERSION, - rbac: cluster.platform_kubernetes_rbac?, - chart: chart, - files: files, - repository: repository - ) - end - - def prepare_uninstall - # No op, see https://gitlab.com/gitlab-org/gitlab/-/issues/350180. - end - - def post_uninstall - runner.destroy! - end - - private - - def gitlab_url - Gitlab::Routing.url_helpers.root_url(only_path: false) - end - - def specification - { - "gitlabUrl" => gitlab_url, - "runners" => { "privileged" => privileged } - } - end - - def content_values - YAML.load_file(chart_values_file).deep_merge!(specification) - end - end - end -end diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index a35ea6ddb46..a2903bba6d2 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -11,18 +11,8 @@ module Clusters self.table_name = 'clusters' - APPLICATIONS = { - Clusters::Applications::Helm.application_name => Clusters::Applications::Helm, - Clusters::Applications::Ingress.application_name => Clusters::Applications::Ingress, - Clusters::Applications::Crossplane.application_name => Clusters::Applications::Crossplane, - Clusters::Applications::Prometheus.application_name => Clusters::Applications::Prometheus, - Clusters::Applications::Runner.application_name => Clusters::Applications::Runner, - Clusters::Applications::Jupyter.application_name => Clusters::Applications::Jupyter, - Clusters::Applications::Knative.application_name => Clusters::Applications::Knative - }.freeze DEFAULT_ENVIRONMENT = '*' KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN' - APPLICATIONS_ASSOCIATIONS = APPLICATIONS.values.map(&:association_name).freeze self.reactive_cache_work_type = :external_dependency @@ -54,14 +44,6 @@ module Clusters has_one application.association_name, class_name: application.to_s, inverse_of: :cluster # rubocop:disable Rails/ReflectionClassName end - has_one_cluster_application :helm - has_one_cluster_application :ingress - has_one_cluster_application :crossplane - has_one_cluster_application :prometheus - has_one_cluster_application :runner - has_one_cluster_application :jupyter - has_one_cluster_application :knative - has_many :kubernetes_namespaces has_many :metrics_dashboard_annotations, class_name: 'Metrics::Dashboard::Annotation', inverse_of: :cluster @@ -88,9 +70,6 @@ module Clusters delegate :status, to: :provider, allow_nil: true delegate :status_reason, to: :provider, allow_nil: true - delegate :external_ip, to: :application_ingress, prefix: true, allow_nil: true - delegate :external_hostname, to: :application_ingress, prefix: true, allow_nil: true - alias_attribute :base_domain, :domain alias_attribute :provided_by_user?, :user? @@ -123,7 +102,6 @@ module Clusters scope :distinct_with_deployed_environments, -> { joins(:environments).merge(::Deployment.success).distinct } scope :managed, -> { where(managed: true) } - scope :with_persisted_applications, -> { eager_load(*APPLICATIONS_ASSOCIATIONS) } scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) } scope :with_management_project, -> { where.not(management_project: nil) } @@ -232,24 +210,6 @@ module Clusters connection_data.merge(Gitlab::Kubernetes::Node.new(self).all) end - def persisted_applications - APPLICATIONS_ASSOCIATIONS.filter_map { |association_name| public_send(association_name) } # rubocop:disable GitlabSecurity/PublicSend - end - - def applications - APPLICATIONS.each_value.map do |application_class| - find_or_build_application(application_class) - end - end - - def find_or_build_application(application_class) - raise ArgumentError, "#{application_class} is not in APPLICATIONS" unless APPLICATIONS.value?(application_class) - - association_name = application_class.association_name - - public_send(association_name) || public_send("build_#{association_name}") # rubocop:disable GitlabSecurity/PublicSend - end - def find_or_build_integration_prometheus integration_prometheus || build_integration_prometheus end @@ -270,18 +230,6 @@ module Clusters !!platform_kubernetes&.rbac? end - def application_helm_available? - !!application_helm&.available? - end - - def application_ingress_available? - !!application_ingress&.available? - end - - def application_knative_available? - !!application_knative&.available? - end - def integration_prometheus_available? !!integration_prometheus&.available? end @@ -365,12 +313,6 @@ module Clusters end end - def serverless_domain - strong_memoize(:serverless_domain) do - self.application_knative&.serverless_domain_cluster - end - end - def prometheus_adapter integration_prometheus end diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb index 42332bdc193..dfb5c4cc5eb 100644 --- a/app/models/clusters/kubernetes_namespace.rb +++ b/app/models/clusters/kubernetes_namespace.rb @@ -22,9 +22,9 @@ module Clusters delegate :api_url, to: :platform_kubernetes, allow_nil: true attr_encrypted :service_account_token, - mode: :per_attribute_iv, - key: Settings.attr_encrypted_db_key_base_truncated, - algorithm: 'aes-256-cbc' + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-cbc' scope :has_service_account_token, -> { where.not(encrypted_service_account_token: nil) } scope :with_environment_name, -> (name) { joins(:environment).where(environments: { name: name }) } diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index 165285b34b2..123ad0ebfaf 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -4,7 +4,6 @@ module Clusters module Platforms class Kubernetes < ApplicationRecord include Gitlab::Kubernetes - include EnumWithNil include AfterCommitQueue include ReactiveCaching include NullifyIfBlank @@ -63,7 +62,7 @@ module Clusters alias_attribute :ca_pem, :ca_cert - enum_with_nil authorization_type: { + enum authorization_type: { unknown_authorization: nil, rbac: 1, abac: 2 |