diff options
Diffstat (limited to 'app/models/clusters')
-rw-r--r-- | app/models/clusters/applications/cert_manager.rb | 8 | ||||
-rw-r--r-- | app/models/clusters/applications/helm.rb | 9 | ||||
-rw-r--r-- | app/models/clusters/applications/ingress.rb | 10 | ||||
-rw-r--r-- | app/models/clusters/applications/jupyter.rb | 41 | ||||
-rw-r--r-- | app/models/clusters/applications/knative.rb | 61 | ||||
-rw-r--r-- | app/models/clusters/applications/prometheus.rb | 20 | ||||
-rw-r--r-- | app/models/clusters/applications/runner.rb | 28 | ||||
-rw-r--r-- | app/models/clusters/cluster.rb | 101 | ||||
-rw-r--r-- | app/models/clusters/concerns/application_core.rb | 16 | ||||
-rw-r--r-- | app/models/clusters/concerns/application_data.rb | 66 | ||||
-rw-r--r-- | app/models/clusters/concerns/application_status.rb | 13 | ||||
-rw-r--r-- | app/models/clusters/group.rb | 2 | ||||
-rw-r--r-- | app/models/clusters/instance.rb | 17 | ||||
-rw-r--r-- | app/models/clusters/kubernetes_namespace.rb | 4 | ||||
-rw-r--r-- | app/models/clusters/platforms/kubernetes.rb | 81 | ||||
-rw-r--r-- | app/models/clusters/project.rb | 3 | ||||
-rw-r--r-- | app/models/clusters/providers/gcp.rb | 2 |
17 files changed, 299 insertions, 183 deletions
diff --git a/app/models/clusters/applications/cert_manager.rb b/app/models/clusters/applications/cert_manager.rb index c758577815a..d6a7d1d2bdd 100644 --- a/app/models/clusters/applications/cert_manager.rb +++ b/app/models/clusters/applications/cert_manager.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class CertManager < ActiveRecord::Base + class CertManager < ApplicationRecord VERSION = 'v0.5.2'.freeze self.table_name = 'clusters_applications_cert_managers' @@ -24,6 +24,12 @@ module Clusters 'stable/cert-manager' end + # We will implement this in future MRs. + # Need to reverse postinstall step + def allowed_to_uninstall? + false + end + def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: 'certmanager', diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb index 423071ec024..a83d06c4b00 100644 --- a/app/models/clusters/applications/helm.rb +++ b/app/models/clusters/applications/helm.rb @@ -4,7 +4,7 @@ require 'openssl' module Clusters module Applications - class Helm < ActiveRecord::Base + class Helm < ApplicationRecord self.table_name = 'clusters_applications_helm' attr_encrypted :ca_key, @@ -29,6 +29,13 @@ module Clusters self.status = 'installable' if cluster&.platform_kubernetes_active? end + # We will implement this in future MRs. + # Basically we need to check all other applications are not installed + # first. + def allowed_to_uninstall? + false + end + def install_command Gitlab::Kubernetes::Helm::InitCommand.new( name: name, diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 7c15aaa4825..a1023f44049 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class Ingress < ActiveRecord::Base + class Ingress < ApplicationRecord VERSION = '1.1.2'.freeze self.table_name = 'clusters_applications_ingress' @@ -35,6 +35,13 @@ module Clusters 'stable/nginx-ingress' end + # We will implement this in future MRs. + # Basically we need to check all dependent applications are not installed + # first. + def allowed_to_uninstall? + false + end + def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -48,6 +55,7 @@ module Clusters def schedule_status_update return unless installed? return if external_ip + return if external_hostname ClusterWaitForIngressIpAddressWorker.perform_async(name, id) end diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb index 421a923d386..4aaa1f941e5 100644 --- a/app/models/clusters/applications/jupyter.rb +++ b/app/models/clusters/applications/jupyter.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true +require 'securerandom' + module Clusters module Applications - class Jupyter < ActiveRecord::Base - VERSION = 'v0.6'.freeze + class Jupyter < ApplicationRecord + VERSION = '0.9-174bbd5'.freeze self.table_name = 'clusters_applications_jupyter' @@ -18,8 +20,10 @@ module Clusters def set_initial_status return unless not_installable? + return unless cluster&.application_ingress_available? - if cluster&.application_ingress_available? && cluster.application_ingress.external_ip + ingress = cluster.application_ingress + if ingress.external_ip || ingress.external_hostname self.status = 'installable' end end @@ -36,6 +40,12 @@ module Clusters content_values.to_yaml end + # Will be addressed in future MRs + # We need to investigate and document what will be permanently deleted. + def allowed_to_uninstall? + false + end + def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -51,6 +61,10 @@ module Clusters "http://#{hostname}/hub/oauth_callback" end + def oauth_scopes + 'api read_repository write_repository' + end + private def specification @@ -72,24 +86,41 @@ module Clusters "secretToken" => secret_token }, "auth" => { + "state" => { + "cryptoKey" => crypto_key + }, "gitlab" => { "clientId" => oauth_application.uid, "clientSecret" => oauth_application.secret, - "callbackUrl" => callback_url + "callbackUrl" => callback_url, + "gitlabProjectIdWhitelist" => [project_id] } }, "singleuser" => { "extraEnv" => { - "GITLAB_CLUSTER_ID" => cluster.id + "GITLAB_CLUSTER_ID" => cluster.id.to_s, + "GITLAB_HOST" => gitlab_host } } } end + def crypto_key + @crypto_key ||= SecureRandom.hex(32) + end + + def project_id + cluster&.project&.id + 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 diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index 8d79b041b64..d5a3bd62e3d 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -2,8 +2,8 @@ module Clusters module Applications - class Knative < ActiveRecord::Base - VERSION = '0.2.2'.freeze + class Knative < ApplicationRecord + VERSION = '0.5.0'.freeze REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'.freeze FETCH_IP_ADDRESS_DELAY = 30.seconds @@ -15,9 +15,6 @@ module Clusters include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationData include AfterCommitQueue - include ReactiveCaching - - self.reactive_cache_key = ->(knative) { [knative.class.model_name.singular, knative.id] } def set_initial_status return unless not_installable? @@ -41,8 +38,6 @@ module Clusters scope :for_cluster, -> (cluster) { where(cluster: cluster) } - after_save :clear_reactive_cache! - def chart 'knative/knative' end @@ -51,6 +46,12 @@ module Clusters { "domain" => hostname }.to_yaml end + # Handled in a new issue: + # https://gitlab.com/gitlab-org/gitlab-ce/issues/59369 + def allowed_to_uninstall? + false + end + def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -66,59 +67,17 @@ module Clusters def schedule_status_update return unless installed? return if external_ip + return if external_hostname ClusterWaitForIngressIpAddressWorker.perform_async(name, id) end - def client - cluster.kubeclient.knative_client - end - - def services - with_reactive_cache do |data| - data[:services] - end - end - - def calculate_reactive_cache - { services: read_services, pods: read_pods } - end - def ingress_service - cluster.kubeclient.get_service('knative-ingressgateway', 'istio-system') - end - - def services_for(ns: namespace) - return [] unless services - return [] unless ns - - services.select do |service| - service.dig('metadata', 'namespace') == ns - end - end - - def service_pod_details(ns, service) - with_reactive_cache do |data| - data[:pods].select { |pod| filter_pods(pod, ns, service) } - end + cluster.kubeclient.get_service('istio-ingressgateway', 'istio-system') end private - def read_pods - cluster.kubeclient.core_client.get_pods.as_json - end - - def filter_pods(pod, namespace, service) - pod["metadata"]["namespace"] == namespace && pod["metadata"]["labels"]["serving.knative.dev/service"] == service - end - - def read_services - client.get_services.as_json - rescue Kubeclient::ResourceNotFoundError - [] - end - def install_knative_metrics ["kubectl apply -f #{METRICS_CONFIG}"] if cluster.application_prometheus_available? end diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index fa7ce363531..a6b7617b830 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class Prometheus < ActiveRecord::Base + class Prometheus < ApplicationRecord include PrometheusAdapter VERSION = '6.7.3' @@ -16,10 +16,12 @@ module Clusters default_value_for :version, VERSION + after_destroy :disable_prometheus_integration + state_machine :status do after_transition any => [:installed] do |application| application.cluster.projects.each do |project| - project.find_or_initialize_service('prometheus').update(active: true) + project.find_or_initialize_service('prometheus').update!(active: true) end end end @@ -47,6 +49,14 @@ module Clusters ) end + def uninstall_command + Gitlab::Kubernetes::Helm::DeleteCommand.new( + name: name, + rbac: cluster.platform_kubernetes_rbac?, + files: files + ) + end + def upgrade_command(values) ::Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -82,6 +92,12 @@ module Clusters private + def disable_prometheus_integration + cluster.projects.each do |project| + project.prometheus_service&.update!(active: false) + end + end + def kube_client cluster&.kubeclient&.core_client end diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index 941551dadaa..db7fd8524c2 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -2,8 +2,8 @@ module Clusters module Applications - class Runner < ActiveRecord::Base - VERSION = '0.2.0'.freeze + class Runner < ApplicationRecord + VERSION = '0.5.2'.freeze self.table_name = 'clusters_applications_runners' @@ -13,7 +13,7 @@ module Clusters include ::Clusters::Concerns::ApplicationData belongs_to :runner, class_name: 'Ci::Runner', foreign_key: :runner_id - delegate :project, to: :cluster + delegate :project, :group, to: :cluster default_value_for :version, VERSION @@ -29,6 +29,13 @@ module Clusters content_values.to_yaml end + # Need to investigate if pipelines run by this runner will stop upon the + # executor pod stopping + # I.e.run a pipeline, and uninstall runner while pipeline is running + def allowed_to_uninstall? + false + end + def install_command Gitlab::Kubernetes::Helm::InstallCommand.new( name: name, @@ -55,12 +62,19 @@ module Clusters end def runner_create_params - { + attributes = { name: 'kubernetes-cluster', - runner_type: :project_type, - tag_list: %w(kubernetes cluster), - projects: [project] + runner_type: cluster.cluster_type, + tag_list: %w[kubernetes cluster] } + + if cluster.group_type? + attributes[:groups] = [group] + elsif cluster.project_type? + attributes[:projects] = [project] + end + + attributes end def gitlab_url diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index be3e6a05e1e..e1d6b2a802b 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -1,22 +1,26 @@ # frozen_string_literal: true module Clusters - class Cluster < ActiveRecord::Base + class Cluster < ApplicationRecord include Presentable include Gitlab::Utils::StrongMemoize include FromUnion + include ReactiveCaching self.table_name = 'clusters' + self.reactive_cache_key = -> (cluster) { [cluster.class.model_name.singular, cluster.id] } + PROJECT_ONLY_APPLICATIONS = { + Applications::Jupyter.application_name => Applications::Jupyter, + Applications::Knative.application_name => Applications::Knative + }.freeze APPLICATIONS = { Applications::Helm.application_name => Applications::Helm, Applications::Ingress.application_name => Applications::Ingress, Applications::CertManager.application_name => Applications::CertManager, - Applications::Prometheus.application_name => Applications::Prometheus, Applications::Runner.application_name => Applications::Runner, - Applications::Jupyter.application_name => Applications::Jupyter, - Applications::Knative.application_name => Applications::Knative - }.freeze + Applications::Prometheus.application_name => Applications::Prometheus + }.merge(PROJECT_ONLY_APPLICATIONS).freeze DEFAULT_ENVIRONMENT = '*'.freeze KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN'.freeze @@ -43,7 +47,6 @@ module Clusters has_one :application_knative, class_name: 'Clusters::Applications::Knative' has_many :kubernetes_namespaces - has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace' accepts_nested_attributes_for :provider_gcp, update_only: true accepts_nested_attributes_for :platform_kubernetes, update_only: true @@ -56,6 +59,8 @@ module Clusters validate :no_groups, unless: :group_type? validate :no_projects, unless: :project_type? + after_save :clear_reactive_cache! + delegate :status, to: :provider, allow_nil: true delegate :status_reason, to: :provider, allow_nil: true delegate :on_creation?, to: :provider, allow_nil: true @@ -67,8 +72,10 @@ module Clusters delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true delegate :available?, to: :application_knative, prefix: true, 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? enum cluster_type: { instance_type: 1, @@ -90,6 +97,7 @@ module Clusters scope :user_provided, -> { where(provider_type: ::Clusters::Cluster.provider_types[:user]) } scope :gcp_provided, -> { where(provider_type: ::Clusters::Cluster.provider_types[:gcp]) } scope :gcp_installed, -> { gcp_provided.includes(:provider_gcp).where(cluster_providers_gcp: { status: ::Clusters::Providers::Gcp.state_machines[:status].states[:created].value }) } + scope :managed, -> { where(managed: true) } scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) } @@ -103,29 +111,35 @@ module Clusters scope :preload_knative, -> { preload( - :kubernetes_namespace, + :kubernetes_namespaces, :platform_kubernetes, :application_knative ) } def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc) + return [] if clusterable.is_a?(Instance) + hierarchy_groups = clusterable.ancestors_upto(hierarchy_order: hierarchy_order).eager_load(:clusters) hierarchy_groups = hierarchy_groups.merge(current_scope) if current_scope - hierarchy_groups.flat_map(&:clusters) + hierarchy_groups.flat_map(&:clusters) + Instance.new.clusters end def status_name - if provider - provider.status_name - else - :created + provider&.status_name || connection_status.presence || :created + end + + def connection_status + with_reactive_cache do |data| + data[:connection_status] end end - def created? - status_name == :created + def calculate_reactive_cache + return unless enabled? + + { connection_status: retrieve_connection_status } end def applications @@ -148,10 +162,6 @@ module Clusters return platform_kubernetes if kubernetes? end - def managed? - !user? - end - def all_projects if project_type? projects @@ -176,20 +186,24 @@ module Clusters end alias_method :group, :first_group + def instance + Instance.new if instance_type? + end + def kubeclient platform_kubernetes.kubeclient if kubernetes? end + def kubernetes_namespace_for(project) + find_or_initialize_kubernetes_namespace_for_project(project).namespace + end + def find_or_initialize_kubernetes_namespace_for_project(project) - if project_type? - kubernetes_namespaces.find_or_initialize_by( - project: project, - cluster_project: cluster_project - ) - else - kubernetes_namespaces.find_or_initialize_by( - project: project - ) + attributes = { project: project } + attributes[:cluster_project] = cluster_project if project_type? + + kubernetes_namespaces.find_or_initialize_by(attributes).tap do |namespace| + namespace.set_defaults end end @@ -198,7 +212,7 @@ module Clusters end def kube_ingress_domain - @kube_ingress_domain ||= domain.presence || instance_domain || legacy_auto_devops_domain + @kube_ingress_domain ||= domain.presence || instance_domain end def predefined_variables @@ -209,12 +223,43 @@ module Clusters end end + def knative_services_finder(project) + @knative_services_finder ||= KnativeServicesFinder.new(self, project) + end + private def instance_domain @instance_domain ||= Gitlab::CurrentSettings.auto_devops_domain end + def retrieve_connection_status + kubeclient.core_client.discover + rescue *Gitlab::Kubernetes::Errors::CONNECTION + :unreachable + rescue *Gitlab::Kubernetes::Errors::AUTHENTICATION + :authentication_failure + rescue Kubeclient::HttpError => e + kubeclient_error_status(e.message) + rescue => e + Gitlab::Sentry.track_acceptable_exception(e, extra: { cluster_id: id }) + + :unknown_failure + else + :connected + end + + # KubeClient uses the same error class + # For connection errors (eg. timeout) and + # for Kubernetes errors. + def kubeclient_error_status(message) + if message&.match?(/timed out|timeout/i) + :unreachable + else + :authentication_failure + end + end + # To keep backward compatibility with AUTO_DEVOPS_DOMAIN # environment variable, we need to ensure KUBE_INGRESS_BASE_DOMAIN # is set if AUTO_DEVOPS_DOMAIN is set on any of the following options: diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb index 683b45331f6..4514498b84b 100644 --- a/app/models/clusters/concerns/application_core.rb +++ b/app/models/clusters/concerns/application_core.rb @@ -18,6 +18,16 @@ module Clusters self.status = 'installable' if cluster&.application_helm_available? end + def can_uninstall? + allowed_to_uninstall? + end + + # All new applications should uninstall by default + # Override if there's dependencies that needs to be uninstalled first + def allowed_to_uninstall? + true + end + def self.application_name self.to_s.demodulize.underscore end @@ -30,6 +40,12 @@ module Clusters # Override if you need extra data synchronized # from K8s after installation end + + def update_command + install_command.tap do |command| + command.version = version + end + end end end end diff --git a/app/models/clusters/concerns/application_data.rb b/app/models/clusters/concerns/application_data.rb index 52498f123ff..3479fea415e 100644 --- a/app/models/clusters/concerns/application_data.rb +++ b/app/models/clusters/concerns/application_data.rb @@ -3,48 +3,52 @@ module Clusters module Concerns module ApplicationData - extend ActiveSupport::Concern + def uninstall_command + Gitlab::Kubernetes::Helm::DeleteCommand.new( + name: name, + rbac: cluster.platform_kubernetes_rbac?, + files: files + ) + end - included do - def repository - nil - end + def repository + nil + end - def values - File.read(chart_values_file) - end + def values + File.read(chart_values_file) + end - def files - @files ||= begin - files = { 'values.yaml': values } + def files + @files ||= begin + files = { 'values.yaml': values } - files.merge!(certificate_files) if cluster.application_helm.has_ssl? + files.merge!(certificate_files) if cluster.application_helm.has_ssl? - files - end + files end + end - private + private - def certificate_files - { - 'ca.pem': ca_cert, - 'cert.pem': helm_cert.cert_string, - 'key.pem': helm_cert.key_string - } - end + def certificate_files + { + 'ca.pem': ca_cert, + 'cert.pem': helm_cert.cert_string, + 'key.pem': helm_cert.key_string + } + end - def ca_cert - cluster.application_helm.ca_cert - end + def ca_cert + cluster.application_helm.ca_cert + end - def helm_cert - @helm_cert ||= cluster.application_helm.issue_client_cert - end + def helm_cert + @helm_cert ||= cluster.application_helm.issue_client_cert + end - def chart_values_file - "#{Rails.root}/vendor/#{name}/values.yaml" - end + def chart_values_file + "#{Rails.root}/vendor/#{name}/values.yaml" end end end diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb index 1273ed83abe..54a3dda6d75 100644 --- a/app/models/clusters/concerns/application_status.rb +++ b/app/models/clusters/concerns/application_status.rb @@ -25,9 +25,11 @@ module Clusters state :updating, value: 4 state :updated, value: 5 state :update_errored, value: 6 + state :uninstalling, value: 7 + state :uninstall_errored, value: 8 event :make_scheduled do - transition [:installable, :errored, :installed, :updated, :update_errored] => :scheduled + transition [:installable, :errored, :installed, :updated, :update_errored, :uninstall_errored] => :scheduled end event :make_installing do @@ -40,8 +42,9 @@ module Clusters end event :make_errored do - transition any - [:updating] => :errored + transition any - [:updating, :uninstalling] => :errored transition [:updating] => :update_errored + transition [:uninstalling] => :uninstall_errored end event :make_updating do @@ -52,6 +55,10 @@ module Clusters transition any => :update_errored end + event :make_uninstalling do + transition [:scheduled] => :uninstalling + end + before_transition any => [:scheduled] do |app_status, _| app_status.status_reason = nil end @@ -65,7 +72,7 @@ module Clusters app_status.status_reason = nil end - before_transition any => [:update_errored] do |app_status, transition| + before_transition any => [:update_errored, :uninstall_errored] do |app_status, transition| status_reason = transition.args.first app_status.status_reason = status_reason if status_reason end diff --git a/app/models/clusters/group.rb b/app/models/clusters/group.rb index 2b08a9e47f0..27f39b53579 100644 --- a/app/models/clusters/group.rb +++ b/app/models/clusters/group.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Clusters - class Group < ActiveRecord::Base + class Group < ApplicationRecord self.table_name = 'cluster_groups' belongs_to :cluster, class_name: 'Clusters::Cluster' diff --git a/app/models/clusters/instance.rb b/app/models/clusters/instance.rb new file mode 100644 index 00000000000..d8a888d53ba --- /dev/null +++ b/app/models/clusters/instance.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Clusters + class Instance + def clusters + Clusters::Cluster.instance_type + end + + def feature_available?(feature) + ::Feature.enabled?(feature, default_enabled: true) + end + + def self.enabled? + ::Feature.enabled?(:instance_clusters, default_enabled: true) + end + end +end diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb index 73da6cb37d7..b0c4900546e 100644 --- a/app/models/clusters/kubernetes_namespace.rb +++ b/app/models/clusters/kubernetes_namespace.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Clusters - class KubernetesNamespace < ActiveRecord::Base + class KubernetesNamespace < ApplicationRecord include Gitlab::Kubernetes self.table_name = 'clusters_kubernetes_namespaces' @@ -37,7 +37,7 @@ module Clusters variables .append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name.to_s) .append(key: 'KUBE_NAMESPACE', value: namespace.to_s) - .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false) + .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false, masked: true) .append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) end end diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index 46d0898014e..9b951578aee 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -2,7 +2,7 @@ module Clusters module Platforms - class Kubernetes < ActiveRecord::Base + class Kubernetes < ApplicationRecord include Gitlab::Kubernetes include ReactiveCaching include EnumWithNil @@ -41,7 +41,7 @@ module Clusters validate :no_namespace, unless: :allow_user_defined_namespace? # We expect to be `active?` only when enabled and cluster is created (the api_url is assigned) - validates :api_url, url: true, presence: true + validates :api_url, public_url: true, presence: true validates :token, presence: true validates :ca_cert, certificate: true, allow_blank: true, if: :ca_cert_changed? @@ -52,11 +52,14 @@ module Clusters alias_attribute :ca_pem, :ca_cert - delegate :project, to: :cluster, allow_nil: true delegate :enabled?, to: :cluster, allow_nil: true - delegate :managed?, to: :cluster, allow_nil: true + delegate :provided_by_user?, to: :cluster, allow_nil: true delegate :allow_user_defined_namespace?, to: :cluster, allow_nil: true - delegate :kubernetes_namespace, to: :cluster + + # This is just to maintain compatibility with KubernetesService, which + # will be removed in https://gitlab.com/gitlab-org/gitlab-ce/issues/39217. + # It can be removed once KubernetesService is gone. + delegate :kubernetes_namespace_for, to: :cluster, allow_nil: true alias_method :active?, :enabled? @@ -68,14 +71,6 @@ module Clusters default_value_for :authorization_type, :rbac - def actual_namespace - if namespace.present? - namespace - else - default_namespace - end - end - def predefined_variables(project:) Gitlab::Ci::Variables::Collection.new.tap do |variables| variables.append(key: 'KUBE_URL', value: api_url) @@ -88,16 +83,19 @@ module Clusters if kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project) variables.concat(kubernetes_namespace.predefined_variables) - elsif cluster.project_type? - # From 11.5, every Clusters::Project should have at least one - # Clusters::KubernetesNamespace, so once migration has been completed, - # this 'else' branch will be removed. For more information, please see - # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22433 + elsif cluster.project_type? || !cluster.managed? + # As of 11.11 a user can create a cluster that they manage themselves, + # which replicates the existing project-level cluster behaviour. + # Once we have marked all project-level clusters that make use of this + # behaviour as "unmanaged", we can remove the `cluster.project_type?` + # check here. + project_namespace = cluster.kubernetes_namespace_for(project) + variables .append(key: 'KUBE_URL', value: api_url) - .append(key: 'KUBE_TOKEN', value: token, public: false) - .append(key: 'KUBE_NAMESPACE', value: actual_namespace) - .append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) + .append(key: 'KUBE_TOKEN', value: token, public: false, masked: true) + .append(key: 'KUBE_NAMESPACE', value: project_namespace) + .append(key: 'KUBECONFIG', value: kubeconfig(project_namespace), public: false, file: true) end variables.concat(cluster.predefined_variables) @@ -110,8 +108,10 @@ module Clusters # short time later def terminals(environment) with_reactive_cache do |data| - pods = filter_by_label(data[:pods], app: environment.slug) - terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact + project = environment.project + + pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug) + terminals = pods.flat_map { |pod| terminals_for_pod(api_url, cluster.kubernetes_namespace_for(project), pod) }.compact terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) } end end @@ -119,7 +119,7 @@ module Clusters # Caches resources in the namespace so other calls don't need to block on # network access def calculate_reactive_cache - return unless enabled? && project && !project.pending_delete? + return unless enabled? # We may want to cache extra things in the future { pods: read_pods } @@ -131,33 +131,16 @@ module Clusters private - def kubeconfig + def kubeconfig(namespace) to_kubeconfig( url: api_url, - namespace: actual_namespace, + namespace: namespace, token: token, ca_pem: ca_pem) end - def default_namespace - kubernetes_namespace&.namespace.presence || fallback_default_namespace - end - - # DEPRECATED - # - # On 11.4 Clusters::KubernetesNamespace was introduced, this model will allow to - # have multiple namespaces per project. This method will be removed after migration - # has been completed. - def fallback_default_namespace - return unless project - - slug = "#{project.path}-#{project.id}".downcase - Gitlab::NamespaceSanitizer.sanitize(slug) - end - def build_kube_client! raise "Incomplete settings" unless api_url - raise "No namespace" if cluster.project_type? && actual_namespace.empty? # can probably remove this line once we remove #actual_namespace unless (username && password) || token raise "Either username/password or token is required to access API" @@ -173,9 +156,13 @@ module Clusters # Returns a hash of all pods in the namespace def read_pods - kubeclient = build_kube_client! + # TODO: The project lookup here should be moved (to environment?), + # which will enable reading pods from the correct namespace for group + # and instance clusters. + # This will be done in https://gitlab.com/gitlab-org/gitlab-ce/issues/61156 + return [] unless cluster.project_type? - kubeclient.get_pods(namespace: actual_namespace).as_json + kubeclient.get_pods(namespace: cluster.kubernetes_namespace_for(cluster.first_project)).as_json rescue Kubeclient::ResourceNotFoundError [] end @@ -219,7 +206,7 @@ module Clusters end def prevent_modification - return unless managed? + return if provided_by_user? if api_url_changed? || token_changed? || ca_pem_changed? errors.add(:base, _('Cannot modify managed Kubernetes cluster')) @@ -230,7 +217,7 @@ module Clusters end def update_kubernetes_namespace - return unless namespace_changed? + return unless saved_change_to_namespace? run_after_commit do ClusterConfigureWorker.perform_async(cluster_id) diff --git a/app/models/clusters/project.rb b/app/models/clusters/project.rb index 15092b1c9d2..e0bf60164ba 100644 --- a/app/models/clusters/project.rb +++ b/app/models/clusters/project.rb @@ -1,13 +1,12 @@ # frozen_string_literal: true module Clusters - class Project < ActiveRecord::Base + class Project < ApplicationRecord self.table_name = 'cluster_projects' belongs_to :cluster, class_name: 'Clusters::Cluster' belongs_to :project, class_name: '::Project' has_many :kubernetes_namespaces, class_name: 'Clusters::KubernetesNamespace', foreign_key: :cluster_project_id - has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace', foreign_key: :cluster_project_id end end diff --git a/app/models/clusters/providers/gcp.rb b/app/models/clusters/providers/gcp.rb index 16b59cd9d14..390748bf252 100644 --- a/app/models/clusters/providers/gcp.rb +++ b/app/models/clusters/providers/gcp.rb @@ -2,7 +2,7 @@ module Clusters module Providers - class Gcp < ActiveRecord::Base + class Gcp < ApplicationRecord self.table_name = 'cluster_providers_gcp' belongs_to :cluster, inverse_of: :provider_gcp, class_name: 'Clusters::Cluster' |