lib/release_tools/prometheus/query.rb (94 lines of code) (raw):

# frozen_string_literal: true module ReleaseTools module Prometheus class Query include ::SemanticLogger::Loggable class PromQLError < StandardError attr_reader :status_code, :body def initialize(status_code, body) @status_code = status_code @body = body end def to_s "API failure. Status code: #{status_code}, body: #{body}" end end ENDPOINT = "#{ENV.fetch('MIMIR_URL', nil)}/api/v1/query".freeze GITLAB_GPRD_TENANT_ID = 'gitlab-gprd' GITLAB_GSTG_TENANT_ID = 'gitlab-gstg' GITLAB_OPS_TENANT_ID = 'gitlab-ops' # NOTE: Use Gitaly nodes since everything else is deployed via K8S # See https://gitlab.com/gitlab-com/gl-infra/delivery/-/issues/2169 # rubocop:disable Layout/HashAlignment ROLE_VERSIONS = { 'gprd' => ['component="gitlab-rails", type="gitaly", env="gprd", stage="main"', GITLAB_GPRD_TENANT_ID], 'gprd-cny' => ['component="gitlab-rails", type="gitaly", env="gprd", stage="cny"', GITLAB_GPRD_TENANT_ID], 'gstg' => ['component="gitlab-rails", type="gitaly", env="gstg", stage="main"', GITLAB_GSTG_TENANT_ID], 'gstg-cny' => ['component="gitlab-rails", type="gitaly", env="gstg", stage="cny"', GITLAB_GSTG_TENANT_ID], 'gstg-ref' => ['component="gitlab-rails", type="gitaly", env="gstg", stage="ref"', GITLAB_GSTG_TENANT_ID] }.freeze # rubocop:enable Layout/HashAlignment def run(query, tenant_id = GITLAB_GPRD_TENANT_ID) logger.trace('PromQL query', query: query) response = Retriable.retriable { get(query, tenant_id) } response.parse end def rails_version_by_role return {} unless ENV['MIMIR_URL'] ROLE_VERSIONS.each_with_object({}) do |(role, params), memo| selector, tenant_id = params query = "topk(1, count(gitlab_version_info{#{selector}}) by (version))" begin response = run(query, tenant_id) # jq '.data .result | .[] | .metric' metric = response.dig('data', 'result', 0, 'metric') next memo[role] = '' if metric.nil? memo[role] = metric['version'] rescue PromQLError next memo[role] = '' end end end def monthly_release_status(version) params = %{version="#{version}"} query = "last_over_time(delivery_release_monthly_status{#{params}}[30m])" metric_value_from_query(query) end def patch_release_status(versions) params = %{versions="#{versions}"} query = "last_over_time(delivery_release_patch_status{#{params}}[30m])" metric_value_from_query(query) end private def metric_value_from_query(query) return nil unless ENV['MIMIR_URL'] # The delivery metrics services runs in ops, so tenant_id for release metrics needs to be 'gitlab-ops', not the default 'gitlab-gprd' response = run(query, GITLAB_OPS_TENANT_ID) # jq '.data .result | .[] | .value[1]' value = response.dig('data', 'result', 0, 'value', 1) logger.info( 'Received metric value from query', query: query, value: value ) value&.to_i end def get(query, tenant_id) response = client(tenant_id).get(ENDPOINT, params: { query: query }) unless response.status.success? error = PromQLError.new(response.status.code, response.body.to_s) logger.warn('API Failure', status: error.status_code.to_s, error: error.body) raise error end response end def client(tenant_id) HTTP.headers(mimir_headers(tenant_id)) end def mimir_headers(tenant_id) {}.tap do |headers| headers['X-Scope-OrgID'] = tenant_id headers['Authorization'] = "Basic #{Base64.strict_encode64("#{mimir_api_user}:#{mimir_api_password}")}" end end def mimir_api_user ENV.fetch('MIMIR_API_USER', nil) end def mimir_api_password ENV.fetch('MIMIR_API_PASSWORD', nil) end end end end