diff options
Diffstat (limited to 'app/services/prometheus')
-rw-r--r-- | app/services/prometheus/proxy_service.rb | 145 | ||||
-rw-r--r-- | app/services/prometheus/proxy_variable_substitution_service.rb | 155 |
2 files changed, 0 insertions, 300 deletions
diff --git a/app/services/prometheus/proxy_service.rb b/app/services/prometheus/proxy_service.rb deleted file mode 100644 index 33635796771..00000000000 --- a/app/services/prometheus/proxy_service.rb +++ /dev/null @@ -1,145 +0,0 @@ -# frozen_string_literal: true - -module Prometheus - class ProxyService < BaseService - include ReactiveCaching - include Gitlab::Utils::StrongMemoize - - self.reactive_cache_key = ->(service) { [] } - self.reactive_cache_lease_timeout = 30.seconds - - # reactive_cache_refresh_interval should be set to a value higher than - # reactive_cache_lifetime. If the refresh_interval is less than lifetime - # then the ReactiveCachingWorker will re-query prometheus for this - # PromQL query even though it's (probably) already been picked up by - # the frontend - # refresh_interval should be set less than lifetime only if this data - # is expected to change *and* be fetched again by the frontend - self.reactive_cache_refresh_interval = 90.seconds - self.reactive_cache_lifetime = 1.minute - self.reactive_cache_work_type = :external_dependency - self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } - - attr_accessor :proxyable, :method, :path, :params - - PROMETHEUS_QUERY_API = 'query' - PROMETHEUS_QUERY_RANGE_API = 'query_range' - PROMETHEUS_SERIES_API = 'series' - - PROXY_SUPPORT = { - PROMETHEUS_QUERY_API => { - method: ['GET'], - params: %w(query time timeout) - }, - PROMETHEUS_QUERY_RANGE_API => { - method: ['GET'], - params: %w(query start end step timeout) - }, - PROMETHEUS_SERIES_API => { - method: %w(GET), - params: %w(match start end) - } - }.freeze - - def self.from_cache(proxyable_class_name, proxyable_id, method, path, params) - proxyable_class = begin - proxyable_class_name.constantize - rescue NameError - nil - end - return unless proxyable_class - - proxyable = proxyable_class.find(proxyable_id) - - new(proxyable, method, path, params) - end - - # proxyable can be any model which responds to .prometheus_adapter - # like Environment. - def initialize(proxyable, method, path, params) - @proxyable = proxyable - @path = path - - # Convert ActionController::Parameters to hash because reactive_cache_worker - # does not play nice with ActionController::Parameters. - @params = filter_params(params, path).to_hash - - @method = method - end - - def id - nil - end - - def execute - return cannot_proxy_response unless can_proxy? - return no_prometheus_response unless can_query? - - with_reactive_cache(*cache_key) do |result| - result - end - end - - def calculate_reactive_cache(proxyable_class_name, proxyable_id, method, path, params) - return no_prometheus_response unless can_query? - - response = prometheus_client_wrapper.proxy(path, params) - - success(http_status: response.code, body: response.body) - rescue Gitlab::PrometheusClient::Error => err - service_unavailable_response(err) - end - - def cache_key - [@proxyable.class.name, @proxyable.id, @method, @path, @params] - end - - private - - def service_unavailable_response(exception) - error(exception.message, :service_unavailable) - end - - def no_prometheus_response - error('No prometheus server found', :service_unavailable) - end - - def cannot_proxy_response - error('Proxy support for this API is not available currently') - end - - def prometheus_adapter - strong_memoize(:prometheus_adapter) do - @proxyable.prometheus_adapter - end - end - - def prometheus_client_wrapper - prometheus_adapter&.prometheus_client - end - - def can_query? - prometheus_adapter&.can_query? - end - - def filter_params(params, path) - params = substitute_params(params) - - params.slice(*PROXY_SUPPORT.dig(path, :params)) - end - - def can_proxy? - PROXY_SUPPORT.dig(@path, :method)&.include?(@method) - end - - def substitute_params(params) - start_time = params[:start_time] - end_time = params[:end_time] - - params['start'] = start_time if start_time - params['end'] = end_time if end_time - - params - end - end -end diff --git a/app/services/prometheus/proxy_variable_substitution_service.rb b/app/services/prometheus/proxy_variable_substitution_service.rb deleted file mode 100644 index 846dfeb33ce..00000000000 --- a/app/services/prometheus/proxy_variable_substitution_service.rb +++ /dev/null @@ -1,155 +0,0 @@ -# frozen_string_literal: true - -module Prometheus - class ProxyVariableSubstitutionService < BaseService - include Stepable - - VARIABLE_INTERPOLATION_REGEX = / - {{ # Variable needs to be wrapped in these chars. - \s* # Allow whitespace before and after the variable name. - (?<variable> # Named capture. - \w+ # Match one or more word characters. - ) - \s* - }} - /x.freeze - - steps :validate_variables, - :add_params_to_result, - :substitute_params, - :substitute_variables - - # @param environment [Environment] - # @param params [Hash<Symbol,Any>] - # @param params - query [String] The Prometheus query string. - # @param params - start [String] (optional) A time string in the rfc3339 format. - # @param params - start_time [String] (optional) A time string in the rfc3339 format. - # @param params - end [String] (optional) A time string in the rfc3339 format. - # @param params - end_time [String] (optional) A time string in the rfc3339 format. - # @param params - variables [ActionController::Parameters] (optional) Variables with their values. - # The keys in the Hash should be the name of the variable. The value should be the value of the - # variable. Ex: `ActionController::Parameters.new(variable1: 'value 1', variable2: 'value 2').permit!` - # @return [Prometheus::ProxyVariableSubstitutionService] - # - # Example: - # Prometheus::ProxyVariableSubstitutionService.new(environment, { - # params: { - # start_time: '2020-07-03T06:08:36Z', - # end_time: '2020-07-03T14:08:52Z', - # query: 'up{instance="{{instance}}"}', - # variables: { instance: 'srv1' } - # } - # }) - def initialize(environment, params = {}) - @environment = environment - @params = params.deep_dup - end - - # @return - params [Hash<Symbol,Any>] Returns a Hash containing a params key which is - # similar to the `params` that is passed to the initialize method with 2 differences: - # 1. Variables in the query string are substituted with their values. - # If a variable present in the query string has no known value (values - # are obtained from the `variables` Hash in `params` or from - # `Gitlab::Prometheus::QueryVariables.call`), it will not be substituted. - # 2. `start` and `end` keys are added, with their values copied from `start_time` - # and `end_time`. - # - # Example output: - # - # { - # params: { - # start_time: '2020-07-03T06:08:36Z', - # start: '2020-07-03T06:08:36Z', - # end_time: '2020-07-03T14:08:52Z', - # end: '2020-07-03T14:08:52Z', - # query: 'up{instance="srv1"}', - # variables: { instance: 'srv1' } - # } - # } - def execute - execute_steps - end - - private - - def validate_variables(_result) - return success unless variables - - unless variables.is_a?(ActionController::Parameters) - return error(_('Optional parameter "variables" must be a Hash. Ex: variables[key1]=value1')) - end - - success - end - - def add_params_to_result(result) - result[:params] = params - - success(result) - end - - def substitute_params(result) - start_time = result[:params][:start_time] - end_time = result[:params][:end_time] - - result[:params][:start] = start_time if start_time - result[:params][:end] = end_time if end_time - - success(result) - end - - def substitute_variables(result) - return success(result) unless query(result) - - result[:params][:query] = gsub(query(result), full_context(result)) - - success(result) - end - - def gsub(string, context) - # Search for variables of the form `{{variable}}` in the string and replace - # them with their value. - string.gsub(VARIABLE_INTERPOLATION_REGEX) do |match| - # Replace with the value of the variable, or if there is no such variable, - # replace the invalid variable with itself. So, - # `up{instance="{{invalid_variable}}"}` will remain - # `up{instance="{{invalid_variable}}"}` after substitution. - context.fetch($~[:variable], match) - end - end - - def predefined_context(result) - Gitlab::Prometheus::QueryVariables.call( - @environment, - start_time: start_timestamp(result), - end_time: end_timestamp(result) - ).stringify_keys - end - - def full_context(result) - @full_context ||= predefined_context(result).reverse_merge(variables_hash) - end - - def variables - params[:variables] - end - - def variables_hash - variables.to_h - end - - def start_timestamp(result) - Time.rfc3339(result[:params][:start]) - rescue ArgumentError - end - - def end_timestamp(result) - Time.rfc3339(result[:params][:end]) - rescue ArgumentError - end - - def query(result) - result[:params][:query] - end - end -end |