diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-20 09:55:51 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-20 09:55:51 +0000 |
commit | e8d2c2579383897a1dd7f9debd359abe8ae8373d (patch) | |
tree | c42be41678c2586d49a75cabce89322082698334 /spec/models/project_services | |
parent | fc845b37ec3a90aaa719975f607740c22ba6a113 (diff) | |
download | gitlab-ce-e8d2c2579383897a1dd7f9debd359abe8ae8373d.tar.gz |
Add latest changes from gitlab-org/gitlab@14-1-stable-eev14.1.0-rc42
Diffstat (limited to 'spec/models/project_services')
-rw-r--r-- | spec/models/project_services/prometheus_service_spec.rb | 542 |
1 files changed, 0 insertions, 542 deletions
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb deleted file mode 100644 index a2025388fab..00000000000 --- a/spec/models/project_services/prometheus_service_spec.rb +++ /dev/null @@ -1,542 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -require 'googleauth' - -RSpec.describe PrometheusService, :use_clean_rails_memory_store_caching, :snowplow do - include PrometheusHelpers - include ReactiveCachingHelpers - - let_it_be_with_reload(:project) { create(:prometheus_project) } - - let(:service) { project.prometheus_service } - - describe "Associations" do - it { is_expected.to belong_to :project } - end - - context 'redirects' do - it 'does not follow redirects' do - redirect_to = 'https://redirected.example.com' - redirect_req_stub = stub_prometheus_request(prometheus_query_url('1'), status: 302, headers: { location: redirect_to }) - redirected_req_stub = stub_prometheus_request(redirect_to, body: { 'status': 'success' }) - - result = service.test - - # result = { success: false, result: error } - expect(result[:success]).to be_falsy - expect(result[:result]).to be_instance_of(Gitlab::PrometheusClient::UnexpectedResponseError) - - expect(redirect_req_stub).to have_been_requested - expect(redirected_req_stub).not_to have_been_requested - end - end - - describe 'Validations' do - context 'when manual_configuration is enabled' do - before do - service.manual_configuration = true - end - - it 'validates presence of api_url' do - expect(service).to validate_presence_of(:api_url) - end - end - - context 'when manual configuration is disabled' do - before do - service.manual_configuration = false - end - - it 'does not validate presence of api_url' do - expect(service).not_to validate_presence_of(:api_url) - expect(service.valid?).to eq(true) - end - - context 'local connections allowed' do - before do - stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) - end - - it 'does not validate presence of api_url' do - expect(service).not_to validate_presence_of(:api_url) - expect(service.valid?).to eq(true) - end - end - end - - context 'when the api_url domain points to localhost or local network' do - let(:domain) { Addressable::URI.parse(service.api_url).hostname } - - it 'cannot query' do - expect(service.can_query?).to be true - - aggregate_failures do - ['127.0.0.1', '192.168.2.3'].each do |url| - allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)]) - - expect(service.can_query?).to be false - end - end - end - - it 'can query when local requests are allowed' do - stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) - - aggregate_failures do - ['127.0.0.1', '192.168.2.3'].each do |url| - allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)]) - - expect(service.can_query?).to be true - end - end - end - - context 'with self-monitoring project and internal Prometheus' do - before do - service.api_url = 'http://localhost:9090' - - stub_application_setting(self_monitoring_project_id: project.id) - stub_config(prometheus: { enable: true, server_address: 'localhost:9090' }) - end - - it 'allows self-monitoring project to connect to internal Prometheus' do - aggregate_failures do - ['127.0.0.1', '192.168.2.3'].each do |url| - allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)]) - - expect(service.can_query?).to be true - end - end - end - - it 'does not allow self-monitoring project to connect to other local URLs' do - service.api_url = 'http://localhost:8000' - - aggregate_failures do - ['127.0.0.1', '192.168.2.3'].each do |url| - allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)]) - - expect(service.can_query?).to be false - end - end - end - end - end - end - - describe 'callbacks' do - context 'after_create' do - let(:project) { create(:project) } - let(:service) { build(:prometheus_service, project: project) } - - subject(:create_service) { service.save! } - - it 'creates default alerts' do - expect(Prometheus::CreateDefaultAlertsWorker) - .to receive(:perform_async) - .with(project.id) - - create_service - end - - context 'no project exists' do - let(:service) { build(:prometheus_service, :instance) } - - it 'does not create default alerts' do - expect(Prometheus::CreateDefaultAlertsWorker) - .not_to receive(:perform_async) - - create_service - end - end - end - end - - describe '#test' do - before do - service.manual_configuration = true - end - - let!(:req_stub) { stub_prometheus_request(prometheus_query_url('1'), body: prometheus_value_body('vector')) } - - context 'success' do - it 'reads the discovery endpoint' do - expect(service.test[:result]).to eq('Checked API endpoint') - expect(service.test[:success]).to be_truthy - expect(req_stub).to have_been_requested.twice - end - end - - context 'failure' do - let!(:req_stub) { stub_prometheus_request(prometheus_query_url('1'), status: 404) } - - it 'fails to read the discovery endpoint' do - expect(service.test[:success]).to be_falsy - expect(req_stub).to have_been_requested - end - end - end - - describe '#prometheus_client' do - let(:api_url) { 'http://some_url' } - - before do - service.active = true - service.api_url = api_url - service.manual_configuration = manual_configuration - end - - context 'manual configuration is enabled' do - let(:manual_configuration) { true } - - it 'calls valid?' do - allow(service).to receive(:valid?).and_call_original - - expect(service.prometheus_client).not_to be_nil - - expect(service).to have_received(:valid?) - end - end - - context 'manual configuration is disabled' do - let(:manual_configuration) { false } - - it 'no client provided' do - expect(service.prometheus_client).to be_nil - end - end - - context 'when local requests are allowed' do - let(:manual_configuration) { true } - let(:api_url) { 'http://192.168.1.1:9090' } - - before do - stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) - - stub_prometheus_request("#{api_url}/api/v1/query?query=1") - end - - it 'allows local requests' do - expect(service.prometheus_client).not_to be_nil - expect { service.prometheus_client.ping }.not_to raise_error - end - end - - context 'when local requests are blocked' do - let(:manual_configuration) { true } - let(:api_url) { 'http://192.168.1.1:9090' } - - before do - stub_application_setting(allow_local_requests_from_web_hooks_and_services: false) - - stub_prometheus_request("#{api_url}/api/v1/query?query=1") - end - - it 'blocks local requests' do - expect(service.prometheus_client).to be_nil - end - - context 'with self monitoring project and internal Prometheus URL' do - before do - stub_application_setting(allow_local_requests_from_web_hooks_and_services: false) - stub_application_setting(self_monitoring_project_id: project.id) - - stub_config(prometheus: { - enable: true, - server_address: api_url - }) - end - - it 'allows local requests' do - expect(service.prometheus_client).not_to be_nil - expect { service.prometheus_client.ping }.not_to raise_error - end - end - end - - context 'behind IAP' do - let(:manual_configuration) { true } - - let(:google_iap_service_account) do - { - type: "service_account", - # dummy private key generated only for this test to pass openssl validation - private_key: <<~KEY - -----BEGIN RSA PRIVATE KEY----- - MIIBOAIBAAJAU85LgUY5o6j6j/07GMLCNUcWJOBA1buZnNgKELayA6mSsHrIv31J - Y8kS+9WzGPQninea7DcM4hHA7smMgQD1BwIDAQABAkAqKxMy6PL3tn7dFL43p0ex - JyOtSmlVIiAZG1t1LXhE/uoLpYi5DnbYqGgu0oih+7nzLY/dXpNpXUmiRMOUEKmB - AiEAoTi2rBXbrLSi2C+H7M/nTOjMQQDuZ8Wr4uWpKcjYJTMCIQCFEskL565oFl/7 - RRQVH+cARrAsAAoJSbrOBAvYZ0PI3QIgIEFwis10vgEF86rOzxppdIG/G+JL0IdD - 9IluZuXAGPECIGUo7qSaLr75o2VEEgwtAFH5aptIPFjrL5LFCKwtdB4RAiAYZgFV - HCMmaooAw/eELuMoMWNYmujZ7VaAnOewGDW0uw== - -----END RSA PRIVATE KEY----- - KEY - } - end - - def stub_iap_request - service.google_iap_service_account_json = Gitlab::Json.generate(google_iap_service_account) - service.google_iap_audience_client_id = 'IAP_CLIENT_ID.apps.googleusercontent.com' - - stub_request(:post, 'https://oauth2.googleapis.com/token') - .to_return( - status: 200, - body: '{"id_token": "FOO"}', - headers: { 'Content-Type': 'application/json; charset=UTF-8' } - ) - end - - it 'includes the authorization header' do - stub_iap_request - - expect(service.prometheus_client).not_to be_nil - expect(service.prometheus_client.send(:options)).to have_key(:headers) - expect(service.prometheus_client.send(:options)[:headers]).to eq(authorization: "Bearer FOO") - end - - context 'when passed with token_credential_uri', issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/284819' do - let(:malicious_host) { 'http://example.com' } - - where(:param_name) do - [ - :token_credential_uri, - :tokencredentialuri, - :Token_credential_uri, - :tokenCredentialUri - ] - end - - with_them do - it 'does not make any unexpected HTTP requests' do - google_iap_service_account[param_name] = malicious_host - stub_iap_request - stub_request(:any, malicious_host).to_raise('Making additional HTTP requests is forbidden!') - - expect(service.prometheus_client).not_to be_nil - end - end - end - end - end - - describe '#prometheus_available?' do - context 'clusters with enabled prometheus' do - before do - create(:clusters_integrations_prometheus, cluster: cluster) - end - - context 'cluster belongs to project' do - let(:cluster) { create(:cluster, projects: [project]) } - - it 'returns true' do - expect(service.prometheus_available?).to be(true) - end - end - - context 'cluster belongs to projects group' do - let_it_be(:group) { create(:group) } - - let(:project) { create(:prometheus_project, group: group) } - let(:cluster) { create(:cluster_for_group, groups: [group]) } - - it 'returns true' do - expect(service.prometheus_available?).to be(true) - end - - it 'avoids N+1 queries' do - service - 5.times do |i| - other_cluster = create(:cluster_for_group, groups: [group], environment_scope: i) - create(:clusters_integrations_prometheus, cluster: other_cluster) - end - expect { service.prometheus_available? }.not_to exceed_query_limit(1) - end - end - - context 'cluster belongs to gitlab instance' do - let(:cluster) { create(:cluster, :instance) } - - it 'returns true' do - expect(service.prometheus_available?).to be(true) - end - end - end - - context 'clusters with prometheus disabled' do - let(:cluster) { create(:cluster, projects: [project]) } - let!(:prometheus) { create(:clusters_integrations_prometheus, :disabled, cluster: cluster) } - - it 'returns false' do - expect(service.prometheus_available?).to be(false) - end - end - - context 'clusters without prometheus' do - let(:cluster) { create(:cluster, projects: [project]) } - - it 'returns false' do - expect(service.prometheus_available?).to be(false) - end - end - - context 'no clusters' do - it 'returns false' do - expect(service.prometheus_available?).to be(false) - end - end - end - - describe '#synchronize_service_state before_save callback' do - context 'no clusters with prometheus are installed' do - context 'when service is inactive' do - before do - service.active = false - end - - it 'activates service when manual_configuration is enabled' do - expect { service.update!(manual_configuration: true) }.to change { service.active }.from(false).to(true) - end - - it 'keeps service inactive when manual_configuration is disabled' do - expect { service.update!(manual_configuration: false) }.not_to change { service.active }.from(false) - end - end - - context 'when service is active' do - before do - service.active = true - end - - it 'keeps the service active when manual_configuration is enabled' do - expect { service.update!(manual_configuration: true) }.not_to change { service.active }.from(true) - end - - it 'inactivates the service when manual_configuration is disabled' do - expect { service.update!(manual_configuration: false) }.to change { service.active }.from(true).to(false) - end - end - end - - context 'with prometheus installed in the cluster' do - before do - allow(service).to receive(:prometheus_available?).and_return(true) - end - - context 'when service is inactive' do - before do - service.active = false - end - - it 'activates service when manual_configuration is enabled' do - expect { service.update!(manual_configuration: true) }.to change { service.active }.from(false).to(true) - end - - it 'activates service when manual_configuration is disabled' do - expect { service.update!(manual_configuration: false) }.to change { service.active }.from(false).to(true) - end - end - - context 'when service is active' do - before do - service.active = true - end - - it 'keeps service active when manual_configuration is enabled' do - expect { service.update!(manual_configuration: true) }.not_to change { service.active }.from(true) - end - - it 'keeps service active when manual_configuration is disabled' do - expect { service.update!(manual_configuration: false) }.not_to change { service.active }.from(true) - end - end - end - end - - describe '#track_events after_commit callback' do - before do - allow(service).to receive(:prometheus_available?).and_return(true) - end - - context "enabling manual_configuration" do - it "tracks enable event" do - service.update!(manual_configuration: false) - service.update!(manual_configuration: true) - - expect_snowplow_event(category: 'cluster:services:prometheus', action: 'enabled_manual_prometheus') - end - - it "tracks disable event" do - service.update!(manual_configuration: true) - service.update!(manual_configuration: false) - - expect_snowplow_event(category: 'cluster:services:prometheus', action: 'disabled_manual_prometheus') - end - end - end - - describe '#editable?' do - it 'is editable' do - expect(service.editable?).to be(true) - end - - context 'when cluster exists with prometheus enabled' do - let(:cluster) { create(:cluster, projects: [project]) } - - before do - service.update!(manual_configuration: false) - - create(:clusters_integrations_prometheus, cluster: cluster) - end - - it 'remains editable' do - expect(service.editable?).to be(true) - end - end - end - - describe '#fields' do - let(:expected_fields) do - [ - { - type: 'checkbox', - name: 'manual_configuration', - title: s_('PrometheusService|Active'), - help: s_('PrometheusService|Select this checkbox to override the auto configuration settings with your own settings.'), - required: true - }, - { - type: 'text', - name: 'api_url', - title: 'API URL', - placeholder: s_('PrometheusService|https://prometheus.example.com/'), - help: s_('PrometheusService|The Prometheus API base URL.'), - required: true - }, - { - type: 'text', - name: 'google_iap_audience_client_id', - title: 'Google IAP Audience Client ID', - placeholder: s_('PrometheusService|IAP_CLIENT_ID.apps.googleusercontent.com'), - help: s_('PrometheusService|PrometheusService|The ID of the IAP-secured resource.'), - autocomplete: 'off', - required: false - }, - { - type: 'textarea', - name: 'google_iap_service_account_json', - title: 'Google IAP Service Account JSON', - placeholder: s_('PrometheusService|{ "type": "service_account", "project_id": ... }'), - help: s_('PrometheusService|The contents of the credentials.json file of your service account.'), - required: false - } - ] - end - - it 'returns fields' do - expect(service.fields).to eq(expected_fields) - end - end -end |