summaryrefslogtreecommitdiff
path: root/spec/services/clusters/applications
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-24 09:09:25 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-24 09:09:25 +0000
commit6f7881ee9dcec34141a8f34fc814b56b366d2b48 (patch)
tree25f72a06874b32b1049b79a9d7f4f1b7bca43b9b /spec/services/clusters/applications
parent8c8bf44fa64f98114f7439f751c92d59a44b3218 (diff)
downloadgitlab-ce-6f7881ee9dcec34141a8f34fc814b56b366d2b48.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/services/clusters/applications')
-rw-r--r--spec/services/clusters/applications/check_upgrade_progress_service_spec.rb94
-rw-r--r--spec/services/clusters/applications/prometheus_config_service_spec.rb158
-rw-r--r--spec/services/clusters/applications/prometheus_update_service_spec.rb92
-rw-r--r--spec/services/clusters/applications/schedule_update_service_spec.rb37
4 files changed, 381 insertions, 0 deletions
diff --git a/spec/services/clusters/applications/check_upgrade_progress_service_spec.rb b/spec/services/clusters/applications/check_upgrade_progress_service_spec.rb
new file mode 100644
index 00000000000..c08b618fe6a
--- /dev/null
+++ b/spec/services/clusters/applications/check_upgrade_progress_service_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::CheckUpgradeProgressService do
+ RESCHEDULE_PHASES = ::Gitlab::Kubernetes::Pod::PHASES -
+ [::Gitlab::Kubernetes::Pod::SUCCEEDED, ::Gitlab::Kubernetes::Pod::FAILED, ::Gitlab].freeze
+
+ let(:application) { create(:clusters_applications_prometheus, :updating) }
+ let(:service) { described_class.new(application) }
+ let(:phase) { ::Gitlab::Kubernetes::Pod::UNKNOWN }
+ let(:errors) { nil }
+
+ shared_examples 'a terminated upgrade' do
+ it 'removes the POD' do
+ expect(service).to receive(:remove_pod).once
+
+ service.execute
+ end
+ end
+
+ shared_examples 'a not yet terminated upgrade' do |a_phase|
+ let(:phase) { a_phase }
+
+ context "when phase is #{a_phase}" do
+ context 'when not timed out' do
+ it 'reschedule a new check' do
+ expect(::ClusterWaitForAppUpdateWorker).to receive(:perform_in).once
+ expect(service).not_to receive(:remove_pod)
+
+ service.execute
+
+ expect(application).to be_updating
+ expect(application.status_reason).to be_nil
+ end
+ end
+
+ context 'when timed out' do
+ let(:application) { create(:clusters_applications_prometheus, :timed_out, :updating) }
+
+ it_behaves_like 'a terminated upgrade'
+
+ it 'make the application update errored' do
+ expect(::ClusterWaitForAppUpdateWorker).not_to receive(:perform_in)
+
+ service.execute
+
+ expect(application).to be_update_errored
+ expect(application.status_reason).to eq("Update timed out")
+ end
+ end
+ end
+ end
+
+ before do
+ allow(service).to receive(:phase).once.and_return(phase)
+
+ allow(service).to receive(:errors).and_return(errors)
+ allow(service).to receive(:remove_pod).and_return(nil)
+ end
+
+ describe '#execute' do
+ context 'when upgrade pod succeeded' do
+ let(:phase) { ::Gitlab::Kubernetes::Pod::SUCCEEDED }
+
+ it_behaves_like 'a terminated upgrade'
+
+ it 'make the application upgraded' do
+ expect(::ClusterWaitForAppUpdateWorker).not_to receive(:perform_in)
+
+ service.execute
+
+ expect(application).to be_updated
+ expect(application.status_reason).to be_nil
+ end
+ end
+
+ context 'when upgrade pod failed' do
+ let(:phase) { ::Gitlab::Kubernetes::Pod::FAILED }
+ let(:errors) { 'test installation failed' }
+
+ it_behaves_like 'a terminated upgrade'
+
+ it 'make the application update errored' do
+ service.execute
+
+ expect(application).to be_update_errored
+ expect(application.status_reason).to eq(errors)
+ end
+ end
+
+ RESCHEDULE_PHASES.each { |phase| it_behaves_like 'a not yet terminated upgrade', phase }
+ end
+end
diff --git a/spec/services/clusters/applications/prometheus_config_service_spec.rb b/spec/services/clusters/applications/prometheus_config_service_spec.rb
new file mode 100644
index 00000000000..993a697b543
--- /dev/null
+++ b/spec/services/clusters/applications/prometheus_config_service_spec.rb
@@ -0,0 +1,158 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::PrometheusConfigService do
+ include Gitlab::Routing.url_helpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:production) { create(:environment, project: project) }
+ let_it_be(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
+
+ let(:application) do
+ create(:clusters_applications_prometheus, :installed, cluster: cluster)
+ end
+
+ subject { described_class.new(project, cluster, application).execute(input) }
+
+ describe '#execute' do
+ let(:input) do
+ YAML.load_file(Rails.root.join('vendor/prometheus/values.yaml'))
+ end
+
+ context 'with alerts' do
+ let!(:alert) do
+ create(:prometheus_alert, project: project, environment: production)
+ end
+
+ it 'enables alertmanager' do
+ expect(subject.dig('alertmanager', 'enabled')).to eq(true)
+ end
+
+ describe 'alertmanagerFiles' do
+ let(:alertmanager) do
+ subject.dig('alertmanagerFiles', 'alertmanager.yml')
+ end
+
+ it 'contains receivers and route' do
+ expect(alertmanager.keys).to contain_exactly('receivers', 'route')
+ end
+
+ describe 'receivers' do
+ let(:receiver) { alertmanager.dig('receivers', 0) }
+ let(:webhook_config) { receiver.dig('webhook_configs', 0) }
+
+ let(:notify_url) do
+ notify_project_prometheus_alerts_url(project, format: :json)
+ end
+
+ it 'sets receiver' do
+ expect(receiver['name']).to eq('gitlab')
+ end
+
+ it 'sets webhook_config' do
+ expect(webhook_config).to eq(
+ 'url' => notify_url,
+ 'send_resolved' => true,
+ 'http_config' => {
+ 'bearer_token' => application.alert_manager_token
+ }
+ )
+ end
+ end
+
+ describe 'route' do
+ let(:route) { alertmanager.fetch('route') }
+
+ it 'sets route' do
+ expect(route).to eq(
+ 'receiver' => 'gitlab',
+ 'group_wait' => '30s',
+ 'group_interval' => '5m',
+ 'repeat_interval' => '4h'
+ )
+ end
+ end
+ end
+
+ describe 'serverFiles' do
+ let(:groups) { subject.dig('serverFiles', 'alerts', 'groups') }
+
+ it 'sets the alerts' do
+ rules = groups.dig(0, 'rules')
+ expect(rules.size).to eq(1)
+
+ expect(rules.first['alert']).to eq(alert.title)
+ end
+
+ context 'with parameterized queries' do
+ let!(:alert) do
+ create(:prometheus_alert,
+ project: project,
+ environment: production,
+ prometheus_metric: metric)
+ end
+
+ let(:metric) do
+ create(:prometheus_metric, query: query, project: project)
+ end
+
+ let(:query) { '%{ci_environment_slug}' }
+
+ it 'substitutes query variables' do
+ expect(Gitlab::Prometheus::QueryVariables)
+ .to receive(:call)
+ .with(production)
+ .and_call_original
+
+ expr = groups.dig(0, 'rules', 0, 'expr')
+ expect(expr).to include(production.name)
+ end
+ end
+
+ context 'with multiple environments' do
+ let(:staging) { create(:environment, project: project) }
+
+ before do
+ create(:prometheus_alert, project: project, environment: production)
+ create(:prometheus_alert, project: project, environment: staging)
+ end
+
+ it 'sets alerts for multiple environment' do
+ env_names = groups.map { |group| group['name'] }
+ expect(env_names).to contain_exactly(
+ "#{production.name}.rules",
+ "#{staging.name}.rules"
+ )
+ end
+
+ it 'substitutes query variables once per environment' do
+ expect(Gitlab::Prometheus::QueryVariables)
+ .to receive(:call)
+ .with(production)
+
+ expect(Gitlab::Prometheus::QueryVariables)
+ .to receive(:call)
+ .with(staging)
+
+ subject
+ end
+ end
+ end
+ end
+
+ context 'without alerts' do
+ it 'disables alertmanager' do
+ expect(subject.dig('alertmanager', 'enabled')).to eq(false)
+ end
+
+ it 'removes alertmanagerFiles' do
+ expect(subject).not_to include('alertmanagerFiles')
+ end
+
+ it 'removes alerts' do
+ expect(subject.dig('serverFiles', 'alerts')).to eq({})
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/prometheus_update_service_spec.rb b/spec/services/clusters/applications/prometheus_update_service_spec.rb
new file mode 100644
index 00000000000..078b01d2777
--- /dev/null
+++ b/spec/services/clusters/applications/prometheus_update_service_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::PrometheusUpdateService do
+ describe '#execute' do
+ let(:project) { create(:project) }
+ let(:environment) { create(:environment, project: project) }
+ let(:cluster) { create(:cluster, :provided_by_user, :with_installed_helm, projects: [project]) }
+ let(:application) { create(:clusters_applications_prometheus, :installed, cluster: cluster) }
+ let(:empty_alerts_values_update_yaml) { "---\nalertmanager:\n enabled: false\nserverFiles:\n alerts: {}\n" }
+ let!(:patch_command) { application.patch_command(empty_alerts_values_update_yaml) }
+ let(:helm_client) { instance_double(::Gitlab::Kubernetes::Helm::API) }
+
+ subject(:service) { described_class.new(application, project) }
+
+ before do
+ allow(service).to receive(:patch_command).with(empty_alerts_values_update_yaml).and_return(patch_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(:update).with(patch_command)
+
+ allow(::ClusterWaitForAppUpdateWorker)
+ .to receive(:perform_in)
+ .and_return(nil)
+ end
+
+ it 'make the application updating' do
+ expect(application.cluster).not_to be_nil
+
+ service.execute
+
+ expect(application).to be_updating
+ end
+
+ it 'updates current config' do
+ prometheus_config_service = spy(:prometheus_config_service)
+
+ expect(Clusters::Applications::PrometheusConfigService)
+ .to receive(:new)
+ .with(project, cluster, application)
+ .and_return(prometheus_config_service)
+
+ expect(prometheus_config_service)
+ .to receive(:execute)
+ .and_return(YAML.safe_load(empty_alerts_values_update_yaml))
+
+ service.execute
+ end
+
+ it 'schedules async update status check' do
+ expect(::ClusterWaitForAppUpdateWorker).to receive(:perform_in).once
+
+ service.execute
+ end
+ end
+
+ context 'when k8s cluster communication fails' do
+ before do
+ error = ::Kubeclient::HttpError.new(500, 'system failure', nil)
+ allow(helm_client).to receive(:update).and_raise(error)
+ end
+
+ it 'make the application update errored' do
+ service.execute
+
+ expect(application).to be_update_errored
+ expect(application.status_reason).to match(/kubernetes error:/i)
+ end
+ end
+
+ context 'when application cannot be persisted' do
+ let(:application) { build(:clusters_applications_prometheus, :installed) }
+
+ before do
+ allow(application).to receive(:make_updating!).once
+ .and_raise(ActiveRecord::RecordInvalid.new(application))
+ end
+
+ it 'make the application update errored' do
+ expect(helm_client).not_to receive(:update)
+
+ service.execute
+
+ expect(application).to be_update_errored
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/schedule_update_service_spec.rb b/spec/services/clusters/applications/schedule_update_service_spec.rb
new file mode 100644
index 00000000000..0764f5b6a97
--- /dev/null
+++ b/spec/services/clusters/applications/schedule_update_service_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::ScheduleUpdateService do
+ describe '#execute' do
+ let(:project) { create(:project) }
+
+ around do |example|
+ Timecop.freeze { example.run }
+ end
+
+ context 'when application is able to be updated' do
+ context 'when the application was recently scheduled' do
+ it 'schedules worker with a backoff delay' do
+ application = create(:clusters_applications_prometheus, :installed, last_update_started_at: Time.now + 5.minutes)
+ service = described_class.new(application, project)
+
+ expect(::ClusterUpdateAppWorker).to receive(:perform_in).with(described_class::BACKOFF_DELAY, application.name, application.id, project.id, Time.now).once
+
+ service.execute
+ end
+ end
+
+ context 'when the application has not been recently updated' do
+ it 'schedules worker' do
+ application = create(:clusters_applications_prometheus, :installed)
+ service = described_class.new(application, project)
+
+ expect(::ClusterUpdateAppWorker).to receive(:perform_async).with(application.name, application.id, project.id, Time.now).once
+
+ service.execute
+ end
+ end
+ end
+ end
+end