Extend Cluster Applications to allow installation of Prometheus
This commit is contained in:
parent
79cbfedf67
commit
0d4548026f
39 changed files with 805 additions and 327 deletions
|
@ -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);
|
||||
|
|
|
@ -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: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html", target="_blank" rel="noopener noreferrer">
|
||||
${_.escape(s__('ClusterIntegration|Gitlab Integration'))}
|
||||
</a>`,
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -105,6 +115,16 @@ export default {
|
|||
:status-reason="applications.ingress.statusReason"
|
||||
:request-status="applications.ingress.requestStatus"
|
||||
:request-reason="applications.ingress.requestReason"
|
||||
/>
|
||||
<application-row
|
||||
id="prometheus"
|
||||
:title="applications.prometheus.title"
|
||||
title-link="https://prometheus.io/docs/introduction/overview/"
|
||||
:description="prometheusDescription"
|
||||
:status="applications.prometheus.status"
|
||||
:status-reason="applications.prometheus.statusReason"
|
||||
:request-status="applications.prometheus.requestStatus"
|
||||
:request-reason="applications.prometheus.requestReason"
|
||||
/>
|
||||
<!-- NOTE: Don't forget to update `clusters.scss` min-height for this block and uncomment `application_spec` tests -->
|
||||
<!-- Add GitLab Runner row, all other plumbing is complete -->
|
||||
|
|
|
@ -7,6 +7,7 @@ export default class ClusterService {
|
|||
helm: this.options.installHelmEndpoint,
|
||||
ingress: this.options.installIngressEndpoint,
|
||||
runner: this.options.installRunnerEndpoint,
|
||||
prometheus: this.options.installPrometheusEndpoint,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
26
app/models/clusters/applications/prometheus.rb
Normal file
26
app/models/clusters/applications/prometheus.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
29
app/models/clusters/concerns/application_core.rb
Normal file
29
app/models/clusters/concerns/application_core.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Prometheus to available Cluster applications
|
||||
merge_request: 15895
|
||||
author:
|
||||
type: added
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
42
lib/gitlab/kubernetes/helm/api.rb
Normal file
42
lib/gitlab/kubernetes/helm/api.rb
Normal file
|
@ -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
|
53
lib/gitlab/kubernetes/helm/install_command.rb
Normal file
53
lib/gitlab/kubernetes/helm/install_command.rb
Normal file
|
@ -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
|
69
lib/gitlab/kubernetes/helm/pod.rb
Normal file
69
lib/gitlab/kubernetes/helm/pod.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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': { },
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Kubernetes::Helm do
|
||||
describe Gitlab::Kubernetes::Helm::Api do
|
||||
let(:client) { double('kubernetes client') }
|
||||
let(:helm) { described_class.new(client) }
|
||||
let(:namespace) { Gitlab::Kubernetes::Namespace.new(described_class::NAMESPACE, 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, chart) }
|
||||
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(described_class::NAMESPACE, client).and_return(namespace)
|
||||
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(described_class::NAMESPACE, client)
|
||||
expect(Gitlab::Kubernetes::Namespace).to receive(:new).with(gitlab_namespace, client)
|
||||
|
||||
subject
|
||||
end
|
||||
|
@ -41,7 +42,7 @@ describe Gitlab::Kubernetes::Helm do
|
|||
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(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
|
||||
|
@ -52,7 +53,7 @@ describe Gitlab::Kubernetes::Helm do
|
|||
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(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
|
||||
|
@ -60,41 +61,9 @@ describe Gitlab::Kubernetes::Helm do
|
|||
|
||||
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
|
||||
expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_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
|
111
spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
Normal file
111
spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
Normal file
|
@ -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
|
86
spec/lib/gitlab/kubernetes/helm/pod_spec.rb
Normal file
86
spec/lib/gitlab/kubernetes/helm/pod_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
16
spec/models/clusters/applications/prometheus_spec.rb
Normal file
16
spec/models/clusters/applications/prometheus_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
105
spec/support/cluster_application_spec.rb
Normal file
105
spec/support/cluster_application_spec.rb
Normal file
|
@ -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
|
134
vendor/prometheus/values.yaml
vendored
Normal file
134
vendor/prometheus/values.yaml
vendored
Normal file
|
@ -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
|
Loading…
Reference in a new issue