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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
# frozen_string_literal: true
module Gitlab
module DatabaseImporters
module SelfMonitoring
module Project
class CreateService < ::BaseService
include Stepable
STEPS_ALLOWED_TO_FAIL = [
:validate_application_settings, :validate_project_created, :validate_admins
].freeze
VISIBILITY_LEVEL = Gitlab::VisibilityLevel::INTERNAL
PROJECT_NAME = 'GitLab Instance Administration'
steps :validate_application_settings,
:validate_project_created,
:validate_admins,
:create_group,
:create_project,
:save_project_id,
:add_group_members,
:add_to_whitelist,
:add_prometheus_manual_configuration
def initialize
super(nil)
end
def execute!
result = execute_steps
if result[:status] == :success
result
elsif STEPS_ALLOWED_TO_FAIL.include?(result[:failed_step])
success
else
raise StandardError, result[:message]
end
end
private
def validate_application_settings
return success if application_settings
log_error(_('No application_settings found'))
error(_('No application_settings found'))
end
def validate_project_created
return success unless project_created?
log_error(_('Project already created'))
error(_('Project already created'))
end
def validate_admins
unless instance_admins.any?
log_error(_('No active admin user found'))
return error(_('No active admin user found'))
end
success
end
def create_group
if project_created?
log_info(_('Instance administrators group already exists'))
@group = application_settings.instance_administration_project.owner
return success(group: @group)
end
@group = ::Groups::CreateService.new(group_owner, create_group_params).execute
if @group.persisted?
success(group: @group)
else
error(_('Could not create group'))
end
end
def create_project
if project_created?
log_info(_('Instance administration project already exists'))
@project = application_settings.instance_administration_project
return success(project: project)
end
@project = ::Projects::CreateService.new(group_owner, create_project_params).execute
if project.persisted?
success(project: project)
else
log_error(_("Could not create instance administration project. Errors: %{errors}") % { errors: project.errors.full_messages })
error(_('Could not create project'))
end
end
def save_project_id
return success if project_created?
result = application_settings.update(instance_administration_project_id: @project.id)
if result
success
else
log_error(_("Could not save instance administration project ID, errors: %{errors}") % { errors: application_settings.errors.full_messages })
error(_('Could not save project ID'))
end
end
def add_group_members
members = @group.add_users(members_to_add, Gitlab::Access::MAINTAINER)
errors = members.flat_map { |member| member.errors.full_messages }
if errors.any?
log_error(_('Could not add admins as members to self-monitoring project. Errors: %{errors}') % { errors: errors })
error(_('Could not add admins as members'))
else
success
end
end
def add_to_whitelist
return success unless prometheus_enabled?
return success unless prometheus_listen_address.present?
uri = parse_url(internal_prometheus_listen_address_uri)
return error(_('Prometheus listen_address is not a valid URI')) unless uri
application_settings.add_to_outbound_local_requests_whitelist([uri.normalized_host])
result = application_settings.save
if result
# Expire the Gitlab::CurrentSettings cache after updating the whitelist.
# This happens automatically in an after_commit hook, but in migrations,
# the after_commit hook only runs at the end of the migration.
Gitlab::CurrentSettings.expire_current_application_settings
success
else
log_error(_("Could not add prometheus URL to whitelist, errors: %{errors}") % { errors: application_settings.errors.full_messages })
error(_('Could not add prometheus URL to whitelist'))
end
end
def add_prometheus_manual_configuration
return success unless prometheus_enabled?
return success unless prometheus_listen_address.present?
service = project.find_or_initialize_service('prometheus')
unless service.update(prometheus_service_attributes)
log_error(_('Could not save prometheus manual configuration for self-monitoring project. Errors: %{errors}') % { errors: service.errors.full_messages })
return error(_('Could not save prometheus manual configuration'))
end
success
end
def application_settings
@application_settings ||= ApplicationSetting.current_without_cache
end
def project_created?
application_settings.instance_administration_project.present?
end
def parse_url(uri_string)
Addressable::URI.parse(uri_string)
rescue Addressable::URI::InvalidURIError, TypeError
end
def prometheus_enabled?
Gitlab.config.prometheus.enable if Gitlab.config.prometheus
rescue Settingslogic::MissingSetting
log_error(_('prometheus.enable is not present in gitlab.yml'))
false
end
def prometheus_listen_address
Gitlab.config.prometheus.listen_address if Gitlab.config.prometheus
rescue Settingslogic::MissingSetting
log_error(_('prometheus.listen_address is not present in gitlab.yml'))
nil
end
def instance_admins
@instance_admins ||= User.admins.active
end
def group_owner
instance_admins.first
end
def members_to_add
# Exclude admins who are already members of group because
# `@group.add_users(users)` returns an error if the users parameter contains
# users who are already members of the group.
instance_admins - @group.members.collect(&:user)
end
def create_group_params
{
name: 'GitLab Instance Administrators',
path: "gitlab-instance-administrators-#{SecureRandom.hex(4)}",
visibility_level: VISIBILITY_LEVEL
}
end
def docs_path
Rails.application.routes.url_helpers.help_page_path(
'administration/monitoring/gitlab_instance_administration_project/index'
)
end
def create_project_params
{
initialize_with_readme: true,
visibility_level: VISIBILITY_LEVEL,
name: PROJECT_NAME,
description: "This project is automatically generated and will be used to help monitor this GitLab instance. [More information](#{docs_path})",
namespace_id: @group.id
}
end
def internal_prometheus_listen_address_uri
if prometheus_listen_address.starts_with?('http')
prometheus_listen_address
else
'http://' + prometheus_listen_address
end
end
def prometheus_service_attributes
{
api_url: internal_prometheus_listen_address_uri,
manual_configuration: true,
active: true
}
end
end
end
end
end
end
|