Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/clusters/platforms/kubernetes.rb')
-rw-r--r--app/models/clusters/platforms/kubernetes.rb157
1 files changed, 120 insertions, 37 deletions
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 6dc1ee810d3..9160a169452 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -1,7 +1,12 @@
module Clusters
module Platforms
class Kubernetes < ActiveRecord::Base
+ include Gitlab::CurrentSettings
+ include Gitlab::Kubernetes
+ include ReactiveCaching
+
self.table_name = 'cluster_platforms_kubernetes'
+ self.reactive_cache_key = ->(kubernetes) { [kubernetes.class.model_name.singular, kubernetes.id] }
belongs_to :cluster, inverse_of: :platform_kubernetes, class_name: 'Clusters::Cluster'
@@ -29,19 +34,17 @@ module Clusters
validates :api_url, url: true, presence: true
validates :token, presence: true
- # TODO: Glue code till we migrate Kubernetes Integration into Platforms::Kubernetes
- after_destroy :destroy_kubernetes_integration!
+ validate :prevent_modification, on: :update
+
+ after_save :clear_reactive_cache!
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
- class << self
- def namespace_for_project(project)
- "#{project.path}-#{project.id}"
- end
- end
+ alias_method :active?, :enabled?
def actual_namespace
if namespace.present?
@@ -51,58 +54,138 @@ module Clusters
end
end
- def default_namespace
- self.class.namespace_for_project(project) if project
+ def predefined_variables
+ config = YAML.dump(kubeconfig)
+
+ variables = [
+ { key: 'KUBE_URL', value: api_url, public: true },
+ { key: 'KUBE_TOKEN', value: token, public: false },
+ { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true },
+ { key: 'KUBECONFIG', value: config, public: false, file: true }
+ ]
+
+ if ca_pem.present?
+ variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true }
+ variables << { key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true }
+ end
+
+ variables
end
- def kubeclient
- @kubeclient ||= kubernetes_service.kubeclient if manages_kubernetes_service?
+ # Constructs a list of terminals from the reactive cache
+ #
+ # Returns nil if the cache is empty, in which case you should try again a
+ # 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) }
+ terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
+ end
end
- def update_kubernetes_integration!
- raise 'Kubernetes service already configured' unless manages_kubernetes_service?
+ # 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?
- # This is neccesary, otheriwse enabled? returns true even though cluster updated with enabled: false
- cluster.reload
+ # We may want to cache extra things in the future
+ { pods: read_pods }
+ end
+
+ def kubeclient
+ @kubeclient ||= build_kubeclient!
+ end
+
+ private
- ensure_kubernetes_service&.update!(
- active: enabled?,
- api_url: api_url,
- namespace: namespace,
+ def kubeconfig
+ to_kubeconfig(
+ url: api_url,
+ namespace: actual_namespace,
token: token,
- ca_pem: ca_cert
- )
+ ca_pem: ca_pem)
end
- def active?
- manages_kubernetes_service?
+ def default_namespace
+ return unless project
+
+ slug = "#{project.path}-#{project.id}".downcase
+ slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
end
- private
+ def build_kubeclient!(api_path: 'api', api_version: 'v1')
+ raise "Incomplete settings" unless api_url && actual_namespace
- def enforce_namespace_to_lower_case
- self.namespace = self.namespace&.downcase
+ unless (username && password) || token
+ raise "Either username/password or token is required to access API"
+ end
+
+ ::Kubeclient::Client.new(
+ join_api_url(api_path),
+ api_version,
+ auth_options: kubeclient_auth_options,
+ ssl_options: kubeclient_ssl_options,
+ http_proxy_uri: ENV['http_proxy']
+ )
end
- # TODO: glue code till we migrate Kubernetes Service into Platforms::Kubernetes class
- def manages_kubernetes_service?
- return true unless kubernetes_service&.active?
+ # Returns a hash of all pods in the namespace
+ def read_pods
+ kubeclient = build_kubeclient!
+
+ kubeclient.get_pods(namespace: actual_namespace).as_json
+ rescue KubeException => err
+ raise err unless err.error_code == 404
+
+ []
+ end
+
+ def kubeclient_ssl_options
+ opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
+
+ if ca_pem.present?
+ opts[:cert_store] = OpenSSL::X509::Store.new
+ opts[:cert_store].add_cert(OpenSSL::X509::Certificate.new(ca_pem))
+ end
+
+ opts
+ end
- kubernetes_service.api_url == api_url
+ def kubeclient_auth_options
+ { bearer_token: token }
end
- def destroy_kubernetes_integration!
- return unless manages_kubernetes_service?
+ def join_api_url(api_path)
+ url = URI.parse(api_url)
+ prefix = url.path.sub(%r{/+\z}, '')
+
+ url.path = [prefix, api_path].join("/")
- kubernetes_service&.destroy!
+ url.to_s
end
- def kubernetes_service
- @kubernetes_service ||= project&.kubernetes_service
+ def terminal_auth
+ {
+ token: token,
+ ca_pem: ca_pem,
+ max_session_time: current_application_settings.terminal_max_session_time
+ }
end
- def ensure_kubernetes_service
- @kubernetes_service ||= kubernetes_service || project&.build_kubernetes_service
+ def enforce_namespace_to_lower_case
+ self.namespace = self.namespace&.downcase
+ end
+
+ def prevent_modification
+ return unless managed?
+
+ if api_url_changed? || token_changed? || ca_pem_changed?
+ errors.add(:base, "cannot modify managed cluster")
+ return false
+ end
+
+ true
end
end
end