From 0d4548026f3060ca0a8f7aa8d8fc89838bc66130 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Fri, 22 Dec 2017 17:23:43 +0000 Subject: Extend Cluster Applications to allow installation of Prometheus --- app/assets/javascripts/clusters/clusters_bundle.js | 2 + .../clusters/components/applications.vue | 20 +++ .../clusters/services/clusters_service.js | 1 + .../javascripts/clusters/stores/clusters_store.js | 7 ++ app/assets/stylesheets/pages/clusters.scss | 2 +- app/models/clusters/applications/helm.rb | 17 +-- app/models/clusters/applications/ingress.rb | 23 +--- app/models/clusters/applications/prometheus.rb | 26 ++++ app/models/clusters/cluster.rb | 7 +- app/models/clusters/concerns/application_core.rb | 29 +++++ .../clusters/applications/base_helm_service.rb | 2 +- app/views/projects/clusters/show.html.haml | 1 + ...applications-to-allow-install-to-prometheus.yml | 5 + ...3433_create_clusters_applications_prometheus.rb | 18 +++ db/schema.rb | 9 ++ lib/gitlab/kubernetes/helm.rb | 90 +------------- lib/gitlab/kubernetes/helm/api.rb | 42 +++++++ lib/gitlab/kubernetes/helm/install_command.rb | 53 ++++++++ lib/gitlab/kubernetes/helm/pod.rb | 69 +++++++++++ .../clusters/applications_controller_spec.rb | 2 +- spec/factories/clusters/applications/helm.rb | 5 +- spec/factories/clusters/applications/ingress.rb | 35 ------ .../projects/clusters/applications_spec.rb | 2 +- .../clusters/components/applications_spec.js | 5 + spec/javascripts/clusters/services/mock_data.js | 6 + .../clusters/stores/clusters_store_spec.js | 7 ++ spec/lib/gitlab/kubernetes/helm/api_spec.rb | 69 +++++++++++ .../gitlab/kubernetes/helm/install_command_spec.rb | 111 +++++++++++++++++ spec/lib/gitlab/kubernetes/helm/pod_spec.rb | 86 +++++++++++++ spec/lib/gitlab/kubernetes/helm_spec.rb | 100 --------------- spec/models/clusters/applications/helm_spec.rb | 12 +- spec/models/clusters/applications/ingress_spec.rb | 102 +--------------- .../clusters/applications/prometheus_spec.rb | 16 +++ spec/models/clusters/cluster_spec.rb | 10 +- .../serializers/cluster_application_entity_spec.rb | 4 +- .../check_installation_progress_service_spec.rb | 4 +- .../clusters/applications/install_service_spec.rb | 12 +- .../schedule_installation_service_spec.rb | 2 +- spec/support/cluster_application_spec.rb | 105 ++++++++++++++++ vendor/prometheus/values.yaml | 134 +++++++++++++++++++++ 40 files changed, 865 insertions(+), 387 deletions(-) create mode 100644 app/models/clusters/applications/prometheus.rb create mode 100644 app/models/clusters/concerns/application_core.rb create mode 100644 changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml create mode 100644 db/migrate/20171212203433_create_clusters_applications_prometheus.rb create mode 100644 lib/gitlab/kubernetes/helm/api.rb create mode 100644 lib/gitlab/kubernetes/helm/install_command.rb create mode 100644 lib/gitlab/kubernetes/helm/pod.rb delete mode 100644 spec/factories/clusters/applications/ingress.rb create mode 100644 spec/lib/gitlab/kubernetes/helm/api_spec.rb create mode 100644 spec/lib/gitlab/kubernetes/helm/install_command_spec.rb create mode 100644 spec/lib/gitlab/kubernetes/helm/pod_spec.rb delete mode 100644 spec/lib/gitlab/kubernetes/helm_spec.rb create mode 100644 spec/models/clusters/applications/prometheus_spec.rb create mode 100644 spec/support/cluster_application_spec.rb create mode 100644 vendor/prometheus/values.yaml diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 2cfd6179a25..637d0dbde23 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -30,6 +30,7 @@ export default class Clusters { installHelmPath, installIngressPath, installRunnerPath, + installPrometheusPath, clusterStatus, clusterStatusReason, helpPath, @@ -44,6 +45,7 @@ export default class Clusters { installHelmEndpoint: installHelmPath, installIngressEndpoint: installIngressPath, installRunnerEndpoint: installRunnerPath, + installPrometheusEndpoint: installPrometheusPath, }); this.toggle = this.toggle.bind(this); diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index e5ae439d26e..cd58b88db69 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -67,6 +67,16 @@ export default { and send the results back to GitLab.`, )); }, + prometheusDescription() { + return sprintf( + _.escape(s__('ClusterIntegration|Prometheus is an open-source monitoring system with %{gitlabIntegrationLink} to monitor deployed applications.')), { + gitlabIntegrationLink: ` + ${_.escape(s__('ClusterIntegration|Gitlab Integration'))} + `, + }, + false, + ); + }, }, }; @@ -105,6 +115,16 @@ export default { :status-reason="applications.ingress.statusReason" :request-status="applications.ingress.requestStatus" :request-reason="applications.ingress.requestReason" + /> + diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js index 755c2981c2e..13468578f4f 100644 --- a/app/assets/javascripts/clusters/services/clusters_service.js +++ b/app/assets/javascripts/clusters/services/clusters_service.js @@ -7,6 +7,7 @@ export default class ClusterService { helm: this.options.installHelmEndpoint, ingress: this.options.installIngressEndpoint, runner: this.options.installRunnerEndpoint, + prometheus: this.options.installPrometheusEndpoint, }; } diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js index e731cdc3042..bd4a1fb37f9 100644 --- a/app/assets/javascripts/clusters/stores/clusters_store.js +++ b/app/assets/javascripts/clusters/stores/clusters_store.js @@ -28,6 +28,13 @@ export default class ClusterStore { requestStatus: null, requestReason: null, }, + prometheus: { + title: s__('ClusterIntegration|Prometheus'), + status: null, + statusReason: null, + requestStatus: null, + requestReason: null, + }, }, }; } diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss index 88d44131d5b..7b8ee026357 100644 --- a/app/assets/stylesheets/pages/clusters.scss +++ b/app/assets/stylesheets/pages/clusters.scss @@ -6,7 +6,7 @@ .cluster-applications-table { // Wait for the Vue to kick-in and render the applications block - min-height: 302px; + min-height: 400px; } .clusters-dropdown-menu { diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb index c7949d11ef8..193bb48e54d 100644 --- a/app/models/clusters/applications/helm.rb +++ b/app/models/clusters/applications/helm.rb @@ -3,32 +3,19 @@ module Clusters class Helm < ActiveRecord::Base self.table_name = 'clusters_applications_helm' + include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus - belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id - default_value_for :version, Gitlab::Kubernetes::Helm::HELM_VERSION - validates :cluster, presence: true - - after_initialize :set_initial_status - - def self.application_name - self.to_s.demodulize.underscore - end - def set_initial_status return unless not_installable? self.status = 'installable' if cluster&.platform_kubernetes_active? end - def name - self.class.application_name - end - def install_command - Gitlab::Kubernetes::Helm::InstallCommand.new(name, true) + Gitlab::Kubernetes::Helm::InstallCommand.new(name, install_helm: true) end end end diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 44bd979741e..9024f1df1cd 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -3,41 +3,22 @@ module Clusters class Ingress < ActiveRecord::Base self.table_name = 'clusters_applications_ingress' + include ::Clusters::Concerns::ApplicationCore include ::Clusters::Concerns::ApplicationStatus - belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id - - validates :cluster, presence: true - default_value_for :ingress_type, :nginx default_value_for :version, :nginx - after_initialize :set_initial_status - enum ingress_type: { nginx: 1 } - def self.application_name - self.to_s.demodulize.underscore - end - - def set_initial_status - return unless not_installable? - - self.status = 'installable' if cluster&.application_helm_installed? - end - - def name - self.class.application_name - end - def chart 'stable/nginx-ingress' end def install_command - Gitlab::Kubernetes::Helm::InstallCommand.new(name, false, chart) + Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart) end end end diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb new file mode 100644 index 00000000000..9b0787ee6ca --- /dev/null +++ b/app/models/clusters/applications/prometheus.rb @@ -0,0 +1,26 @@ +module Clusters + module Applications + class Prometheus < ActiveRecord::Base + VERSION = "2.0.0".freeze + + self.table_name = 'clusters_applications_prometheus' + + include ::Clusters::Concerns::ApplicationCore + include ::Clusters::Concerns::ApplicationStatus + + default_value_for :version, VERSION + + def chart + 'stable/prometheus' + end + + def chart_values_file + "#{Rails.root}/vendor/#{name}/values.yaml" + end + + def install_command + Gitlab::Kubernetes::Helm::InstallCommand.new(name, chart: chart, chart_values_file: chart_values_file) + end + end + end +end diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 55419189282..5ecbd4cbceb 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -6,7 +6,8 @@ module Clusters APPLICATIONS = { Applications::Helm.application_name => Applications::Helm, - Applications::Ingress.application_name => Applications::Ingress + Applications::Ingress.application_name => Applications::Ingress, + Applications::Prometheus.application_name => Applications::Prometheus }.freeze belongs_to :user @@ -21,6 +22,7 @@ module Clusters has_one :application_helm, class_name: 'Clusters::Applications::Helm' has_one :application_ingress, class_name: 'Clusters::Applications::Ingress' + has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus' accepts_nested_attributes_for :provider_gcp, update_only: true accepts_nested_attributes_for :platform_kubernetes, update_only: true @@ -62,7 +64,8 @@ module Clusters def applications [ application_helm || build_application_helm, - application_ingress || build_application_ingress + application_ingress || build_application_ingress, + application_prometheus || build_application_prometheus ] end diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb new file mode 100644 index 00000000000..a98fa85a5ff --- /dev/null +++ b/app/models/clusters/concerns/application_core.rb @@ -0,0 +1,29 @@ +module Clusters + module Concerns + module ApplicationCore + extend ActiveSupport::Concern + + included do + belongs_to :cluster, class_name: 'Clusters::Cluster', foreign_key: :cluster_id + + validates :cluster, presence: true + + after_initialize :set_initial_status + + def set_initial_status + return unless not_installable? + + self.status = 'installable' if cluster&.application_helm_installed? + end + + def self.application_name + self.to_s.demodulize.underscore + end + + def name + self.class.application_name + end + end + end + end +end diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb index 9a4ce31cb39..cba1b920f7c 100644 --- a/app/services/clusters/applications/base_helm_service.rb +++ b/app/services/clusters/applications/base_helm_service.rb @@ -18,7 +18,7 @@ module Clusters end def helm_api - @helm_api ||= Gitlab::Kubernetes::Helm.new(kubeclient) + @helm_api ||= Gitlab::Kubernetes::Helm::Api.new(kubeclient) end def install_command diff --git a/app/views/projects/clusters/show.html.haml b/app/views/projects/clusters/show.html.haml index fe6dacf1f0d..0115c64c076 100644 --- a/app/views/projects/clusters/show.html.haml +++ b/app/views/projects/clusters/show.html.haml @@ -9,6 +9,7 @@ .edit-cluster-form.js-edit-cluster-form{ data: { status_path: status_path, install_helm_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :helm), install_ingress_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :ingress), + install_prometheus_path: install_applications_namespace_project_cluster_path(@cluster.project.namespace, @cluster.project, @cluster, :prometheus), toggle_status: @cluster.enabled? ? 'true': 'false', cluster_status: @cluster.status_name, cluster_status_reason: @cluster.status_reason, diff --git a/changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml b/changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml new file mode 100644 index 00000000000..ffb79d7d79f --- /dev/null +++ b/changelogs/unreleased/41053-extend-cluster-applications-to-allow-install-to-prometheus.yml @@ -0,0 +1,5 @@ +--- +title: Add Prometheus to available Cluster applications +merge_request: 15895 +author: +type: added diff --git a/db/migrate/20171212203433_create_clusters_applications_prometheus.rb b/db/migrate/20171212203433_create_clusters_applications_prometheus.rb new file mode 100644 index 00000000000..dc2531d2691 --- /dev/null +++ b/db/migrate/20171212203433_create_clusters_applications_prometheus.rb @@ -0,0 +1,18 @@ +class CreateClustersApplicationsPrometheus < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :clusters_applications_prometheus do |t| + t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade } + + t.integer :status, null: false + t.string :version, null: false + + t.text :status_reason + + t.timestamps_with_timezone null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 81b594cd0c1..aa5db5da4f0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -568,6 +568,15 @@ ActiveRecord::Schema.define(version: 20171220191323) do t.text "status_reason" end + create_table "clusters_applications_prometheus", force: :cascade do |t| + t.integer "cluster_id", null: false + t.integer "status", null: false + t.string "version", null: false + t.text "status_reason" + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false + end + create_table "container_repositories", force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb index 407cdefc04d..0f0588b8b23 100644 --- a/lib/gitlab/kubernetes/helm.rb +++ b/lib/gitlab/kubernetes/helm.rb @@ -1,96 +1,8 @@ module Gitlab module Kubernetes - class Helm + module Helm HELM_VERSION = '2.7.0'.freeze NAMESPACE = 'gitlab-managed-apps'.freeze - INSTALL_DEPS = <<-EOS.freeze - set -eo pipefail - apk add -U ca-certificates openssl >/dev/null - wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null - mv /tmp/linux-amd64/helm /usr/bin/ - EOS - - InstallCommand = Struct.new(:name, :install_helm, :chart) do - def pod_name - "install-#{name}" - end - end - - def initialize(kubeclient) - @kubeclient = kubeclient - @namespace = Gitlab::Kubernetes::Namespace.new(NAMESPACE, kubeclient) - end - - def install(command) - @namespace.ensure_exists! - @kubeclient.create_pod(pod_resource(command)) - end - - ## - # Returns Pod phase - # - # https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase - # - # values: "Pending", "Running", "Succeeded", "Failed", "Unknown" - # - def installation_status(pod_name) - @kubeclient.get_pod(pod_name, @namespace.name).status.phase - end - - def installation_log(pod_name) - @kubeclient.get_pod_log(pod_name, @namespace.name).body - end - - def delete_installation_pod!(pod_name) - @kubeclient.delete_pod(pod_name, @namespace.name) - end - - private - - def pod_resource(command) - labels = { 'gitlab.org/action': 'install', 'gitlab.org/application': command.name } - metadata = { name: command.pod_name, namespace: @namespace.name, labels: labels } - container = { - name: 'helm', - image: 'alpine:3.6', - env: generate_pod_env(command), - command: %w(/bin/sh), - args: %w(-c $(COMMAND_SCRIPT)) - } - spec = { containers: [container], restartPolicy: 'Never' } - - ::Kubeclient::Resource.new(metadata: metadata, spec: spec) - end - - def generate_pod_env(command) - { - HELM_VERSION: HELM_VERSION, - TILLER_NAMESPACE: @namespace.name, - COMMAND_SCRIPT: generate_script(command) - }.map { |key, value| { name: key, value: value } } - end - - def generate_script(command) - [ - INSTALL_DEPS, - helm_init_command(command), - helm_install_command(command) - ].join("\n") - end - - def helm_init_command(command) - if command.install_helm - 'helm init >/dev/null' - else - 'helm init --client-only >/dev/null' - end - end - - def helm_install_command(command) - return if command.chart.nil? - - "helm install #{command.chart} --name #{command.name} --namespace #{@namespace.name} >/dev/null" - end end end end diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb new file mode 100644 index 00000000000..ebd7dc1b100 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/api.rb @@ -0,0 +1,42 @@ +module Gitlab + module Kubernetes + module Helm + class Api + def initialize(kubeclient) + @kubeclient = kubeclient + @namespace = Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, kubeclient) + end + + def install(command) + @namespace.ensure_exists! + @kubeclient.create_pod(pod_resource(command)) + end + + ## + # Returns Pod phase + # + # https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase + # + # values: "Pending", "Running", "Succeeded", "Failed", "Unknown" + # + def installation_status(pod_name) + @kubeclient.get_pod(pod_name, @namespace.name).status.phase + end + + def installation_log(pod_name) + @kubeclient.get_pod_log(pod_name, @namespace.name).body + end + + def delete_installation_pod!(pod_name) + @kubeclient.delete_pod(pod_name, @namespace.name) + end + + private + + def pod_resource(command) + Pod.new(command, @namespace.name, @kubeclient).generate + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/install_command.rb b/lib/gitlab/kubernetes/helm/install_command.rb new file mode 100644 index 00000000000..8d8c441a4b1 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/install_command.rb @@ -0,0 +1,53 @@ +module Gitlab + module Kubernetes + module Helm + class InstallCommand + attr_reader :name, :install_helm, :chart, :chart_values_file + + def initialize(name, install_helm: false, chart: false, chart_values_file: false) + @name = name + @install_helm = install_helm + @chart = chart + @chart_values_file = chart_values_file + end + + def pod_name + "install-#{name}" + end + + def generate_script(namespace_name) + [ + install_dps_command, + init_command, + complete_command(namespace_name) + ].join("\n") + end + + private + + def init_command + if install_helm + 'helm init >/dev/null' + else + 'helm init --client-only >/dev/null' + end + end + + def complete_command(namespace_name) + return unless chart + + "helm install #{chart} --name #{name} --namespace #{namespace_name} >/dev/null" + end + + def install_dps_command + <<~HEREDOC + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + HEREDOC + end + end + end + end +end diff --git a/lib/gitlab/kubernetes/helm/pod.rb b/lib/gitlab/kubernetes/helm/pod.rb new file mode 100644 index 00000000000..233f6bf6227 --- /dev/null +++ b/lib/gitlab/kubernetes/helm/pod.rb @@ -0,0 +1,69 @@ +module Gitlab + module Kubernetes + module Helm + class Pod + def initialize(command, namespace_name, kubeclient) + @command = command + @namespace_name = namespace_name + @kubeclient = kubeclient + end + + def generate + spec = { containers: [container_specification], restartPolicy: 'Never' } + if command.chart_values_file + generate_config_map + spec['volumes'] = volumes_specification + end + ::Kubeclient::Resource.new(metadata: metadata, spec: spec) + end + + private + + attr_reader :command, :namespace_name, :kubeclient + + def container_specification + container = { + name: 'helm', + image: 'alpine:3.6', + env: generate_pod_env(command), + command: %w(/bin/sh), + args: %w(-c $(COMMAND_SCRIPT)) + } + container[:volumeMounts] = volume_mounts_specification if command.chart_values_file + container + end + + def labels + { 'gitlab.org/action': 'install', 'gitlab.org/application': command.name } + end + + def metadata + { name: command.pod_name, namespace: namespace_name, labels: labels } + end + + def volume_mounts_specification + [{ name: 'config-volume', mountPath: '/etc/config' }] + end + + def volumes_specification + [{ name: 'config-volume', configMap: { name: 'values-config' } }] + end + + def generate_pod_env(command) + { + HELM_VERSION: Gitlab::Kubernetes::Helm::HELM_VERSION, + TILLER_NAMESPACE: namespace_name, + COMMAND_SCRIPT: command.generate_script(namespace_name) + }.map { |key, value| { name: key, value: value } } + end + + def generate_config_map + resource = ::Kubeclient::Resource.new + resource.metadata = { name: 'values-config', namespace: namespace_name } + resource.data = YAML.load_file(command.chart_values_file) + kubeclient.create_config_map(resource) + end + end + end + end +end diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb index 8b460646059..99fdff5f846 100644 --- a/spec/controllers/projects/clusters/applications_controller_spec.rb +++ b/spec/controllers/projects/clusters/applications_controller_spec.rb @@ -52,7 +52,7 @@ describe Projects::Clusters::ApplicationsController do context 'when application is already installing' do before do - create(:cluster_applications_helm, :installing, cluster: cluster) + create(:clusters_applications_helm, :installing, cluster: cluster) end it 'returns 400' do diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index d82fa8e34aa..775fbb3d27b 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -1,5 +1,5 @@ FactoryBot.define do - factory :cluster_applications_helm, class: Clusters::Applications::Helm do + factory :clusters_applications_helm, class: Clusters::Applications::Helm do cluster factory: %i(cluster provided_by_gcp) trait :not_installable do @@ -31,5 +31,8 @@ FactoryBot.define do installing updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago end + + factory :clusters_applications_ingress, class: Clusters::Applications::Ingress + factory :clusters_applications_prometheus, class: Clusters::Applications::Prometheus end end diff --git a/spec/factories/clusters/applications/ingress.rb b/spec/factories/clusters/applications/ingress.rb deleted file mode 100644 index 85f54a9d744..00000000000 --- a/spec/factories/clusters/applications/ingress.rb +++ /dev/null @@ -1,35 +0,0 @@ -FactoryBot.define do - factory :cluster_applications_ingress, class: Clusters::Applications::Ingress do - cluster factory: %i(cluster provided_by_gcp) - - trait :not_installable do - status(-2) - end - - trait :installable do - status 0 - end - - trait :scheduled do - status 1 - end - - trait :installing do - status 2 - end - - trait :installed do - status 3 - end - - trait :errored do - status(-1) - status_reason 'something went wrong' - end - - trait :timeouted do - installing - updated_at ClusterWaitForAppInstallationWorker::TIMEOUT.ago - end - end -end diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb index b34cd061ec6..9c4abec115f 100644 --- a/spec/features/projects/clusters/applications_spec.rb +++ b/spec/features/projects/clusters/applications_spec.rb @@ -73,7 +73,7 @@ feature 'Clusters Applications', :js do before do allow(ClusterInstallAppWorker).to receive(:perform_async).and_return(nil) - create(:cluster_applications_helm, :installed, cluster: cluster) + create(:clusters_applications_helm, :installed, cluster: cluster) page.within('.js-cluster-application-row-ingress') do page.find(:css, '.js-cluster-application-install-button').click diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js index 7460da031c4..1a8affad4e3 100644 --- a/spec/javascripts/clusters/components/applications_spec.js +++ b/spec/javascripts/clusters/components/applications_spec.js @@ -21,6 +21,7 @@ describe('Applications', () => { helm: { title: 'Helm Tiller' }, ingress: { title: 'Ingress' }, runner: { title: 'GitLab Runner' }, + prometheus: { title: 'Prometheus' }, }, }); }); @@ -33,6 +34,10 @@ describe('Applications', () => { expect(vm.$el.querySelector('.js-cluster-application-row-ingress')).toBeDefined(); }); + it('renders a row for Prometheus', () => { + expect(vm.$el.querySelector('.js-cluster-application-row-prometheus')).toBeDefined(); + }); + /* * / it('renders a row for GitLab Runner', () => { expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeDefined(); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index af6b6a73819..253b3c45243 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -22,6 +22,11 @@ const CLUSTERS_MOCK_DATA = { name: 'runner', status: APPLICATION_INSTALLING, status_reason: null, + }, + { + name: 'prometheus', + status: APPLICATION_ERROR, + status_reason: 'Cannot connect', }], }, }, @@ -30,6 +35,7 @@ const CLUSTERS_MOCK_DATA = { '/gitlab-org/gitlab-shell/clusters/1/applications/helm': { }, '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': { }, '/gitlab-org/gitlab-shell/clusters/1/applications/runner': { }, + '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': { }, }, }; diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index cb8b3d38e2e..ec2889355e6 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -82,6 +82,13 @@ describe('Clusters Store', () => { requestStatus: null, requestReason: null, }, + prometheus: { + title: 'Prometheus', + status: mockResponseData.applications[3].status, + statusReason: mockResponseData.applications[3].status_reason, + requestStatus: null, + requestReason: null, + }, }, }); }); diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb new file mode 100644 index 00000000000..69112fe90b1 --- /dev/null +++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe Gitlab::Kubernetes::Helm::Api do + let(:client) { double('kubernetes client') } + let(:helm) { described_class.new(client) } + let(:gitlab_namespace) { Gitlab::Kubernetes::Helm::NAMESPACE } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(gitlab_namespace, client) } + let(:install_helm) { true } + let(:chart) { 'stable/a_chart' } + let(:application_name) { 'app_name' } + let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm: install_helm, chart: chart) } + subject { helm } + + before do + allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client).and_return(namespace) + end + + describe '#initialize' do + it 'creates a namespace object' do + expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client) + + subject + end + end + + describe '#install' do + before do + allow(client).to receive(:create_pod).and_return(nil) + allow(namespace).to receive(:ensure_exists!).once + end + + it 'ensures the namespace exists before creating the POD' do + expect(namespace).to receive(:ensure_exists!).once.ordered + expect(client).to receive(:create_pod).once.ordered + + subject.install(command) + end + end + + describe '#installation_status' do + let(:phase) { Gitlab::Kubernetes::Pod::RUNNING } + let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation + + it 'fetches POD phase from kubernetes cluster' do + expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod) + + expect(subject.installation_status(command.pod_name)).to eq(phase) + end + end + + describe '#installation_log' do + let(:log) { 'some output' } + let(:response) { RestClient::Response.new(log) } + + it 'fetches POD phase from kubernetes cluster' do + expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response) + + expect(subject.installation_log(command.pod_name)).to eq(log) + end + end + + describe '#delete_installation_pod!' do + it 'deletes the POD from kubernetes cluster' do + expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once + + subject.delete_installation_pod!(command.pod_name) + end + end +end diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb new file mode 100644 index 00000000000..4afe48e72ad --- /dev/null +++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb @@ -0,0 +1,111 @@ +require 'rails_helper' + +describe Gitlab::Kubernetes::Helm::InstallCommand do + let(:prometheus) { create(:clusters_applications_prometheus) } + + describe "#initialize" do + context "With all the params" do + subject { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart, chart_values_file: prometheus.chart_values_file) } + + it 'should assign all parameters' do + expect(subject.name).to eq(prometheus.name) + expect(subject.install_helm).to be_truthy + expect(subject.chart).to eq(prometheus.chart) + expect(subject.chart_values_file).to eq("#{Rails.root}/vendor/prometheus/values.yaml") + end + end + + context 'when install_helm is not set' do + subject { described_class.new(prometheus.name, chart: prometheus.chart, chart_values_file: true) } + + it 'should set install_helm as false' do + expect(subject.install_helm).to be_falsy + end + end + + context 'when chart is not set' do + subject { described_class.new(prometheus.name, install_helm: true) } + + it 'should set chart as nil' do + expect(subject.chart).to be_falsy + end + end + + context 'when chart_values_file is not set' do + subject { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart) } + + it 'should set chart_values_file as nil' do + expect(subject.chart_values_file).to be_falsy + end + end + end + + describe "#generate_script" do + let(:install_command) { described_class.new(prometheus.name, install_helm: install_helm) } + let(:client) { double('kubernetes client') } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, client) } + subject { install_command.send(:generate_script, namespace.name) } + + context 'when install helm is true' do + let(:install_helm) { true } + let(:command) do + <<~MSG + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + + helm init >/dev/null + MSG + end + + it 'should return appropriate command' do + is_expected.to eq(command) + end + end + + context 'when install helm is false' do + let(:install_helm) { false } + let(:command) do + <<~MSG + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + + helm init --client-only >/dev/null + MSG + end + + it 'should return appropriate command' do + is_expected.to eq(command) + end + end + + context 'when chart is present' do + let(:install_command) { described_class.new(prometheus.name, chart: prometheus.chart) } + let(:command) do + <<~MSG.chomp + set -eo pipefail + apk add -U ca-certificates openssl >/dev/null + wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null + mv /tmp/linux-amd64/helm /usr/bin/ + + helm init --client-only >/dev/null + helm install #{prometheus.chart} --name #{prometheus.name} --namespace #{namespace.name} >/dev/null + MSG + end + + it 'should return appropriate command' do + is_expected.to eq(command) + end + end + end + + describe "#pod_name" do + let(:install_command) { described_class.new(prometheus.name, install_helm: true, chart: prometheus.chart, chart_values_file: true) } + subject { install_command.send(:pod_name) } + + it { is_expected.to eq('install-prometheus') } + end +end diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb new file mode 100644 index 00000000000..906b10b96d4 --- /dev/null +++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb @@ -0,0 +1,86 @@ +require 'rails_helper' + +describe Gitlab::Kubernetes::Helm::Pod do + describe '#generate' do + let(:cluster) { create(:cluster) } + let(:app) { create(:clusters_applications_prometheus, cluster: cluster) } + let(:command) { app.install_command } + let(:client) { double('kubernetes client') } + let(:namespace) { Gitlab::Kubernetes::Namespace.new(Gitlab::Kubernetes::Helm::NAMESPACE, client) } + subject { described_class.new(command, namespace.name, client) } + + before do + allow(client).to receive(:create_config_map).and_return(nil) + end + + shared_examples 'helm pod' do + it 'should generate a Kubeclient::Resource' do + expect(subject.generate).to be_a_kind_of(Kubeclient::Resource) + end + + it 'should generate the appropriate metadata' do + metadata = subject.generate.metadata + expect(metadata.name).to eq("install-#{app.name}") + expect(metadata.namespace).to eq('gitlab-managed-apps') + expect(metadata.labels['gitlab.org/action']).to eq('install') + expect(metadata.labels['gitlab.org/application']).to eq(app.name) + end + + it 'should generate a container spec' do + spec = subject.generate.spec + expect(spec.containers.count).to eq(1) + end + + it 'should generate the appropriate specifications for the container' do + container = subject.generate.spec.containers.first + expect(container.name).to eq('helm') + expect(container.image).to eq('alpine:3.6') + expect(container.env.count).to eq(3) + expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT]) + expect(container.command).to match_array(["/bin/sh"]) + expect(container.args).to match_array(["-c", "$(COMMAND_SCRIPT)"]) + end + + it 'should include a never restart policy' do + spec = subject.generate.spec + expect(spec.restartPolicy).to eq('Never') + end + end + + context 'with a configuration file' do + it_behaves_like 'helm pod' + + it 'should include volumes for the container' do + container = subject.generate.spec.containers.first + expect(container.volumeMounts.first['name']).to eq('config-volume') + expect(container.volumeMounts.first['mountPath']).to eq('/etc/config') + end + + it 'should include a volume inside the specification' do + spec = subject.generate.spec + expect(spec.volumes.first['name']).to eq('config-volume') + end + + it 'should mount configMap specification in the volume' do + spec = subject.generate.spec + expect(spec.volumes.first.configMap['name']).to eq('values-config') + end + end + + context 'without a configuration file' do + let(:app) { create(:clusters_applications_ingress, cluster: cluster) } + + it_behaves_like 'helm pod' + + it 'should not include volumeMounts inside the container' do + container = subject.generate.spec.containers.first + expect(container.volumeMounts).to be_nil + end + + it 'should not a volume inside the specification' do + spec = subject.generate.spec + expect(spec.volumes).to be_nil + end + end + end +end diff --git a/spec/lib/gitlab/kubernetes/helm_spec.rb b/spec/lib/gitlab/kubernetes/helm_spec.rb deleted file mode 100644 index 15f99b0401f..00000000000 --- a/spec/lib/gitlab/kubernetes/helm_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Kubernetes::Helm do - let(:client) { double('kubernetes client') } - let(:helm) { described_class.new(client) } - let(:namespace) { Gitlab::Kubernetes::Namespace.new(described_class::NAMESPACE, client) } - let(:install_helm) { true } - let(:chart) { 'stable/a_chart' } - let(:application_name) { 'app_name' } - let(:command) { Gitlab::Kubernetes::Helm::InstallCommand.new(application_name, install_helm, chart) } - subject { helm } - - before do - allow(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client).and_return(namespace) - end - - describe '#initialize' do - it 'creates a namespace object' do - expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(described_class::NAMESPACE, client) - - subject - end - end - - describe '#install' do - before do - allow(client).to receive(:create_pod).and_return(nil) - allow(namespace).to receive(:ensure_exists!).once - end - - it 'ensures the namespace exists before creating the POD' do - expect(namespace).to receive(:ensure_exists!).once.ordered - expect(client).to receive(:create_pod).once.ordered - - subject.install(command) - end - end - - describe '#installation_status' do - let(:phase) { Gitlab::Kubernetes::Pod::RUNNING } - let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation - - it 'fetches POD phase from kubernetes cluster' do - expect(client).to receive(:get_pod).with(command.pod_name, described_class::NAMESPACE).once.and_return(pod) - - expect(subject.installation_status(command.pod_name)).to eq(phase) - end - end - - describe '#installation_log' do - let(:log) { 'some output' } - let(:response) { RestClient::Response.new(log) } - - it 'fetches POD phase from kubernetes cluster' do - expect(client).to receive(:get_pod_log).with(command.pod_name, described_class::NAMESPACE).once.and_return(response) - - expect(subject.installation_log(command.pod_name)).to eq(log) - end - end - - describe '#delete_installation_pod!' do - it 'deletes the POD from kubernetes cluster' do - expect(client).to receive(:delete_pod).with(command.pod_name, described_class::NAMESPACE).once - - subject.delete_installation_pod!(command.pod_name) - end - end - - describe '#helm_init_command' do - subject { helm.send(:helm_init_command, command) } - - context 'when command.install_helm is true' do - let(:install_helm) { true } - - it { is_expected.to eq('helm init >/dev/null') } - end - - context 'when command.install_helm is false' do - let(:install_helm) { false } - - it { is_expected.to eq('helm init --client-only >/dev/null') } - end - end - - describe '#helm_install_command' do - subject { helm.send(:helm_install_command, command) } - - context 'when command.chart is nil' do - let(:chart) { nil } - - it { is_expected.to be_nil } - end - - context 'when command.chart is set' do - let(:chart) { 'stable/a_chart' } - - it { is_expected.to eq("helm install #{chart} --name #{application_name} --namespace #{namespace.name} >/dev/null")} - end - end -end diff --git a/spec/models/clusters/applications/helm_spec.rb b/spec/models/clusters/applications/helm_spec.rb index f8855079842..eb57abaf6ef 100644 --- a/spec/models/clusters/applications/helm_spec.rb +++ b/spec/models/clusters/applications/helm_spec.rb @@ -40,13 +40,13 @@ describe Clusters::Applications::Helm do describe '#install_command' do it 'has all the needed information' do - expect(subject.install_command).to have_attributes(name: subject.name, install_helm: true, chart: nil) + expect(subject.install_command).to have_attributes(name: subject.name, install_helm: true) end end describe 'status state machine' do describe '#make_installing' do - subject { create(:cluster_applications_helm, :scheduled) } + subject { create(:clusters_applications_helm, :scheduled) } it 'is installing' do subject.make_installing! @@ -56,7 +56,7 @@ describe Clusters::Applications::Helm do end describe '#make_installed' do - subject { create(:cluster_applications_helm, :installing) } + subject { create(:clusters_applications_helm, :installing) } it 'is installed' do subject.make_installed @@ -66,7 +66,7 @@ describe Clusters::Applications::Helm do end describe '#make_errored' do - subject { create(:cluster_applications_helm, :installing) } + subject { create(:clusters_applications_helm, :installing) } let(:reason) { 'some errors' } it 'is errored' do @@ -78,7 +78,7 @@ describe Clusters::Applications::Helm do end describe '#make_scheduled' do - subject { create(:cluster_applications_helm, :installable) } + subject { create(:clusters_applications_helm, :installable) } it 'is scheduled' do subject.make_scheduled @@ -87,7 +87,7 @@ describe Clusters::Applications::Helm do end describe 'when was errored' do - subject { create(:cluster_applications_helm, :errored) } + subject { create(:clusters_applications_helm, :errored) } it 'clears #status_reason' do expect(subject.status_reason).not_to be_nil diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb index b83472e1944..619c088b0bf 100644 --- a/spec/models/clusters/applications/ingress_spec.rb +++ b/spec/models/clusters/applications/ingress_spec.rb @@ -4,105 +4,5 @@ describe Clusters::Applications::Ingress do it { is_expected.to belong_to(:cluster) } it { is_expected.to validate_presence_of(:cluster) } - describe '#name' do - it 'is .application_name' do - expect(subject.name).to eq(described_class.application_name) - end - - it 'is recorded in Clusters::Cluster::APPLICATIONS' do - expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class) - end - end - - describe '#status' do - let(:cluster) { create(:cluster, :provided_by_gcp) } - - subject { described_class.new(cluster: cluster) } - - it 'defaults to :not_installable' do - expect(subject.status_name).to be(:not_installable) - end - - context 'when application helm is scheduled' do - before do - create(:cluster_applications_helm, :scheduled, cluster: cluster) - end - - it 'defaults to :not_installable' do - expect(subject.status_name).to be(:not_installable) - end - end - - context 'when application helm is installed' do - before do - create(:cluster_applications_helm, :installed, cluster: cluster) - end - - it 'defaults to :installable' do - expect(subject.status_name).to be(:installable) - end - end - end - - describe '#install_command' do - it 'has all the needed information' do - expect(subject.install_command).to have_attributes(name: subject.name, install_helm: false, chart: subject.chart) - end - end - - describe 'status state machine' do - describe '#make_installing' do - subject { create(:cluster_applications_ingress, :scheduled) } - - it 'is installing' do - subject.make_installing! - - expect(subject).to be_installing - end - end - - describe '#make_installed' do - subject { create(:cluster_applications_ingress, :installing) } - - it 'is installed' do - subject.make_installed - - expect(subject).to be_installed - end - end - - describe '#make_errored' do - subject { create(:cluster_applications_ingress, :installing) } - let(:reason) { 'some errors' } - - it 'is errored' do - subject.make_errored(reason) - - expect(subject).to be_errored - expect(subject.status_reason).to eq(reason) - end - end - - describe '#make_scheduled' do - subject { create(:cluster_applications_ingress, :installable) } - - it 'is scheduled' do - subject.make_scheduled - - expect(subject).to be_scheduled - end - - describe 'when was errored' do - subject { create(:cluster_applications_ingress, :errored) } - - it 'clears #status_reason' do - expect(subject.status_reason).not_to be_nil - - subject.make_scheduled! - - expect(subject.status_reason).to be_nil - end - end - end - end + include_examples 'cluster application specs', described_class end diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb new file mode 100644 index 00000000000..696099f7cf7 --- /dev/null +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +describe Clusters::Applications::Prometheus do + it { is_expected.to belong_to(:cluster) } + it { is_expected.to validate_presence_of(:cluster) } + + include_examples 'cluster application specs', described_class + + describe "#chart_values_file" do + subject { create(:clusters_applications_prometheus).chart_values_file } + + it 'should return chart values file path' do + expect(subject).to eq("#{Rails.root}/vendor/prometheus/values.yaml") + end + end +end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 2683d21ddbe..799d7ced116 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -5,6 +5,9 @@ describe Clusters::Cluster do it { is_expected.to have_many(:projects) } it { is_expected.to have_one(:provider_gcp) } it { is_expected.to have_one(:platform_kubernetes) } + it { is_expected.to have_one(:application_helm) } + it { is_expected.to have_one(:application_ingress) } + it { is_expected.to have_one(:application_prometheus) } it { is_expected.to delegate_method(:status).to(:provider) } it { is_expected.to delegate_method(:status_reason).to(:provider) } it { is_expected.to delegate_method(:status_name).to(:provider) } @@ -190,11 +193,12 @@ describe Clusters::Cluster do end context 'when applications are created' do - let!(:helm) { create(:cluster_applications_helm, cluster: cluster) } - let!(:ingress) { create(:cluster_applications_ingress, cluster: cluster) } + let!(:helm) { create(:clusters_applications_helm, cluster: cluster) } + let!(:ingress) { create(:clusters_applications_ingress, cluster: cluster) } + let!(:prometheus) { create(:clusters_applications_prometheus, cluster: cluster) } it 'returns a list of created applications' do - is_expected.to contain_exactly(helm, ingress) + is_expected.to contain_exactly(helm, ingress, prometheus) end end end diff --git a/spec/serializers/cluster_application_entity_spec.rb b/spec/serializers/cluster_application_entity_spec.rb index 87c7b2ad36e..b5a55b4ef6e 100644 --- a/spec/serializers/cluster_application_entity_spec.rb +++ b/spec/serializers/cluster_application_entity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ClusterApplicationEntity do describe '#as_json' do - let(:application) { build(:cluster_applications_helm) } + let(:application) { build(:clusters_applications_helm) } subject { described_class.new(application).as_json } it 'has name' do @@ -18,7 +18,7 @@ describe ClusterApplicationEntity do end context 'when application is errored' do - let(:application) { build(:cluster_applications_helm, :errored) } + let(:application) { build(:clusters_applications_helm, :errored) } it 'has corresponded data' do expect(subject[:status]).to eq(:errored) diff --git a/spec/services/clusters/applications/check_installation_progress_service_spec.rb b/spec/services/clusters/applications/check_installation_progress_service_spec.rb index 75fc05d36e9..6894c1797b0 100644 --- a/spec/services/clusters/applications/check_installation_progress_service_spec.rb +++ b/spec/services/clusters/applications/check_installation_progress_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Clusters::Applications::CheckInstallationProgressService do RESCHEDULE_PHASES = Gitlab::Kubernetes::Pod::PHASES - [Gitlab::Kubernetes::Pod::SUCCEEDED, Gitlab::Kubernetes::Pod::FAILED].freeze - let(:application) { create(:cluster_applications_helm, :installing) } + let(:application) { create(:clusters_applications_helm, :installing) } let(:service) { described_class.new(application) } let(:phase) { Gitlab::Kubernetes::Pod::UNKNOWN } let(:errors) { nil } @@ -33,7 +33,7 @@ describe Clusters::Applications::CheckInstallationProgressService do end context 'when timeouted' do - let(:application) { create(:cluster_applications_helm, :timeouted) } + let(:application) { create(:clusters_applications_helm, :timeouted) } it_behaves_like 'a terminated installation' diff --git a/spec/services/clusters/applications/install_service_spec.rb b/spec/services/clusters/applications/install_service_spec.rb index 054a49ffedf..ad175226e92 100644 --- a/spec/services/clusters/applications/install_service_spec.rb +++ b/spec/services/clusters/applications/install_service_spec.rb @@ -2,17 +2,19 @@ require 'spec_helper' describe Clusters::Applications::InstallService do describe '#execute' do - let(:application) { create(:cluster_applications_helm, :scheduled) } + let(:application) { create(:clusters_applications_helm, :scheduled) } + let!(:install_command) { application.install_command } let(:service) { described_class.new(application) } - let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm) } + let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) } before do + allow(service).to receive(:install_command).and_return(install_command) allow(service).to receive(:helm_api).and_return(helm_client) end context 'when there are no errors' do before do - expect(helm_client).to receive(:install).with(application.install_command) + expect(helm_client).to receive(:install).with(install_command) allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil) end @@ -33,7 +35,7 @@ describe Clusters::Applications::InstallService do context 'when k8s cluster communication fails' do before do error = KubeException.new(500, 'system failure', nil) - expect(helm_client).to receive(:install).with(application.install_command).and_raise(error) + expect(helm_client).to receive(:install).with(install_command).and_raise(error) end it 'make the application errored' do @@ -45,7 +47,7 @@ describe Clusters::Applications::InstallService do end context 'when application cannot be persisted' do - let(:application) { build(:cluster_applications_helm, :scheduled) } + let(:application) { build(:clusters_applications_helm, :scheduled) } it 'make the application errored' do expect(application).to receive(:make_installing!).once.and_raise(ActiveRecord::RecordInvalid) diff --git a/spec/services/clusters/applications/schedule_installation_service_spec.rb b/spec/services/clusters/applications/schedule_installation_service_spec.rb index 047a6e44dab..473dbcbb692 100644 --- a/spec/services/clusters/applications/schedule_installation_service_spec.rb +++ b/spec/services/clusters/applications/schedule_installation_service_spec.rb @@ -34,7 +34,7 @@ describe Clusters::Applications::ScheduleInstallationService do end context 'when installation is already in progress' do - let(:application) { create(:cluster_applications_helm, :installing) } + let(:application) { create(:clusters_applications_helm, :installing) } let(:cluster) { application.cluster } it_behaves_like 'a failing service' diff --git a/spec/support/cluster_application_spec.rb b/spec/support/cluster_application_spec.rb new file mode 100644 index 00000000000..ab77910a050 --- /dev/null +++ b/spec/support/cluster_application_spec.rb @@ -0,0 +1,105 @@ +shared_examples 'cluster application specs' do + let(:factory_name) { described_class.to_s.downcase.gsub("::", "_") } + + describe '#name' do + it 'is .application_name' do + expect(subject.name).to eq(described_class.application_name) + end + + it 'is recorded in Clusters::Cluster::APPLICATIONS' do + expect(Clusters::Cluster::APPLICATIONS[subject.name]).to eq(described_class) + end + end + + describe '#status' do + let(:cluster) { create(:cluster, :provided_by_gcp) } + + subject { described_class.new(cluster: cluster) } + + it 'defaults to :not_installable' do + expect(subject.status_name).to be(:not_installable) + end + + context 'when application helm is scheduled' do + before do + create(factory_name, :scheduled, cluster: cluster) + end + + it 'defaults to :not_installable' do + expect(subject.status_name).to be(:not_installable) + end + end + + context 'when application helm is installed' do + before do + create(:clusters_applications_helm, :installed, cluster: cluster) + end + + it 'defaults to :installable' do + expect(subject.status_name).to be(:installable) + end + end + end + + describe '#install_command' do + it 'has all the needed information' do + expect(subject.install_command).to have_attributes(name: subject.name, install_helm: false) + end + end + + describe 'status state machine' do + describe '#make_installing' do + subject { create(factory_name, :scheduled) } + + it 'is installing' do + subject.make_installing! + + expect(subject).to be_installing + end + end + + describe '#make_installed' do + subject { create(factory_name, :installing) } + + it 'is installed' do + subject.make_installed + + expect(subject).to be_installed + end + end + + describe '#make_errored' do + subject { create(factory_name, :installing) } + let(:reason) { 'some errors' } + + it 'is errored' do + subject.make_errored(reason) + + expect(subject).to be_errored + expect(subject.status_reason).to eq(reason) + end + end + + describe '#make_scheduled' do + subject { create(factory_name, :installable) } + + it 'is scheduled' do + subject.make_scheduled + + expect(subject).to be_scheduled + end + + describe 'when was errored' do + subject { create(factory_name, :errored) } + + it 'clears #status_reason' do + expect(subject.status_reason).not_to be_nil + + subject.make_scheduled! + + expect(subject.status_reason).to be_nil + end + end + end + end +end diff --git a/vendor/prometheus/values.yaml b/vendor/prometheus/values.yaml new file mode 100644 index 00000000000..dd9496deb4d --- /dev/null +++ b/vendor/prometheus/values.yaml @@ -0,0 +1,134 @@ +alertmanager: | + enabled: false + +kubeStateMetrics: | + enabled: 'false' + +nodeExporter: | + enabled: 'false' + +pushgateway: | + enabled: 'false' + +serverFiles: | + alerts: '' + rules: '' + + prometheus.yml: |- + rule_files: | + - /etc/config/rules + - /etc/config/alerts + scrape_configs: | + - job_name: prometheus + static_configs: | + - targets: + - localhost:9090 + + - job_name: 'kubernetes-apiservers' + kubernetes_sd_configs: | + - role: endpoints + scheme: https + + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + relabel_configs: + - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] + action: keep + regex: default;kubernetes;https + - job_name: 'kubernetes-nodes' + scheme: https + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + insecure_skip_verify: true + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + kubernetes_sd_configs: + - role: node + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/${1}/proxy/metrics + + - job_name: 'kubernetes-service-endpoints' + kubernetes_sd_configs: + - role: endpoints + relabel_configs: | + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] + action: keep + regex: 'true' + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] + action: replace + target_label: __scheme__ + regex: (https?) + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] + action: replace + target_label: __address__ + regex: (.+)(?::\d+);(\d+) + replacement: $1:$2 + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: kubernetes_namespace + - source_labels: [__meta_kubernetes_service_name] + action: replace + target_label: kubernetes_name + - job_name: 'prometheus-pushgateway' + honor_labels: true + kubernetes_sd_configs: | + - role: service + relabel_configs: | + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] + action: keep + regex: pushgateway + - job_name: 'kubernetes-services' + metrics_path: /probe + params: | + module: [http_2xx] + kubernetes_sd_configs: | + - role: service + relabel_configs: | + - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] + action: keep + regex: 'true' + - source_labels: [__address__] + target_label: __param_target + - target_label: __address__ + replacement: blackbox + - source_labels: [__param_target] + target_label: instance + - action: labelmap + regex: __meta_kubernetes_service_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + target_label: kubernetes_namespace + - source_labels: [__meta_kubernetes_service_name] + target_label: kubernetes_name + - job_name: 'kubernetes-pods' + kubernetes_sd_configs: + - role: pod + relabel_configs: + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] + action: keep + regex: 'true' + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] + action: replace + target_label: __metrics_path__ + regex: (.+) + - action: labelmap + regex: __meta_kubernetes_pod_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + action: replace + target_label: kubernetes_namespace + - source_labels: [__meta_kubernetes_pod_name] + action: replace + target_label: kubernetes_pod_name -- cgit v1.2.3