diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-24 09:09:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-24 09:09:25 +0000 |
commit | 6f7881ee9dcec34141a8f34fc814b56b366d2b48 (patch) | |
tree | 25f72a06874b32b1049b79a9d7f4f1b7bca43b9b /spec/services/clusters/applications | |
parent | 8c8bf44fa64f98114f7439f751c92d59a44b3218 (diff) | |
download | gitlab-ce-6f7881ee9dcec34141a8f34fc814b56b366d2b48.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/services/clusters/applications')
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 |