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
|
# frozen_string_literal: true
# Copies system dashboard definition in .yml file into designated
# .yml file inside `.gitlab/dashboards`
module Metrics
module Dashboard
class CloneDashboardService < ::BaseService
include Stepable
include Gitlab::Utils::StrongMemoize
ALLOWED_FILE_TYPE = '.yml'
USER_DASHBOARDS_DIR = ::Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT
SEQUENCES = {
::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH => [
::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter,
::Gitlab::Metrics::Dashboard::Stages::Sorter
].freeze,
::Metrics::Dashboard::SelfMonitoringDashboardService::DASHBOARD_PATH => [
::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter
].freeze,
::Metrics::Dashboard::ClusterDashboardService::DASHBOARD_PATH => [
::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
::Gitlab::Metrics::Dashboard::Stages::Sorter
].freeze
}.freeze
steps :check_push_authorized,
:check_branch_name,
:check_file_type,
:check_dashboard_template,
:create_file,
:refresh_repository_method_caches
def execute
execute_steps
end
private
def check_push_authorized(result)
return error(_('You are not allowed to push into this branch. Create another branch or open a merge request.'), :forbidden) unless push_authorized?
success(result)
end
def check_branch_name(result)
return error(_('There was an error creating the dashboard, branch name is invalid.'), :bad_request) unless valid_branch_name?
return error(_('There was an error creating the dashboard, branch named: %{branch} already exists.') % { branch: params[:branch] }, :bad_request) unless new_or_default_branch?
success(result)
end
def check_file_type(result)
return error(_('The file name should have a .yml extension'), :bad_request) unless target_file_type_valid?
success(result)
end
# Only allow out of the box metrics dashboards to be cloned. This can be
# changed to allow cloning of any metrics dashboard, if desired.
# However, only metrics dashboards should be allowed. If any file is
# allowed to be cloned, this will become a security risk.
def check_dashboard_template(result)
return error(_('Not found.'), :not_found) unless dashboard_service&.out_of_the_box_dashboard?
success(result)
end
def create_file(result)
create_file_response = ::Files::CreateService.new(project, current_user, dashboard_attrs).execute
if create_file_response[:status] == :success
success(result.merge(create_file_response))
else
wrap_error(create_file_response)
end
end
def refresh_repository_method_caches(result)
repository.refresh_method_caches([:metrics_dashboard])
success(result.merge(http_status: :created, dashboard: dashboard_details))
end
def dashboard_service
strong_memoize(:dashboard_service) do
Gitlab::Metrics::Dashboard::ServiceSelector.call(dashboard_service_options)
end
end
def dashboard_attrs
{
commit_message: params[:commit_message],
file_path: new_dashboard_path,
file_content: new_dashboard_content,
encoding: 'text',
branch_name: branch,
start_branch: repository.branch_exists?(branch) ? branch : project.default_branch
}
end
def dashboard_details
{
path: new_dashboard_path,
display_name: ::Metrics::Dashboard::CustomDashboardService.name_for_path(new_dashboard_path),
default: false,
system_dashboard: false
}
end
def push_authorized?
Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch)
end
def dashboard_template
@dashboard_template ||= params[:dashboard]
end
def branch
@branch ||= params[:branch]
end
def new_or_default_branch?
!repository.branch_exists?(params[:branch]) || project.default_branch == params[:branch]
end
def valid_branch_name?
Gitlab::GitRefValidator.validate(params[:branch])
end
def new_dashboard_path
@new_dashboard_path ||= File.join(USER_DASHBOARDS_DIR, file_name)
end
def file_name
@file_name ||= File.basename(params[:file_name])
end
def target_file_type_valid?
File.extname(params[:file_name]) == ALLOWED_FILE_TYPE
end
def wrap_error(result)
if result[:message] == 'A file with this name already exists'
error(_("A file with '%{file_name}' already exists in %{branch} branch") % { file_name: file_name, branch: branch }, :bad_request)
else
result
end
end
def new_dashboard_content
::Gitlab::Metrics::Dashboard::Processor
.new(project, raw_dashboard, sequence, {})
.process.deep_stringify_keys.to_yaml
end
def repository
@repository ||= project.repository
end
def raw_dashboard
dashboard_service.new(project, current_user, dashboard_service_options).raw_dashboard
end
def dashboard_service_options
{
embedded: false,
dashboard_path: dashboard_template
}
end
def sequence
SEQUENCES[dashboard_template] || []
end
end
end
end
|