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