diff options
Diffstat (limited to 'app/services/metrics')
12 files changed, 196 insertions, 40 deletions
diff --git a/app/services/metrics/dashboard/base_service.rb b/app/services/metrics/dashboard/base_service.rb index c2a0f22e73e..5fa127d64b2 100644 --- a/app/services/metrics/dashboard/base_service.rb +++ b/app/services/metrics/dashboard/base_service.rb @@ -10,7 +10,8 @@ module Metrics STAGES = ::Gitlab::Metrics::Dashboard::Stages SEQUENCE = [ STAGES::CommonMetricsInserter, - STAGES::EndpointInserter, + STAGES::MetricEndpointInserter, + STAGES::VariableEndpointInserter, STAGES::PanelIdsInserter, STAGES::Sorter, STAGES::AlertsInserter, @@ -36,6 +37,14 @@ module Metrics Gitlab::Metrics::Dashboard::Cache.fetch(cache_key) { get_raw_dashboard } end + # Should return true if this dashboard service is for an out-of-the-box + # dashboard. + # This method is overridden in app/services/metrics/dashboard/predefined_dashboard_service.rb. + # @return Boolean + def self.out_of_the_box_dashboard? + false + end + private # Determines whether users should be able to view @@ -83,6 +92,17 @@ module Metrics params[:dashboard_path] end + def load_yaml(data) + ::Gitlab::Config::Loader::Yaml.new(data).load_raw! + rescue Gitlab::Config::Loader::Yaml::NotHashError + # Raise more informative error in app/models/performance_monitoring/prometheus_dashboard.rb. + {} + rescue Gitlab::Config::Loader::Yaml::DataTooLargeError => exception + raise Gitlab::Metrics::Dashboard::Errors::LayoutError, exception.message + rescue Gitlab::Config::Loader::FormatError + raise Gitlab::Metrics::Dashboard::Errors::LayoutError, _('Invalid yaml') + end + # @return [Hash] an unmodified dashboard def get_raw_dashboard raise NotImplementedError diff --git a/app/services/metrics/dashboard/clone_dashboard_service.rb b/app/services/metrics/dashboard/clone_dashboard_service.rb index 3ca25b3bd9b..a6bece391f2 100644 --- a/app/services/metrics/dashboard/clone_dashboard_service.rb +++ b/app/services/metrics/dashboard/clone_dashboard_service.rb @@ -6,30 +6,33 @@ module Metrics module Dashboard class CloneDashboardService < ::BaseService include Stepable + include Gitlab::Utils::StrongMemoize ALLOWED_FILE_TYPE = '.yml' USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT + SEQUENCES = { + ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH => [ + ::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter, + ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter, + ::Gitlab::Metrics::Dashboard::Stages::Sorter + ].freeze, + + ::Metrics::Dashboard::SelfMonitoringDashboardService::DASHBOARD_PATH => [ + ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter + ].freeze, + + ::Metrics::Dashboard::ClusterDashboardService::DASHBOARD_PATH => [ + ::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter, + ::Gitlab::Metrics::Dashboard::Stages::Sorter + ].freeze + }.freeze steps :check_push_authorized, - :check_branch_name, - :check_file_type, - :check_dashboard_template, - :create_file, - :refresh_repository_method_caches - - class << self - def allowed_dashboard_templates - @allowed_dashboard_templates ||= Set[::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH].freeze - end - - def sequences - @sequences ||= { - ::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH => [::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter, - ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter, - ::Gitlab::Metrics::Dashboard::Stages::Sorter].freeze - }.freeze - end - end + :check_branch_name, + :check_file_type, + :check_dashboard_template, + :create_file, + :refresh_repository_method_caches def execute execute_steps @@ -56,8 +59,12 @@ module Metrics success(result) end + # Only allow out of the box metrics dashboards to be cloned. This can be + # changed to allow cloning of any metrics dashboard, if desired. + # However, only metrics dashboards should be allowed. If any file is + # allowed to be cloned, this will become a security risk. def check_dashboard_template(result) - return error(_('Not found.'), :not_found) unless self.class.allowed_dashboard_templates.include?(params[:dashboard]) + return error(_('Not found.'), :not_found) unless dashboard_service&.out_of_the_box_dashboard? success(result) end @@ -78,6 +85,12 @@ module Metrics success(result.merge(http_status: :created, dashboard: dashboard_details)) end + def dashboard_service + strong_memoize(:dashboard_service) do + Gitlab::Metrics::Dashboard::ServiceSelector.call(dashboard_service_options) + end + end + def dashboard_attrs { commit_message: params[:commit_message], @@ -149,14 +162,19 @@ module Metrics end def raw_dashboard - YAML.safe_load(File.read(Rails.root.join(dashboard_template))) + dashboard_service.new(project, current_user, dashboard_service_options).raw_dashboard + end + + def dashboard_service_options + { + embedded: false, + dashboard_path: dashboard_template + } end def sequence - self.class.sequences[dashboard_template] + SEQUENCES[dashboard_template] || [] end end end end - -Metrics::Dashboard::CloneDashboardService.prepend_if_ee('EE::Metrics::Dashboard::CloneDashboardService') diff --git a/app/services/metrics/dashboard/cluster_dashboard_service.rb b/app/services/metrics/dashboard/cluster_dashboard_service.rb new file mode 100644 index 00000000000..bfd5abf1126 --- /dev/null +++ b/app/services/metrics/dashboard/cluster_dashboard_service.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Fetches the system metrics dashboard and formats the output. +# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards. +module Metrics + module Dashboard + class ClusterDashboardService < ::Metrics::Dashboard::PredefinedDashboardService + DASHBOARD_PATH = 'config/prometheus/cluster_metrics.yml' + DASHBOARD_NAME = 'Cluster' + + # SHA256 hash of dashboard content + DASHBOARD_VERSION = '9349afc1d96329c08ab478ea0b77db94ee5cc2549b8c754fba67a7f424666b22' + + SEQUENCE = [ + STAGES::ClusterEndpointInserter, + STAGES::PanelIdsInserter, + STAGES::Sorter + ].freeze + + class << self + def valid_params?(params) + # support selecting this service by cluster id via .find + # Use super to support selecting this service by dashboard_path via .find_raw + (params[:cluster].present? && params[:embedded] != 'true') || super + end + end + + # Permissions are handled at the controller level + def allowed? + true + end + + private + + def dashboard_version + DASHBOARD_VERSION + end + end + end +end diff --git a/app/services/metrics/dashboard/cluster_metrics_embed_service.rb b/app/services/metrics/dashboard/cluster_metrics_embed_service.rb new file mode 100644 index 00000000000..6fb39ed3004 --- /dev/null +++ b/app/services/metrics/dashboard/cluster_metrics_embed_service.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +# +module Metrics + module Dashboard + class ClusterMetricsEmbedService < Metrics::Dashboard::DynamicEmbedService + class << self + def valid_params?(params) + [ + params[:cluster], + embedded?(params[:embedded]), + params[:group].present?, + params[:title].present?, + params[:y_label].present? + ].all? + end + end + + private + + # Permissions are handled at the controller level + def allowed? + true + end + + def dashboard_path + ::Metrics::Dashboard::ClusterDashboardService::DASHBOARD_PATH + end + + def sequence + [ + STAGES::ClusterEndpointInserter, + STAGES::PanelIdsInserter + ] + end + end + end +end diff --git a/app/services/metrics/dashboard/custom_dashboard_service.rb b/app/services/metrics/dashboard/custom_dashboard_service.rb index 77173813a4f..741738cc3af 100644 --- a/app/services/metrics/dashboard/custom_dashboard_service.rb +++ b/app/services/metrics/dashboard/custom_dashboard_service.rb @@ -21,7 +21,8 @@ module Metrics path: filepath, display_name: name_for_path(filepath), default: false, - system_dashboard: false + system_dashboard: false, + out_of_the_box_dashboard: out_of_the_box_dashboard? } end end @@ -42,7 +43,7 @@ module Metrics def get_raw_dashboard yml = self.class.file_finder(project).read(dashboard_path) - YAML.safe_load(yml) + load_yaml(yml) end def cache_key diff --git a/app/services/metrics/dashboard/gitlab_alert_embed_service.rb b/app/services/metrics/dashboard/gitlab_alert_embed_service.rb index 38e89d392ad..08d65413e1d 100644 --- a/app/services/metrics/dashboard/gitlab_alert_embed_service.rb +++ b/app/services/metrics/dashboard/gitlab_alert_embed_service.rb @@ -11,7 +11,7 @@ module Metrics include Gitlab::Utils::StrongMemoize SEQUENCE = [ - STAGES::EndpointInserter, + STAGES::MetricEndpointInserter, STAGES::PanelIdsInserter ].freeze diff --git a/app/services/metrics/dashboard/grafana_metric_embed_service.rb b/app/services/metrics/dashboard/grafana_metric_embed_service.rb index d9ce2c5e905..8e72a185406 100644 --- a/app/services/metrics/dashboard/grafana_metric_embed_service.rb +++ b/app/services/metrics/dashboard/grafana_metric_embed_service.rb @@ -80,7 +80,7 @@ module Metrics def fetch_dashboard uid = GrafanaUidParser.new(grafana_url, project).parse - raise DashboardProcessingError.new('Dashboard uid not found') unless uid + raise DashboardProcessingError.new(_('Dashboard uid not found')) unless uid response = client.get_dashboard(uid: uid) @@ -89,7 +89,7 @@ module Metrics def fetch_datasource(dashboard) name = DatasourceNameParser.new(grafana_url, dashboard).parse - raise DashboardProcessingError.new('Datasource name not found') unless name + raise DashboardProcessingError.new(_('Datasource name not found')) unless name response = client.get_datasource(name: name) @@ -115,7 +115,7 @@ module Metrics def parse_json(json) Gitlab::Json.parse(json, symbolize_names: true) rescue JSON::ParserError - raise DashboardProcessingError.new('Grafana response contains invalid json') + raise DashboardProcessingError.new(_('Grafana response contains invalid json')) end end diff --git a/app/services/metrics/dashboard/pod_dashboard_service.rb b/app/services/metrics/dashboard/pod_dashboard_service.rb index 16b87d2d587..8699189deac 100644 --- a/app/services/metrics/dashboard/pod_dashboard_service.rb +++ b/app/services/metrics/dashboard/pod_dashboard_service.rb @@ -5,6 +5,15 @@ module Metrics class PodDashboardService < ::Metrics::Dashboard::PredefinedDashboardService DASHBOARD_PATH = 'config/prometheus/pod_metrics.yml' DASHBOARD_NAME = 'Pod Health' + + # SHA256 hash of dashboard content + DASHBOARD_VERSION = 'f12f641d2575d5dcb69e2c633ff5231dbd879ad35020567d8fc4e1090bfdb4b4' + + private + + def dashboard_version + DASHBOARD_VERSION + end end end end diff --git a/app/services/metrics/dashboard/predefined_dashboard_service.rb b/app/services/metrics/dashboard/predefined_dashboard_service.rb index f454df63773..c21083475f0 100644 --- a/app/services/metrics/dashboard/predefined_dashboard_service.rb +++ b/app/services/metrics/dashboard/predefined_dashboard_service.rb @@ -10,7 +10,8 @@ module Metrics DASHBOARD_NAME = nil SEQUENCE = [ - STAGES::EndpointInserter, + STAGES::MetricEndpointInserter, + STAGES::VariableEndpointInserter, STAGES::PanelIdsInserter, STAGES::Sorter ].freeze @@ -23,12 +24,20 @@ module Metrics def matching_dashboard?(filepath) filepath == self::DASHBOARD_PATH end + + def out_of_the_box_dashboard? + true + end end private + def dashboard_version + raise NotImplementedError + end + def cache_key - "metrics_dashboard_#{dashboard_path}" + "metrics_dashboard_#{dashboard_path}_#{dashboard_version}" end def dashboard_path @@ -39,7 +48,7 @@ module Metrics def get_raw_dashboard yml = File.read(Rails.root.join(dashboard_path)) - YAML.safe_load(yml) + load_yaml(yml) end def sequence diff --git a/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb b/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb index 8599c23c206..f1f5cd7d77e 100644 --- a/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb +++ b/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb @@ -8,9 +8,13 @@ module Metrics DASHBOARD_PATH = 'config/prometheus/self_monitoring_default.yml' DASHBOARD_NAME = N_('Default dashboard') + # SHA256 hash of dashboard content + DASHBOARD_VERSION = '1dff3e3cb76e73c8e368823c98b34c61aec0d141978450dea195a3b3dc2415d6' + SEQUENCE = [ STAGES::CustomMetricsInserter, - STAGES::EndpointInserter, + STAGES::MetricEndpointInserter, + STAGES::VariableEndpointInserter, STAGES::PanelIdsInserter, STAGES::Sorter ].freeze @@ -25,7 +29,8 @@ module Metrics path: DASHBOARD_PATH, display_name: _(DASHBOARD_NAME), default: true, - system_dashboard: false + system_dashboard: false, + out_of_the_box_dashboard: out_of_the_box_dashboard? }] end @@ -33,6 +38,12 @@ module Metrics params[:dashboard_path].nil? && params[:environment]&.project&.self_monitoring? end end + + private + + def dashboard_version + DASHBOARD_VERSION + end end end end diff --git a/app/services/metrics/dashboard/system_dashboard_service.rb b/app/services/metrics/dashboard/system_dashboard_service.rb index db5599b4def..5c3562b8ca0 100644 --- a/app/services/metrics/dashboard/system_dashboard_service.rb +++ b/app/services/metrics/dashboard/system_dashboard_service.rb @@ -8,11 +8,15 @@ module Metrics DASHBOARD_PATH = 'config/prometheus/common_metrics.yml' DASHBOARD_NAME = N_('Default dashboard') + # SHA256 hash of dashboard content + DASHBOARD_VERSION = '4685fe386c25b1a786b3be18f79bb2ee9828019003e003816284cdb634fa3e13' + SEQUENCE = [ STAGES::CommonMetricsInserter, STAGES::CustomMetricsInserter, STAGES::CustomMetricsDetailsInserter, - STAGES::EndpointInserter, + STAGES::MetricEndpointInserter, + STAGES::VariableEndpointInserter, STAGES::PanelIdsInserter, STAGES::Sorter, STAGES::AlertsInserter @@ -24,10 +28,17 @@ module Metrics path: DASHBOARD_PATH, display_name: _(DASHBOARD_NAME), default: true, - system_dashboard: true + system_dashboard: true, + out_of_the_box_dashboard: out_of_the_box_dashboard? }] end end + + private + + def dashboard_version + DASHBOARD_VERSION + end end end end diff --git a/app/services/metrics/dashboard/transient_embed_service.rb b/app/services/metrics/dashboard/transient_embed_service.rb index cb6ca215447..0a9c4bc7b86 100644 --- a/app/services/metrics/dashboard/transient_embed_service.rb +++ b/app/services/metrics/dashboard/transient_embed_service.rb @@ -30,7 +30,7 @@ module Metrics override :sequence def sequence - [STAGES::EndpointInserter] + [STAGES::MetricEndpointInserter] end override :identifiers @@ -39,7 +39,7 @@ module Metrics end def invalid_embed_json!(message) - raise DashboardProcessingError.new("Parsing error for param :embed_json. #{message}") + raise DashboardProcessingError.new(_("Parsing error for param :embed_json. %{message}") % { message: message }) end end end |