diff options
Diffstat (limited to 'spec/lib/gitlab/usage_data_spec.rb')
-rw-r--r-- | spec/lib/gitlab/usage_data_spec.rb | 344 |
1 files changed, 298 insertions, 46 deletions
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index a46778bb6c3..9c6aab10083 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -7,6 +7,8 @@ describe Gitlab::UsageData, :aggregate_failures do before do allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false) + + stub_object_store_settings end shared_examples "usage data execution" do @@ -42,6 +44,9 @@ describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:projects_jira_active]).to eq(4) expect(count_data[:projects_jira_server_active]).to eq(2) expect(count_data[:projects_jira_cloud_active]).to eq(2) + expect(count_data[:jira_imports_projects_count]).to eq(2) + expect(count_data[:jira_imports_total_imported_count]).to eq(3) + expect(count_data[:jira_imports_total_imported_issues_count]).to eq(13) expect(count_data[:projects_slack_notifications_active]).to eq(2) expect(count_data[:projects_slack_slash_active]).to eq(1) expect(count_data[:projects_slack_active]).to eq(2) @@ -57,6 +62,9 @@ describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:issues_using_zoom_quick_actions]).to eq(3) expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2) expect(count_data[:incident_issues]).to eq(4) + expect(count_data[:issues_created_gitlab_alerts]).to eq(1) + expect(count_data[:alert_bot_incident_issues]).to eq(4) + expect(count_data[:incident_labeled_issues]).to eq(3) expect(count_data[:clusters_enabled]).to eq(6) expect(count_data[:project_clusters_enabled]).to eq(4) @@ -82,6 +90,56 @@ describe Gitlab::UsageData, :aggregate_failures do expect(count_data[:clusters_management_project]).to eq(1) end + it 'gathers object store usage correctly' do + expect(subject[:object_store]).to eq( + { artifacts: { enabled: true, object_store: { enabled: true, direct_upload: true, background_upload: false, provider: "AWS" } }, + external_diffs: { enabled: false }, + lfs: { enabled: true, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } }, + uploads: { enabled: nil, object_store: { enabled: false, direct_upload: true, background_upload: false, provider: "AWS" } }, + packages: { enabled: true, object_store: { enabled: false, direct_upload: false, background_upload: true, provider: "AWS" } } } + ) + end + + context 'with existing container expiration policies' do + let_it_be(:disabled) { create(:container_expiration_policy, enabled: false) } + let_it_be(:enabled) { create(:container_expiration_policy, enabled: true) } + + %i[keep_n cadence older_than].each do |attribute| + ContainerExpirationPolicy.send("#{attribute}_options").keys.each do |value| + let_it_be("container_expiration_policy_with_#{attribute}_set_to_#{value}") { create(:container_expiration_policy, attribute => value) } + end + end + + let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) } + let(:active_policies) { ::ContainerExpirationPolicy.active } + + subject { described_class.data[:counts] } + + it 'gathers usage data' do + expect(subject[:projects_with_expiration_policy_enabled]).to eq 20 + expect(subject[:projects_with_expiration_policy_disabled]).to eq 1 + + expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_unset]).to eq 14 + expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_1]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_5]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_10]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_25]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_50]).to eq 1 + + expect(subject[:projects_with_expiration_policy_enabled_with_older_than_unset]).to eq 16 + expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_7d]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_14d]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_30d]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_90d]).to eq 1 + + expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1d]).to eq 12 + expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_7d]).to eq 5 + expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_14d]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1month]).to eq 1 + expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_3month]).to eq 1 + end + end + it 'works when queries time out' do allow_any_instance_of(ActiveRecord::Relation) .to receive(:count).and_raise(ActiveRecord::StatementInvalid.new('')) @@ -101,6 +159,7 @@ describe Gitlab::UsageData, :aggregate_failures do subject { described_class.usage_data_counters } it { is_expected.to all(respond_to :totals) } + it { is_expected.to all(respond_to :fallback_totals) } describe 'the results of calling #totals on all objects in the array' do subject { described_class.usage_data_counters.map(&:totals) } @@ -109,6 +168,13 @@ describe Gitlab::UsageData, :aggregate_failures do it { is_expected.to all(have_attributes(keys: all(be_a Symbol), values: all(be_a Integer))) } end + describe 'the results of calling #fallback_totals on all objects in the array' do + subject { described_class.usage_data_counters.map(&:fallback_totals) } + + it { is_expected.to all(be_a Hash) } + it { is_expected.to all(have_attributes(keys: all(be_a Symbol), values: all(eq(-1)))) } + end + it 'does not have any conflicts' do all_keys = subject.flat_map { |counter| counter.totals.keys } @@ -128,6 +194,14 @@ describe Gitlab::UsageData, :aggregate_failures do end end + describe '.recording_ce_finished_at' do + subject { described_class.recording_ce_finish_data } + + it 'gathers time ce recording finishes at' do + expect(subject[:recording_ce_finished_at]).to be_a(Time) + end + end + context 'when not relying on database records' do describe '#features_usage_data_ce' do subject { described_class.features_usage_data_ce } @@ -143,42 +217,20 @@ describe Gitlab::UsageData, :aggregate_failures do expect(subject[:dependency_proxy_enabled]).to eq(Gitlab.config.dependency_proxy.enabled) expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled) expect(subject[:web_ide_clientside_preview_enabled]).to eq(Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?) + expect(subject[:grafana_link_enabled]).to eq(Gitlab::CurrentSettings.grafana_enabled?) end - context 'with existing container expiration policies' do - let_it_be(:disabled) { create(:container_expiration_policy, enabled: false) } - let_it_be(:enabled) { create(:container_expiration_policy, enabled: true) } - %i[keep_n cadence older_than].each do |attribute| - ContainerExpirationPolicy.send("#{attribute}_options").keys.each do |value| - let_it_be("container_expiration_policy_with_#{attribute}_set_to_#{value}") { create(:container_expiration_policy, attribute => value) } - end + context 'with embedded grafana' do + it 'returns true when embedded grafana is enabled' do + stub_application_setting(grafana_enabled: true) + + expect(subject[:grafana_link_enabled]).to eq(true) end - let(:inactive_policies) { ::ContainerExpirationPolicy.where(enabled: false) } - let(:active_policies) { ::ContainerExpirationPolicy.active } - - it 'gathers usage data' do - expect(subject[:projects_with_expiration_policy_enabled]).to eq 16 - expect(subject[:projects_with_expiration_policy_disabled]).to eq 1 - - expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_unset]).to eq 10 - expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_1]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_5]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_10]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_25]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_keep_n_set_to_50]).to eq 1 - - expect(subject[:projects_with_expiration_policy_enabled_with_older_than_unset]).to eq 12 - expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_7d]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_14d]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_30d]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_older_than_set_to_90d]).to eq 1 - - expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1d]).to eq 12 - expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_7d]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_14d]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_1month]).to eq 1 - expect(subject[:projects_with_expiration_policy_enabled_with_cadence_set_to_3month]).to eq 1 + it 'returns false when embedded grafana is disabled' do + stub_application_setting(grafana_enabled: false) + + expect(subject[:grafana_link_enabled]).to eq(false) end end end @@ -223,6 +275,66 @@ describe Gitlab::UsageData, :aggregate_failures do end end + describe '#object_store_config' do + let(:component) { 'lfs' } + + subject { described_class.object_store_config(component) } + + context 'when object_store is not configured' do + it 'returns component enable status only' do + allow(Settings).to receive(:[]).with(component).and_return({ 'enabled' => false }) + + expect(subject).to eq({ enabled: false }) + end + end + + context 'when object_store is configured' do + it 'returns filtered object store config' do + allow(Settings).to receive(:[]).with(component) + .and_return( + { 'enabled' => true, + 'object_store' => + { 'enabled' => true, + 'remote_directory' => component, + 'direct_upload' => true, + 'connection' => + { 'provider' => 'AWS', 'aws_access_key_id' => 'minio', 'aws_secret_access_key' => 'gdk-minio', 'region' => 'gdk', 'endpoint' => 'http://127.0.0.1:9000', 'path_style' => true }, + 'background_upload' => false, + 'proxy_download' => false } }) + + expect(subject).to eq( + { enabled: true, object_store: { enabled: true, direct_upload: true, background_upload: false, provider: "AWS" } }) + end + end + + context 'when retrieve component setting meets exception' do + it 'returns -1 for component enable status' do + allow(Settings).to receive(:[]).with(component).and_raise(StandardError) + + expect(subject).to eq({ enabled: -1 }) + end + end + end + + describe '#object_store_usage_data' do + subject { described_class.object_store_usage_data } + + it 'fetches object store config of five components' do + %w(artifacts external_diffs lfs uploads packages).each do |component| + expect(described_class).to receive(:object_store_config).with(component).and_return("#{component}_object_store_config") + end + + expect(subject).to eq( + object_store: { + artifacts: 'artifacts_object_store_config', + external_diffs: 'external_diffs_object_store_config', + lfs: 'lfs_object_store_config', + uploads: 'uploads_object_store_config', + packages: 'packages_object_store_config' + }) + end + end + describe '#cycle_analytics_usage_data' do subject { described_class.cycle_analytics_usage_data } @@ -244,18 +356,132 @@ describe Gitlab::UsageData, :aggregate_failures do describe '#ingress_modsecurity_usage' do subject { described_class.ingress_modsecurity_usage } - it 'gathers variable data' do - allow_any_instance_of( - ::Clusters::Applications::IngressModsecurityUsageService - ).to receive(:execute).and_return( - { - ingress_modsecurity_blocking: 1, - ingress_modsecurity_disabled: 2 - } - ) - - expect(subject[:ingress_modsecurity_blocking]).to eq(1) - expect(subject[:ingress_modsecurity_disabled]).to eq(2) + let(:environment) { create(:environment) } + let(:project) { environment.project } + let(:environment_scope) { '*' } + let(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) } + let(:cluster) { create(:cluster, environment_scope: environment_scope, projects: [project]) } + let(:ingress_mode) { :modsecurity_blocking } + let!(:ingress) { create(:clusters_applications_ingress, ingress_mode, cluster: cluster) } + + context 'when cluster is disabled' do + let(:cluster) { create(:cluster, :disabled, projects: [project]) } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'when deployment is unsuccessful' do + let!(:deployment) { create(:deployment, :failed, environment: environment, project: project, cluster: cluster) } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'when deployment is successful' do + let!(:deployment) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) } + + context 'when modsecurity is in blocking mode' do + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(1) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'when modsecurity is in logging mode' do + let(:ingress_mode) { :modsecurity_logging } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(1) + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'when modsecurity is disabled' do + let(:ingress_mode) { :modsecurity_disabled } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(1) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'when modsecurity is not installed' do + let(:ingress_mode) { :modsecurity_not_installed } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(1) + end + end + + context 'with multiple projects' do + let(:environment_2) { create(:environment) } + let(:project_2) { environment_2.project } + let(:cluster_2) { create(:cluster, environment_scope: environment_scope, projects: [project_2]) } + let!(:ingress_2) { create(:clusters_applications_ingress, :modsecurity_logging, cluster: cluster_2) } + let!(:deployment_2) { create(:deployment, :success, environment: environment_2, project: project_2, cluster: cluster_2) } + + it 'gathers non-duplicated ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(1) + expect(subject[:ingress_modsecurity_blocking]).to eq(1) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'with multiple deployments' do + let!(:deployment_2) { create(:deployment, :success, environment: environment, project: project, cluster: cluster) } + + it 'gathers non-duplicated ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(1) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'with multiple projects' do + let(:environment_2) { create(:environment) } + let(:project_2) { environment_2.project } + let!(:deployment_2) { create(:deployment, :success, environment: environment_2, project: project_2, cluster: cluster) } + let(:cluster) { create(:cluster, environment_scope: environment_scope, projects: [project, project_2]) } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(2) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end + + context 'with multiple environments' do + let!(:environment_2) { create(:environment, project: project) } + let!(:deployment_2) { create(:deployment, :success, environment: environment_2, project: project, cluster: cluster) } + + it 'gathers ingress data' do + expect(subject[:ingress_modsecurity_logging]).to eq(0) + expect(subject[:ingress_modsecurity_blocking]).to eq(2) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + expect(subject[:ingress_modsecurity_not_installed]).to eq(0) + end + end end end @@ -334,9 +560,10 @@ describe Gitlab::UsageData, :aggregate_failures do end it 'returns the fallback value when counting fails' do + stub_const("Gitlab::UsageData::FALLBACK", 15) allow(relation).to receive(:count).and_raise(ActiveRecord::StatementInvalid.new('')) - expect(described_class.count(relation, fallback: 15, batch: false)).to eq(15) + expect(described_class.count(relation, batch: false)).to eq(15) end end @@ -350,9 +577,10 @@ describe Gitlab::UsageData, :aggregate_failures do end it 'returns the fallback value when counting fails' do + stub_const("Gitlab::UsageData::FALLBACK", 15) allow(relation).to receive(:distinct_count_by).and_raise(ActiveRecord::StatementInvalid.new('')) - expect(described_class.distinct_count(relation, fallback: 15, batch: false)).to eq(15) + expect(described_class.distinct_count(relation, batch: false)).to eq(15) end end end @@ -387,4 +615,28 @@ describe Gitlab::UsageData, :aggregate_failures do expect(described_class.alt_usage_data(1)).to eq 1 end end + + describe '#redis_usage_data' do + context 'with block given' do + it 'returns the fallback when it gets an error' do + expect(described_class.redis_usage_data { raise ::Redis::CommandError } ).to eq(-1) + end + + it 'returns the evaluated block when given' do + expect(described_class.redis_usage_data { 1 }).to eq(1) + end + end + + context 'with counter given' do + it 'returns the falback values for all counter keys when it gets an error' do + allow(::Gitlab::UsageDataCounters::WikiPageCounter).to receive(:totals).and_raise(::Redis::CommandError) + expect(described_class.redis_usage_data(::Gitlab::UsageDataCounters::WikiPageCounter)).to eql(::Gitlab::UsageDataCounters::WikiPageCounter.fallback_totals) + end + + it 'returns the totals when couter is given' do + allow(::Gitlab::UsageDataCounters::WikiPageCounter).to receive(:totals).and_return({ wiki_pages_create: 2 }) + expect(described_class.redis_usage_data(::Gitlab::UsageDataCounters::WikiPageCounter)).to eql({ wiki_pages_create: 2 }) + end + end + end end |