diff options
author | Sarah Yasonik <syasonik@gitlab.com> | 2019-05-01 10:16:03 +0000 |
---|---|---|
committer | Sean McGivern <sean@gitlab.com> | 2019-05-01 10:16:03 +0000 |
commit | 552a3d2fd939d3f8a56cfad9fad62227c1d5aa89 (patch) | |
tree | d10ce5f4615b0e9dbeb7543736984b3e3ff10dbb /spec | |
parent | d7b75b661f8ed2468a322c4ae55eadcbdb3b2615 (diff) | |
download | gitlab-ce-552a3d2fd939d3f8a56cfad9fad62227c1d5aa89.tar.gz |
Update metrics dashboard API to load yml from repo
Updates the EnvironmentController#metrics_dashboard endpoint
to support a "dashboard" param, which can be used to specify
the filepath of a dashboard configuration from a project
repository. Dashboard configurations are expected to be
stored in .gitlab/dashboards/.
Updates dashboard post-processing steps to exclude custom
metrics, which should only display on the system dashboard.
Diffstat (limited to 'spec')
9 files changed, 302 insertions, 56 deletions
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index a62422d0229..cf23d937037 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -474,25 +474,102 @@ describe Projects::EnvironmentsController do end end - context 'when prometheus endpoint is enabled' do + shared_examples_for '200 response' do |contains_all_dashboards: false| + let(:expected_keys) { %w(dashboard status) } + + before do + expected_keys << 'all_dashboards' if contains_all_dashboards + end + it 'returns a json representation of the environment dashboard' do - get :metrics_dashboard, params: environment_params(format: :json) + get :metrics_dashboard, params: environment_params(dashboard_params) expect(response).to have_gitlab_http_status(:ok) - expect(json_response.keys).to contain_exactly('dashboard', 'status') + expect(json_response.keys).to contain_exactly(*expected_keys) expect(json_response['dashboard']).to be_an_instance_of(Hash) end + end + + shared_examples_for 'error response' do |status_code, contains_all_dashboards: false| + let(:expected_keys) { %w(message status) } + + before do + expected_keys << 'all_dashboards' if contains_all_dashboards + end + + it 'returns an error response' do + get :metrics_dashboard, params: environment_params(dashboard_params) + + expect(response).to have_gitlab_http_status(status_code) + expect(json_response.keys).to contain_exactly(*expected_keys) + end + end + + shared_examples_for 'has all dashboards' do + it 'includes an index of all available dashboards' do + get :metrics_dashboard, params: environment_params(dashboard_params) + + expect(json_response.keys).to include('all_dashboards') + expect(json_response['all_dashboards']).to be_an_instance_of(Array) + expect(json_response['all_dashboards']).to all( include('path', 'default') ) + end + end + + context 'when multiple dashboards is disabled' do + before do + stub_feature_flags(environment_metrics_show_multiple_dashboards: false) + end + + let(:dashboard_params) { { format: :json } } + + it_behaves_like '200 response' context 'when the dashboard could not be provided' do before do allow(YAML).to receive(:safe_load).and_return({}) end - it 'returns an error response' do - get :metrics_dashboard, params: environment_params(format: :json) + it_behaves_like 'error response', :unprocessable_entity + end + + context 'when a dashboard param is specified' do + let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/not_there_dashboard.yml' } } + + it_behaves_like '200 response' + end + end + + context 'when multiple dashboards is enabled' do + let(:dashboard_params) { { format: :json } } + + it_behaves_like '200 response', contains_all_dashboards: true + it_behaves_like 'has all dashboards' + + context 'when a dashboard could not be provided' do + before do + allow(YAML).to receive(:safe_load).and_return({}) + end + + it_behaves_like 'error response', :unprocessable_entity, contains_all_dashboards: true + it_behaves_like 'has all dashboards' + end + + context 'when a dashboard param is specified' do + let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/test.yml' } } + + context 'when the dashboard is available' do + let(:dashboard_yml) { fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml') } + let(:dashboard_file) { { '.gitlab/dashboards/test.yml' => dashboard_yml } } + let(:project) { create(:project, :custom_repo, files: dashboard_file) } + let(:environment) { create(:environment, name: 'production', project: project) } + + it_behaves_like '200 response', contains_all_dashboards: true + it_behaves_like 'has all dashboards' + end - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response.keys).to contain_exactly('message', 'status', 'http_status') + context 'when the dashboard does not exist' do + it_behaves_like 'error response', :not_found, contains_all_dashboards: true + it_behaves_like 'has all dashboards' end end end diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml b/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml index c2d3d3d8aca..638ecbcc11f 100644 --- a/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml @@ -2,7 +2,7 @@ dashboard: 'Test Dashboard' priority: 1 panel_groups: - group: Group A - priority: 10 + priority: 1 panels: - title: "Super Chart A1" type: "area-chart" @@ -23,7 +23,7 @@ panel_groups: label: Legend Label unit: unit - group: Group B - priority: 1 + priority: 10 panels: - title: "Super Chart B" type: "area-chart" diff --git a/spec/lib/gitlab/metrics/dashboard/finder_spec.rb b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb new file mode 100644 index 00000000000..e88eb140b35 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/finder_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_caching do + include MetricsDashboardHelpers + + set(:project) { build(:project) } + set(:environment) { build(:environment, project: project) } + let(:system_dashboard_path) { Gitlab::Metrics::Dashboard::SystemDashboardService::SYSTEM_DASHBOARD_PATH} + + describe '.find' do + let(:dashboard_path) { '.gitlab/dashboards/test.yml' } + let(:service_call) { described_class.find(project, nil, environment, dashboard_path) } + + it_behaves_like 'misconfigured dashboard service response', :not_found + + context 'when the dashboard exists' do + let(:project) { project_with_dashboard(dashboard_path) } + + it_behaves_like 'valid dashboard service response' + end + + context 'when the dashboard is configured incorrectly' do + let(:project) { project_with_dashboard(dashboard_path, {}) } + + it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity + end + + context 'when the system dashboard is specified' do + let(:dashboard_path) { system_dashboard_path } + + it_behaves_like 'valid dashboard service response' + end + + context 'when no dashboard is specified' do + let(:service_call) { described_class.find(project, nil, environment) } + + it_behaves_like 'valid dashboard service response' + end + end + + describe '.find_all_paths' do + let(:all_dashboard_paths) { described_class.find_all_paths(project) } + let(:system_dashboard) { { path: system_dashboard_path, default: true } } + + it 'includes only the system dashboard by default' do + expect(all_dashboard_paths).to eq([system_dashboard]) + end + + context 'when the project contains dashboards' do + let(:dashboard_path) { '.gitlab/dashboards/test.yml' } + let(:project) { project_with_dashboard(dashboard_path) } + + it 'includes system and project dashboards' do + project_dashboard = { path: dashboard_path, default: false } + + expect(all_dashboard_paths).to contain_exactly(system_dashboard, project_dashboard) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb index ee7c93fce8d..be3c1095bd7 100644 --- a/spec/lib/gitlab/metrics/dashboard/processor_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb @@ -4,12 +4,12 @@ require 'spec_helper' describe Gitlab::Metrics::Dashboard::Processor do let(:project) { build(:project) } - let(:environment) { build(:environment) } + let(:environment) { build(:environment, project: project) } let(:dashboard_yml) { YAML.load_file('spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml') } describe 'process' do let(:process_params) { [project, environment, dashboard_yml] } - let(:dashboard) { described_class.new(*process_params).process } + let(:dashboard) { described_class.new(*process_params).process(insert_project_metrics: true) } context 'when dashboard config corresponds to common metrics' do let!(:common_metric) { create(:prometheus_metric, :common, identifier: 'metric_a1') } @@ -35,9 +35,9 @@ describe Gitlab::Metrics::Dashboard::Processor do it 'orders groups by priority and panels by weight' do expected_metrics_order = [ - 'metric_a2', # group priority 10, panel weight 2 - 'metric_a1', # group priority 10, panel weight 1 - 'metric_b', # group priority 1, panel weight 1 + 'metric_b', # group priority 10, panel weight 1 + 'metric_a2', # group priority 1, panel weight 2 + 'metric_a1', # group priority 1, panel weight 1 project_business_metric.id, # group priority 0, panel weight nil (0) project_response_metric.id, # group priority -5, panel weight nil (0) project_system_metric.id, # group priority -10, panel weight nil (0) @@ -46,6 +46,17 @@ describe Gitlab::Metrics::Dashboard::Processor do expect(actual_metrics_order).to eq expected_metrics_order end + + context 'when the dashboard should not include project metrics' do + let(:dashboard) { described_class.new(*process_params).process(insert_project_metrics: false) } + + it 'includes only dashboard metrics' do + metrics = all_metrics.map { |m| m[:id] } + + expect(metrics.length).to be(3) + expect(metrics).to eq %w(metric_b metric_a2 metric_a1) + end + end end shared_examples_for 'errors with message' do |expected_message| diff --git a/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb new file mode 100644 index 00000000000..162beb0268a --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/project_dashboard_service_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Gitlab::Metrics::Dashboard::ProjectDashboardService, :use_clean_rails_memory_store_caching do + include MetricsDashboardHelpers + + set(:user) { build(:user) } + set(:project) { build(:project) } + set(:environment) { build(:environment, project: project) } + + before do + project.add_maintainer(user) + end + + describe 'get_dashboard' do + let(:dashboard_path) { '.gitlab/dashboards/test.yml' } + let(:service_params) { [project, user, { environment: environment, dashboard_path: dashboard_path }] } + let(:service_call) { described_class.new(*service_params).get_dashboard } + + context 'when the dashboard does not exist' do + it_behaves_like 'misconfigured dashboard service response', :not_found + end + + context 'when the dashboard exists' do + let(:project) { project_with_dashboard(dashboard_path) } + + it_behaves_like 'valid dashboard service response' + + it 'caches the unprocessed dashboard for subsequent calls' do + expect_any_instance_of(described_class) + .to receive(:get_raw_dashboard) + .once + .and_call_original + + described_class.new(*service_params).get_dashboard + described_class.new(*service_params).get_dashboard + end + + context 'and the dashboard is then deleted' do + it 'does not return the previously cached dashboard' do + described_class.new(*service_params).get_dashboard + + delete_project_dashboard(project, user, dashboard_path) + + expect_any_instance_of(described_class) + .to receive(:get_raw_dashboard) + .once + .and_call_original + + described_class.new(*service_params).get_dashboard + end + end + end + + context 'when the dashboard is configured incorrectly' do + let(:project) { project_with_dashboard(dashboard_path, {}) } + + it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/service_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_spec.rb deleted file mode 100644 index e66c356bf49..00000000000 --- a/spec/lib/gitlab/metrics/dashboard/service_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Metrics::Dashboard::Service, :use_clean_rails_memory_store_caching do - let(:project) { build(:project) } - let(:environment) { build(:environment) } - - describe 'get_dashboard' do - let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) } - - it 'returns a json representation of the environment dashboard' do - result = described_class.new(project, environment).get_dashboard - - expect(result.keys).to contain_exactly(:dashboard, :status) - expect(result[:status]).to eq(:success) - - expect(JSON::Validator.fully_validate(dashboard_schema, result[:dashboard])).to be_empty - end - - it 'caches the dashboard for subsequent calls' do - expect(YAML).to receive(:safe_load).once.and_call_original - - described_class.new(project, environment).get_dashboard - described_class.new(project, environment).get_dashboard - end - - context 'when the dashboard is configured incorrectly' do - before do - allow(YAML).to receive(:safe_load).and_return({}) - end - - it 'returns an appropriate message and status code' do - result = described_class.new(project, environment).get_dashboard - - expect(result.keys).to contain_exactly(:message, :http_status, :status) - expect(result[:status]).to eq(:error) - expect(result[:http_status]).to eq(:unprocessable_entity) - end - end - end -end diff --git a/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb b/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb new file mode 100644 index 00000000000..e71ce2481a3 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/system_dashboard_service_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Metrics::Dashboard::SystemDashboardService, :use_clean_rails_memory_store_caching do + include MetricsDashboardHelpers + + set(:project) { build(:project) } + set(:environment) { build(:environment, project: project) } + + describe 'get_dashboard' do + let(:dashboard_path) { described_class::SYSTEM_DASHBOARD_PATH } + let(:service_params) { [project, nil, { environment: environment, dashboard_path: dashboard_path }] } + let(:service_call) { described_class.new(*service_params).get_dashboard } + + it_behaves_like 'valid dashboard service response' + + it 'caches the unprocessed dashboard for subsequent calls' do + expect(YAML).to receive(:safe_load).once.and_call_original + + described_class.new(*service_params).get_dashboard + described_class.new(*service_params).get_dashboard + end + + context 'when called with a non-system dashboard' do + let(:dashboard_path) { 'garbage/dashboard/path' } + + # We want to alwaus return the system dashboard. + it_behaves_like 'valid dashboard service response' + end + end +end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 43ec1125087..a6c3d5756aa 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1637,6 +1637,7 @@ describe Repository do :has_visible_content?, :issue_template_names, :merge_request_template_names, + :metrics_dashboard_paths, :xcode_project? ]) diff --git a/spec/support/helpers/metrics_dashboard_helpers.rb b/spec/support/helpers/metrics_dashboard_helpers.rb new file mode 100644 index 00000000000..1f36b0e217c --- /dev/null +++ b/spec/support/helpers/metrics_dashboard_helpers.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module MetricsDashboardHelpers + def project_with_dashboard(dashboard_path, dashboard_yml = nil) + dashboard_yml ||= fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml') + + create(:project, :custom_repo, files: { dashboard_path => dashboard_yml }) + end + + def delete_project_dashboard(project, user, dashboard_path) + project.repository.delete_file( + user, + dashboard_path, + branch_name: 'master', + message: 'Delete dashboard' + ) + + project.repository.refresh_method_caches([:metrics_dashboard]) + end + + shared_examples_for 'misconfigured dashboard service response' do |status_code| + it 'returns an appropriate message and status code' do + result = service_call + + expect(result.keys).to contain_exactly(:message, :http_status, :status) + expect(result[:status]).to eq(:error) + expect(result[:http_status]).to eq(status_code) + end + end + + shared_examples_for 'valid dashboard service response' do + let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) } + + it 'returns a json representation of the dashboard' do + result = service_call + + expect(result.keys).to contain_exactly(:dashboard, :status) + expect(result[:status]).to eq(:success) + + expect(JSON::Validator.fully_validate(dashboard_schema, result[:dashboard])).to be_empty + end + end +end |