1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
# frozen_string_literal: true
module Prometheus
class ProxyService < BaseService
include ReactiveCaching
self.reactive_cache_key = ->(service) { service.cache_key }
self.reactive_cache_lease_timeout = 30.seconds
self.reactive_cache_refresh_interval = 30.seconds
self.reactive_cache_lifetime = 1.minute
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
attr_accessor :prometheus_owner, :method, :path, :params
PROXY_SUPPORT = {
'query' => 'GET',
'query_range' => 'GET'
}.freeze
def self.from_cache(prometheus_owner_class_name, prometheus_owner_id, method, path, params)
prometheus_owner_class = begin
prometheus_owner_class_name.constantize
rescue NameError
nil
end
return unless prometheus_owner_class
prometheus_owner = prometheus_owner_class.find(prometheus_owner_id)
new(prometheus_owner, method, path, params)
end
# prometheus_owner can be any model which responds to .prometheus_adapter
# like Environment.
def initialize(prometheus_owner, method, path, params)
@prometheus_owner = prometheus_owner
@path = path
# Convert ActionController::Parameters to hash because reactive_cache_worker
# does not play nice with ActionController::Parameters.
@params = params.to_hash
@method = method
end
def id
nil
end
def execute
return cannot_proxy_response unless can_proxy?(@method, @path)
return no_prometheus_response unless can_query?
with_reactive_cache(*cache_key) do |result|
result
end
end
def calculate_reactive_cache(prometheus_owner_class_name, prometheus_owner_id, method, path, params)
@prometheus_owner = prometheus_owner_from_class(prometheus_owner_class_name, prometheus_owner_id)
return cannot_proxy_response unless can_proxy?(method, path)
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
error(err.message, :service_unavailable)
end
def cache_key
[@prometheus_owner.class.name, @prometheus_owner.id, @method, @path, @params]
end
private
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_owner_from_class(prometheus_owner_class_name, prometheus_owner_id)
Kernel.const_get(prometheus_owner_class_name).find(prometheus_owner_id)
end
def prometheus_adapter
@prometheus_adapter ||= @prometheus_owner.prometheus_adapter
end
def prometheus_client_wrapper
prometheus_adapter&.prometheus_client_wrapper
end
def can_query?
prometheus_adapter&.can_query?
end
def can_proxy?(method, path)
PROXY_SUPPORT[path] == method
end
end
end
|