From ccf09824f6d3ef41db4be3b40aa99b6dfd0dc9ac Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 1 Nov 2017 12:57:05 +0100 Subject: Slim down Platforms::Kubernetes, and instead make it instrument KubernetesService --- app/models/clusters/cluster.rb | 7 +- app/models/clusters/platforms/kubernetes.rb | 141 +++++++--------------------- app/services/clusters/create_service.rb | 19 +--- db/schema.rb | 4 +- 4 files changed, 43 insertions(+), 128 deletions(-) diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 177403dcf00..a3f6d20ba43 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -9,8 +9,11 @@ module Clusters has_many :cluster_projects, class_name: 'Clusters::Project' has_many :projects, through: :cluster_projects, class_name: '::Project' - has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp' - has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes' + # we force autosave to happen when we save `Cluster` model + has_one :provider_gcp, class_name: 'Clusters::Providers::Gcp', autosave: true + + # We have to ":destroy" it today to ensure that we clean also the Kubernetes Integration + has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent accepts_nested_attributes_for :provider_gcp, update_only: true accepts_nested_attributes_for :platform_kubernetes, update_only: true diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index 52022509d49..4c3e270892e 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -2,11 +2,8 @@ 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.cluster_id] } belongs_to :cluster, inverse_of: :platform_kubernetes, class_name: 'Clusters::Cluster' @@ -30,17 +27,24 @@ module Clusters message: Gitlab::Regex.kubernetes_namespace_regex_message } - validates :api_url, url: true, presence: true - validates :token, presence: true + # We expect to be `active?` only when enabled and cluster is created (the api_url is assigned) + with_options presence: true, if: :active? do + validates :api_url, url: true, presence: true + validates :token, presence: true + end - after_save :clear_reactive_cache! + # TODO: Glue code till we migrate Kubernetes Integration into Platforms::Kubernetes + after_save :update_kubernetes_integration! + after_destroy :destroy_kubernetes_integration! alias_attribute :ca_pem, :ca_cert delegate :project, to: :cluster, allow_nil: true delegate :enabled?, to: :cluster, allow_nil: true - alias_method :active?, :enabled? + def active? + enabled? && api_url.present? + end class << self def namespace_for_project(project) @@ -60,122 +64,43 @@ module Clusters self.class.namespace_for_project(project) if project end - 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 - - # 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 - - # Caches resources in the namespace so other calls don't need to block on - # network access - def calculate_reactive_cache - return unless active? && project && !project.pending_delete? - - # We may want to cache extra things in the future - { pods: read_pods } - end - - def kubeconfig - to_kubeconfig( - url: api_url, - namespace: actual_namespace, - token: token, - ca_pem: ca_pem) - end - - def read_secrets - kubeclient = build_kubeclient! + private - kubeclient.get_secrets.as_json + def enforce_namespace_to_lower_case + self.namespace = self.namespace&.downcase end - # Returns a hash of all pods in the namespace - def read_pods - kubeclient = build_kubeclient! + # TODO: glue code till we migrate Kubernetes Service into Platforms::Kubernetes class + def manages_kubernetes_service? + return true unless kubernetes_service&.active? - kubeclient.get_pods(namespace: actual_namespace).as_json - rescue KubeException => err - raise err unless err.error_code == 404 - [] + kubernetes_service.api_url == api_url 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 + def destroy_kubernetes_integration! + return unless manages_kubernetes_service? - opts + kubernetes_service.destroy! end - private - - def build_kubeclient!(api_path: 'api', api_version: 'v1') - raise "Incomplete settings" unless api_url && actual_namespace + def update_kubernetes_integration! + return raise 'Kubernetes service already configured' unless manages_kubernetes_service? - 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'] + ensure_kubernetes_service.update!( + active: active?, + api_url: api_url, + namespace: namespace, + token: token, + ca_pem: ca_cert, ) end - def kubeclient_auth_options - return { username: username, password: password } if username && password - return { bearer_token: token } if token - end - - def join_api_url(api_path) - url = URI.parse(api_url) - prefix = url.path.sub(%r{/+\z}, '') - - url.path = [prefix, api_path].join("/") - - url.to_s + def kubernetes_service + @kubernetes_service ||= project.kubernetes_service || project.build_kubernetes_service end - def terminal_auth - { - token: token, - ca_pem: ca_pem, - max_session_time: current_application_settings.terminal_max_session_time - } - end - - def enforce_namespace_to_lower_case - self.namespace = self.namespace&.downcase + def ensure_kubernetes_service + @kubernetes_service ||= kubernetes_service || project.build_kubernetes_service end end end diff --git a/app/services/clusters/create_service.rb b/app/services/clusters/create_service.rb index 503118fa6b6..a1c74566d7a 100644 --- a/app/services/clusters/create_service.rb +++ b/app/services/clusters/create_service.rb @@ -2,9 +2,6 @@ module Clusters class CreateService < BaseService attr_reader :access_token - TEMPOLARY_API_URL = 'http://tempolary_api_url'.freeze - TEMPOLARY_TOKEN = 'tempolary_token'.freeze - def execute(access_token) @access_token = access_token @@ -16,14 +13,9 @@ module Clusters private def create_cluster - cluster = nil - - ActiveRecord::Base.transaction do - cluster = Clusters::Cluster.create!(cluster_params) - cluster.projects << project - end - - cluster + Clusters::Cluster.create!( + cluster_params.merge( + projects: [project])) rescue ActiveRecord::RecordInvalid => e e.record end @@ -33,11 +25,6 @@ module Clusters params[:provider_gcp_attributes].try do |provider| provider[:access_token] = access_token - - params[:platform_kubernetes_attributes].try do |platform| - platform[:api_url] = TEMPOLARY_API_URL - platform[:token] = TEMPOLARY_TOKEN - end end @cluster_params = params.merge(user: current_user) diff --git a/db/schema.rb b/db/schema.rb index adf8b9594fb..24f2d4b439c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -481,8 +481,8 @@ ActiveRecord::Schema.define(version: 20171017145932) do create_table "cluster_projects", force: :cascade do |t| t.integer "project_id", null: false t.integer "cluster_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false end add_index "cluster_projects", ["cluster_id"], name: "index_cluster_projects_on_cluster_id", using: :btree -- cgit v1.2.3