# frozen_string_literal: true require 'spec_helper' RSpec.describe Gitlab::BackgroundMigration::FixProjectsWithoutPrometheusService, :migration, schema: 2020_02_20_115023 do def service_params_for(project_id, params = {}) { project_id: project_id, active: false, properties: '{}', type: 'PrometheusService', template: false, push_events: true, issues_events: true, merge_requests_events: true, tag_push_events: true, note_events: true, category: 'monitoring', default: false, wiki_page_events: true, pipeline_events: true, confidential_issues_events: true, commit_events: true, job_events: true, confidential_note_events: true, deployment_events: false }.merge(params) end let(:namespaces) { table(:namespaces) } let(:projects) { table(:projects) } let(:services) { table(:services) } let(:clusters) { table(:clusters) } let(:cluster_groups) { table(:cluster_groups) } let(:clusters_applications_prometheus) { table(:clusters_applications_prometheus) } let(:namespace) { namespaces.create(name: 'user', path: 'user') } let(:project) { projects.create(namespace_id: namespace.id) } let(:application_statuses) do { errored: -1, installed: 3, updated: 5 } end let(:cluster_types) do { instance_type: 1, group_type: 2, project_type: 3 } end let(:columns) do %w(project_id active properties type template push_events issues_events merge_requests_events tag_push_events note_events category default wiki_page_events pipeline_events confidential_issues_events commit_events job_events confidential_note_events deployment_events) end describe '#perform' do shared_examples 'fix services entries state' do it 'is idempotent' do expect { subject.perform(project.id, project.id + 1) }.to change { services.order(:id).map { |row| row.attributes } } expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end context 'non prometheus services' do it 'does not change them' do other_type = 'SomeOtherService' services.create(service_params_for(project.id, active: true, type: other_type)) expect { subject.perform(project.id, project.id + 1) }.not_to change { services.where(type: other_type).order(:id).map { |row| row.attributes } } end end context 'prometheus integration services do not exist' do it 'creates missing services entries', :aggregate_failures do expect { subject.perform(project.id, project.id + 1) }.to change { services.count }.by(1) expect([service_params_for(project.id, active: true)]).to eq services.order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys } end context 'template is present for prometheus services' do it 'creates missing services entries', :aggregate_failures do services.create(service_params_for(nil, template: true, properties: { 'from_template' => true }.to_json)) expect { subject.perform(project.id, project.id + 1) }.to change { services.count }.by(1) updated_rows = services.where(template: false).order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys } expect([service_params_for(project.id, active: true, properties: { 'from_template' => true }.to_json)]).to eq updated_rows end end end context 'prometheus integration services exist' do context 'in active state' do it 'does not change them' do services.create(service_params_for(project.id, active: true)) expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end end context 'not in active state' do it 'sets active attribute to true' do service = services.create(service_params_for(project.id, active: false)) expect { subject.perform(project.id, project.id + 1) }.to change { service.reload.active? }.from(false).to(true) end context 'prometheus services are configured manually ' do it 'does not change them' do properties = '{"api_url":"http://test.dev","manual_configuration":"1"}' services.create(service_params_for(project.id, properties: properties, active: false)) expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end end end end end context 'k8s cluster shared on instance level' do let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:instance_type]) } context 'with installed prometheus application' do before do clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123') end it_behaves_like 'fix services entries state' end context 'with updated prometheus application' do before do clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123') end it_behaves_like 'fix services entries state' end context 'with errored prometheus application' do before do clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123') end it 'does not change services entries' do expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end end end context 'k8s cluster shared on group level' do let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:group_type]) } before do cluster_groups.create(cluster_id: cluster.id, group_id: project.namespace_id) end context 'with installed prometheus application' do before do clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123') end it_behaves_like 'fix services entries state' context 'second k8s cluster without application available' do let(:namespace_2) { namespaces.create(name: 'namespace2', path: 'namespace2') } let(:project_2) { projects.create(namespace_id: namespace_2.id) } before do cluster_2 = clusters.create(name: 'cluster2', cluster_type: cluster_types[:group_type]) cluster_groups.create(cluster_id: cluster_2.id, group_id: project_2.namespace_id) end it 'changed only affected services entries' do expect { subject.perform(project.id, project_2.id + 1) }.to change { services.count }.by(1) expect([service_params_for(project.id, active: true)]).to eq services.order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys } end end end context 'with updated prometheus application' do before do clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123') end it_behaves_like 'fix services entries state' end context 'with errored prometheus application' do before do clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123') end it 'does not change services entries' do expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end end context 'with missing prometheus application' do it 'does not change services entries' do expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end context 'with inactive service' do it 'does not change services entries' do services.create(service_params_for(project.id)) expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end end end end context 'k8s cluster for single project' do let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:project_type]) } let(:cluster_projects) { table(:cluster_projects) } context 'with installed prometheus application' do before do cluster_projects.create(cluster_id: cluster.id, project_id: project.id) clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123') end it 'does not change services entries' do expect { subject.perform(project.id, project.id + 1) }.not_to change { services.order(:id).map { |row| row.attributes } } end end end end end