summaryrefslogtreecommitdiff
path: root/app/models/clusters
diff options
context:
space:
mode:
authorAlex Groleau <agroleau@gitlab.com>2019-08-27 12:41:39 -0400
committerAlex Groleau <agroleau@gitlab.com>2019-08-27 12:41:39 -0400
commitaa01f092829facd1044ad02f334422b7dbdc8b0e (patch)
treea754bf2497820432df7da0f2108bb7527a8dd7b8 /app/models/clusters
parenta1d9c9994a9a4d79b824c3fd9322688303ac8b03 (diff)
parent6b10779053ff4233c7a64c5ab57754fce63f6710 (diff)
downloadgitlab-ce-aa01f092829facd1044ad02f334422b7dbdc8b0e.tar.gz
Merge branch 'master' of gitlab_gitlab:gitlab-org/gitlab-cerunner-metrics-extractor
Diffstat (limited to 'app/models/clusters')
-rw-r--r--app/models/clusters/applications/cert_manager.rb42
-rw-r--r--app/models/clusters/applications/helm.rb28
-rw-r--r--app/models/clusters/applications/knative.rb60
-rw-r--r--app/models/clusters/applications/prometheus.rb21
-rw-r--r--app/models/clusters/applications/runner.rb17
-rw-r--r--app/models/clusters/cluster.rb68
-rw-r--r--app/models/clusters/clusters_hierarchy.rb4
-rw-r--r--app/models/clusters/concerns/application_core.rb10
-rw-r--r--app/models/clusters/concerns/application_status.rb24
-rw-r--r--app/models/clusters/kubernetes_namespace.rb31
-rw-r--r--app/models/clusters/platforms/kubernetes.rb32
11 files changed, 199 insertions, 138 deletions
diff --git a/app/models/clusters/applications/cert_manager.rb b/app/models/clusters/applications/cert_manager.rb
index d6a7d1d2bdd..6bd7473c8ff 100644
--- a/app/models/clusters/applications/cert_manager.rb
+++ b/app/models/clusters/applications/cert_manager.rb
@@ -24,12 +24,6 @@ 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',
@@ -41,10 +35,44 @@ module Clusters
)
end
+ def uninstall_command
+ Gitlab::Kubernetes::Helm::DeleteCommand.new(
+ name: 'certmanager',
+ rbac: cluster.platform_kubernetes_rbac?,
+ files: files,
+ postdelete: post_delete_script
+ )
+ end
+
private
def post_install_script
- ["/usr/bin/kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml"]
+ ["kubectl create -f /data/helm/certmanager/config/cluster_issuer.yaml"]
+ end
+
+ def post_delete_script
+ [
+ delete_private_key,
+ delete_crd('certificates.certmanager.k8s.io'),
+ delete_crd('clusterissuers.certmanager.k8s.io'),
+ delete_crd('issuers.certmanager.k8s.io')
+ ].compact
+ end
+
+ def private_key_name
+ @private_key_name ||= cluster_issuer_content.dig('spec', 'acme', 'privateKeySecretRef', 'name')
+ end
+
+ def delete_private_key
+ return unless private_key_name.present?
+
+ args = %W(secret -n #{Gitlab::Kubernetes::Helm::NAMESPACE} #{private_key_name} --ignore-not-found)
+
+ Gitlab::Kubernetes::KubectlCmd.delete(*args)
+ end
+
+ def delete_crd(definition)
+ Gitlab::Kubernetes::KubectlCmd.delete("crd", definition, "--ignore-not-found")
end
def cluster_issuer_file
diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb
index a83d06c4b00..455cf200fbc 100644
--- a/app/models/clusters/applications/helm.rb
+++ b/app/models/clusters/applications/helm.rb
@@ -14,6 +14,7 @@ module Clusters
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
+ include ::Gitlab::Utils::StrongMemoize
default_value_for :version, Gitlab::Kubernetes::Helm::HELM_VERSION
@@ -29,11 +30,22 @@ 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.
+ # It can only be uninstalled if there are no other applications installed
+ # or with intermitent installation statuses in the database.
def allowed_to_uninstall?
- false
+ strong_memoize(:allowed_to_uninstall) do
+ applications = nil
+
+ Clusters::Cluster::APPLICATIONS.each do |application_name, klass|
+ next if application_name == 'helm'
+
+ extra_apps = Clusters::Applications::Helm.where('EXISTS (?)', klass.select(1).where(cluster_id: cluster_id))
+
+ applications = applications ? applications.or(extra_apps) : extra_apps
+ end
+
+ !applications.exists?
+ end
end
def install_command
@@ -44,6 +56,14 @@ module Clusters
)
end
+ def uninstall_command
+ Gitlab::Kubernetes::Helm::ResetCommand.new(
+ name: name,
+ files: files,
+ rbac: cluster.platform_kubernetes_rbac?
+ )
+ end
+
def has_ssl?
ca_key.present? && ca_cert.present?
end
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index 5df4812bd25..244fe738396 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -7,6 +7,7 @@ module Clusters
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
+ API_RESOURCES_PATH = 'config/knative/api_resources.yml'
self.table_name = 'clusters_applications_knative'
@@ -46,12 +47,6 @@ 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,
@@ -76,10 +71,61 @@ module Clusters
cluster.kubeclient.get_service('istio-ingressgateway', 'istio-system')
end
+ def uninstall_command
+ Gitlab::Kubernetes::Helm::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_resources.map do |crd|
+ Gitlab::Kubernetes::KubectlCmd.delete("--ignore-not-found", "crd", "#{crd}")
+ end
+ end
+
+ # returns an array of CRDs to be postdelete since helm does not
+ # manage the CRDs it creates.
+ def api_resources
+ @api_resources ||= YAML.safe_load(File.read(Rails.root.join(API_RESOURCES_PATH)))
+ end
+
def install_knative_metrics
- ["kubectl apply -f #{METRICS_CONFIG}"] if cluster.application_prometheus_available?
+ 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
def verify_cluster?
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 805c8a73f8c..f31a6b8b50e 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -59,6 +59,15 @@ module Clusters
)
end
+ def uninstall_command
+ Gitlab::Kubernetes::Helm::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.
#
@@ -74,7 +83,7 @@ module Clusters
# ensures headers containing auth data are appended to original k8s client options
options = kube_client.rest_client.options.merge(headers: kube_client.headers)
- RestClient::Resource.new(proxy_url, options)
+ Gitlab::PrometheusClient.new(proxy_url, options)
rescue Kubeclient::HttpError
# If users have mistakenly set parameters or removed the depended clusters,
# `proxy_url` could raise an exception because gitlab can not communicate with the cluster.
@@ -95,7 +104,15 @@ module Clusters
end
def install_knative_metrics
- ["kubectl apply -f #{Clusters::Applications::Knative::METRICS_CONFIG}"] if cluster.application_knative_available?
+ 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)]
end
end
end
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index f0256ff4d41..6533b7a186e 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
- VERSION = '0.6.0'.freeze
+ VERSION = '0.7.0'.freeze
self.table_name = 'clusters_applications_runners'
@@ -29,13 +29,6 @@ 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,
@@ -47,6 +40,14 @@ module Clusters
)
end
+ def prepare_uninstall
+ runner&.update!(active: false)
+ end
+
+ def post_uninstall
+ runner.destroy!
+ end
+
private
def ensure_runner
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 8c044c86c47..97d39491b73 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -53,6 +53,7 @@ module Clusters
validates :name, cluster_name: true
validates :cluster_type, presence: true
validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
+ validates :namespace_per_environment, inclusion: { in: [true, false] }
validate :restrict_modification, on: :update
validate :no_groups, unless: :group_type?
@@ -100,22 +101,6 @@ module Clusters
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
- scope :missing_kubernetes_namespace, -> (kubernetes_namespaces) do
- subquery = kubernetes_namespaces.select('1').where('clusters_kubernetes_namespaces.cluster_id = clusters.id')
-
- where('NOT EXISTS (?)', subquery)
- end
-
- scope :with_knative_installed, -> { joins(:application_knative).merge(Clusters::Applications::Knative.available) }
-
- scope :preload_knative, -> {
- preload(
- :kubernetes_namespaces,
- :platform_kubernetes,
- :application_knative
- )
- }
-
def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc)
return [] if clusterable.is_a?(Instance)
@@ -161,16 +146,6 @@ module Clusters
return platform_kubernetes if kubernetes?
end
- def all_projects
- if project_type?
- projects
- elsif group_type?
- first_group.all_projects
- else
- Project.none
- end
- end
-
def first_project
strong_memoize(:first_project) do
projects.first
@@ -193,36 +168,15 @@ module Clusters
platform_kubernetes.kubeclient if kubernetes?
end
- ##
- # This is subtly different to #find_or_initialize_kubernetes_namespace_for_project
- # below because it will ignore any namespaces that have not got a service account
- # token. This provides a guarantee that any namespace selected here can be used
- # for cluster operations - a namespace needs to have a service account configured
- # before it it can be used.
- #
- # This is used for selecting a namespace to use when querying a cluster, or
- # generating variables to pass to CI.
- def kubernetes_namespace_for(project)
- find_or_initialize_kubernetes_namespace_for_project(
- project, scope: kubernetes_namespaces.has_service_account_token
- ).namespace
- end
+ def kubernetes_namespace_for(environment)
+ project = environment.project
+ persisted_namespace = Clusters::KubernetesNamespaceFinder.new(
+ self,
+ project: project,
+ environment_slug: environment.slug
+ ).execute
- ##
- # This is subtly different to #kubernetes_namespace_for because it will include
- # namespaces that have yet to receive a service account token. This allows
- # the namespace configuration process to be repeatable - if a namespace has
- # already been created without a token we don't need to create another
- # record entirely, just set the token on the pre-existing namespace.
- #
- # This is used for configuring cluster namespaces.
- def find_or_initialize_kubernetes_namespace_for_project(project, scope: kubernetes_namespaces)
- attributes = { project: project }
- attributes[:cluster_project] = cluster_project if project_type?
-
- scope.find_or_initialize_by(attributes).tap do |namespace|
- namespace.set_defaults
- end
+ persisted_namespace&.namespace || Gitlab::Kubernetes::DefaultNamespace.new(self, project: project).from_environment_slug(environment.slug)
end
def allow_user_defined_namespace?
@@ -241,10 +195,6 @@ module Clusters
end
end
- def knative_services_finder(project)
- @knative_services_finder ||= KnativeServicesFinder.new(self, project)
- end
-
private
def instance_domain
diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb
index dab034b7234..5556fc8d3f0 100644
--- a/app/models/clusters/clusters_hierarchy.rb
+++ b/app/models/clusters/clusters_hierarchy.rb
@@ -46,7 +46,7 @@ module Clusters
def group_clusters_base_query
group_parent_id_alias = alias_as_column(groups[:parent_id], 'group_parent_id')
- join_sources = ::Group.left_joins(:clusters).join_sources
+ join_sources = ::Group.left_joins(:clusters).arel.join_sources
model
.unscoped
@@ -59,7 +59,7 @@ module Clusters
def project_clusters_base_query
projects = ::Project.arel_table
project_parent_id_alias = alias_as_column(projects[:namespace_id], 'group_parent_id')
- join_sources = ::Project.left_joins(:clusters).join_sources
+ join_sources = ::Project.left_joins(:clusters).arel.join_sources
model
.unscoped
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index 4514498b84b..803a65726d3 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -46,6 +46,16 @@ module Clusters
command.version = version
end
end
+
+ def prepare_uninstall
+ # Override if your application needs any action before
+ # being uninstalled by Helm
+ end
+
+ def post_uninstall
+ # Override if your application needs any action after
+ # being uninstalled by Helm
+ end
end
end
end
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index 54a3dda6d75..342d766f723 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -59,29 +59,33 @@ module Clusters
transition [:scheduled] => :uninstalling
end
- before_transition any => [:scheduled] do |app_status, _|
- app_status.status_reason = nil
+ before_transition any => [:scheduled] do |application, _|
+ application.status_reason = nil
end
- before_transition any => [:errored] do |app_status, transition|
+ before_transition any => [:errored] do |application, transition|
status_reason = transition.args.first
- app_status.status_reason = status_reason if status_reason
+ application.status_reason = status_reason if status_reason
end
- before_transition any => [:updating] do |app_status, _|
- app_status.status_reason = nil
+ before_transition any => [:updating] do |application, _|
+ application.status_reason = nil
end
- before_transition any => [:update_errored, :uninstall_errored] do |app_status, transition|
+ before_transition any => [:update_errored, :uninstall_errored] do |application, transition|
status_reason = transition.args.first
- app_status.status_reason = status_reason if status_reason
+ application.status_reason = status_reason if status_reason
end
- before_transition any => [:installed, :updated] do |app_status, _|
+ before_transition any => [:installed, :updated] do |application, _|
# When installing any application we are also performing an update
# of tiller (see Gitlab::Kubernetes::Helm::ClientCommand) so
# therefore we need to reflect that in the database.
- app_status.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION)
+ application.cluster.application_helm.update!(version: Gitlab::Kubernetes::Helm::HELM_VERSION)
+ end
+
+ after_transition any => [:uninstalling], :use_transactions => false do |application, _|
+ application.prepare_uninstall
end
end
end
diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb
index b0c4900546e..69a2b99fcb6 100644
--- a/app/models/clusters/kubernetes_namespace.rb
+++ b/app/models/clusters/kubernetes_namespace.rb
@@ -9,12 +9,12 @@ module Clusters
belongs_to :cluster_project, class_name: 'Clusters::Project'
belongs_to :cluster, class_name: 'Clusters::Cluster'
belongs_to :project, class_name: '::Project'
+ belongs_to :environment, optional: true
has_one :platform_kubernetes, through: :cluster
- before_validation :set_defaults
-
validates :namespace, presence: true
validates :namespace, uniqueness: { scope: :cluster_id }
+ validates :environment_id, uniqueness: { scope: [:cluster_id, :project_id] }, allow_nil: true
validates :service_account_name, presence: true
@@ -27,6 +27,7 @@ module Clusters
algorithm: 'aes-256-cbc'
scope :has_service_account_token, -> { where.not(encrypted_service_account_token: nil) }
+ scope :with_environment_slug, -> (slug) { joins(:environment).where(environments: { slug: slug }) }
def token_name
"#{namespace}-token"
@@ -42,34 +43,8 @@ module Clusters
end
end
- def set_defaults
- self.namespace ||= default_platform_kubernetes_namespace
- self.namespace ||= default_project_namespace
- self.service_account_name ||= default_service_account_name
- end
-
private
- def default_service_account_name
- return unless namespace
-
- "#{namespace}-service-account"
- end
-
- def default_platform_kubernetes_namespace
- platform_kubernetes&.namespace.presence
- end
-
- def default_project_namespace
- Gitlab::NamespaceSanitizer.sanitize(project_slug) if project_slug
- end
-
- def project_slug
- return unless project
-
- "#{project.path}-#{project.id}".downcase
- end
-
def kubeconfig
to_kubeconfig(
url: api_url,
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 9296c28776b..37614fbe3ca 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -51,11 +51,6 @@ module Clusters
delegate :provided_by_user?, to: :cluster, allow_nil: true
delegate :allow_user_defined_namespace?, to: :cluster, allow_nil: true
- # 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?
enum_with_nil authorization_type: {
@@ -66,7 +61,7 @@ module Clusters
default_value_for :authorization_type, :rbac
- def predefined_variables(project:)
+ def predefined_variables(project:, environment_name:)
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'KUBE_URL', value: api_url)
@@ -77,15 +72,14 @@ module Clusters
end
if !cluster.managed?
- project_namespace = namespace.presence || "#{project.path}-#{project.id}".downcase
+ namespace = Gitlab::Kubernetes::DefaultNamespace.new(cluster, project: project).from_environment_name(environment_name)
variables
- .append(key: 'KUBE_URL', value: api_url)
.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)
+ .append(key: 'KUBE_NAMESPACE', value: namespace)
+ .append(key: 'KUBECONFIG', value: kubeconfig(namespace), public: false, file: true)
- elsif kubernetes_namespace = cluster.kubernetes_namespaces.has_service_account_token.find_by(project: project)
+ elsif kubernetes_namespace = find_persisted_namespace(project, environment_name: environment_name)
variables.concat(kubernetes_namespace.predefined_variables)
end
@@ -111,6 +105,22 @@ module Clusters
private
+ ##
+ # Environment slug can be predicted given an environment
+ # name, so even if the environment isn't persisted yet we
+ # still know what to look for.
+ def environment_slug(name)
+ Gitlab::Slug::Environment.new(name).generate
+ end
+
+ def find_persisted_namespace(project, environment_name:)
+ Clusters::KubernetesNamespaceFinder.new(
+ cluster,
+ project: project,
+ environment_slug: environment_slug(environment_name)
+ ).execute
+ end
+
def kubeconfig(namespace)
to_kubeconfig(
url: api_url,