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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
# frozen_string_literal: true
RSpec.shared_examples 'misconfigured dashboard service response' do |status_code, message = nil|
it 'returns an appropriate message and status code', :aggregate_failures do
result = service_call
expect(result.keys).to contain_exactly(:message, :http_status, :status)
expect(result[:status]).to eq(:error)
expect(result[:http_status]).to eq(status_code)
expect(result[:message]).to eq(message) if message
end
end
RSpec.shared_examples 'valid dashboard service response for schema' do
file_ref_resolver = proc do |uri|
file = Rails.root.join(uri.path)
raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
Gitlab::Json.parse(File.read(file))
end
it 'returns a json representation of the dashboard' do
result = service_call
expect(result.keys).to contain_exactly(:dashboard, :status)
expect(result[:status]).to eq(:success)
validator = JSONSchemer.schema(dashboard_schema, ref_resolver: file_ref_resolver)
expect(validator.valid?(result[:dashboard].with_indifferent_access)).to be true
end
end
RSpec.shared_examples 'valid dashboard service response' do
let(:dashboard_schema) { Gitlab::Json.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) }
it_behaves_like 'valid dashboard service response for schema'
end
RSpec.shared_examples 'caches the unprocessed dashboard for subsequent calls' do
specify do
expect_next_instance_of(::Gitlab::Config::Loader::Yaml) do |loader|
expect(loader).to receive(:load_raw!).once.and_call_original
end
described_class.new(*service_params).get_dashboard
described_class.new(*service_params).get_dashboard
end
end
# This spec is applicable for predefined/out-of-the-box dashboard services.
RSpec.shared_examples 'refreshes cache when dashboard_version is changed' do
specify do
allow_next_instance_of(described_class) do |service|
allow(service).to receive(:dashboard_version).and_return('1', '2')
end
expect_file_read(Rails.root.join(described_class::DASHBOARD_PATH)).twice.and_call_original
service = described_class.new(*service_params)
service.get_dashboard
service.get_dashboard
end
end
# This spec is applicable for predefined/out-of-the-box dashboard services.
# This shared_example requires the following variables to be defined:
# dashboard_path: Relative path to the dashboard, ex: 'config/prometheus/common_metrics.yml'
# dashboard_version: The version string used in the cache_key.
RSpec.shared_examples 'dashboard_version contains SHA256 hash of dashboard file content' do
specify do
dashboard = File.read(Rails.root.join(dashboard_path))
expect(dashboard_version).to eq(Digest::SHA256.hexdigest(dashboard))
end
end
RSpec.shared_examples 'valid embedded dashboard service response' do
let(:dashboard_schema) { Gitlab::Json.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json')) }
it_behaves_like 'valid dashboard service response for schema'
end
RSpec.shared_examples 'raises error for users with insufficient permissions' do
context 'when the user does not have sufficient access' do
let(:user) { build(:user) }
it_behaves_like 'misconfigured dashboard service response', :unauthorized
end
context 'when the user is anonymous' do
let(:user) { nil }
it_behaves_like 'misconfigured dashboard service response', :unauthorized
end
end
RSpec.shared_examples 'valid dashboard cloning process' do |dashboard_template, sequence|
context "dashboard template: #{dashboard_template}" do
let(:dashboard) { dashboard_template }
let(:dashboard_attrs) do
{
commit_message: commit_message,
branch_name: branch,
start_branch: project.default_branch,
encoding: 'text',
file_path: ".gitlab/dashboards/#{file_name}",
file_content: file_content_hash.to_yaml
}
end
it 'delegates commit creation to Files::CreateService', :aggregate_failures do
service_instance = instance_double(::Files::CreateService)
allow(::Gitlab::Metrics::Dashboard::Processor).to receive(:new).and_return(double(process: file_content_hash))
expect(::Files::CreateService).to receive(:new).with(project, user, dashboard_attrs).and_return(service_instance)
expect(service_instance).to receive(:execute).and_return(status: :success)
service_call
end
context 'user has defined custom metrics' do
it 'uses external service to includes them into new file content', :aggregate_failures do
service_instance = double(::Gitlab::Metrics::Dashboard::Processor)
expect(::Gitlab::Metrics::Dashboard::Processor).to receive(:new).with(project, file_content_hash, sequence, {}).and_return(service_instance)
expect(service_instance).to receive(:process).and_return(file_content_hash)
expect(::Files::CreateService).to receive(:new).with(project, user, dashboard_attrs).and_return(double(execute: { status: :success }))
service_call
end
end
end
end
RSpec.shared_examples 'valid dashboard update process' do
let(:dashboard_attrs) do
{
commit_message: commit_message,
branch_name: branch,
start_branch: project.default_branch,
encoding: 'text',
file_path: ".gitlab/dashboards/#{file_name}",
file_content: ::PerformanceMonitoring::PrometheusDashboard.from_json(file_content_hash).to_yaml
}
end
it 'delegates commit creation to Files::UpdateService', :aggregate_failures do
service_instance = instance_double(::Files::UpdateService)
expect(::Files::UpdateService).to receive(:new).with(project, user, dashboard_attrs).and_return(service_instance)
expect(service_instance).to receive(:execute).and_return(status: :success)
service_call
end
end
RSpec.shared_examples 'misconfigured dashboard service response with stepable' do |status_code, message = nil|
it 'returns an appropriate message and status code', :aggregate_failures do
result = service_call
expect(result.keys).to contain_exactly(:message, :http_status, :status, :last_step)
expect(result[:status]).to eq(:error)
expect(result[:http_status]).to eq(status_code)
expect(result[:message]).to eq(message) if message
end
end
RSpec.shared_examples 'updates gitlab_metrics_dashboard_processing_time_ms metric' do
specify :prometheus do
service_call
metric = subject.send(:processing_time_metric)
labels = subject.send(:processing_time_metric_labels)
expect(metric.get(labels)).to be > 0
end
end
RSpec.shared_examples '#raw_dashboard raises error if dashboard loading fails' do
context 'when yaml is too large' do
before do
allow_next_instance_of(::Gitlab::Config::Loader::Yaml) do |loader|
allow(loader).to receive(:load_raw!)
.and_raise(Gitlab::Config::Loader::Yaml::DataTooLargeError, 'The parsed YAML is too big')
end
end
it 'raises error' do
expect { subject.raw_dashboard }.to raise_error(
Gitlab::Metrics::Dashboard::Errors::LayoutError,
'The parsed YAML is too big'
)
end
end
context 'when yaml loader returns error' do
before do
allow_next_instance_of(::Gitlab::Config::Loader::Yaml) do |loader|
allow(loader).to receive(:load_raw!)
.and_raise(Gitlab::Config::Loader::FormatError, 'Invalid configuration format')
end
end
it 'raises error' do
expect { subject.raw_dashboard }.to raise_error(
Gitlab::Metrics::Dashboard::Errors::LayoutError,
'Invalid yaml'
)
end
end
context 'when yaml is not a hash' do
before do
allow_next_instance_of(::Gitlab::Config::Loader::Yaml) do |loader|
allow(loader).to receive(:load_raw!)
.and_raise(Gitlab::Config::Loader::Yaml::NotHashError, 'Invalid configuration format')
end
end
it 'returns nil' do
expect(subject.raw_dashboard).to eq({})
end
end
end
|