cpflow 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/check_cpln_links.yml +19 -0
- data/.github/workflows/command_docs.yml +24 -0
- data/.github/workflows/rspec-shared.yml +56 -0
- data/.github/workflows/rspec.yml +28 -0
- data/.github/workflows/rubocop.yml +24 -0
- data/.gitignore +18 -0
- data/.overcommit.yml +16 -0
- data/.rubocop.yml +22 -0
- data/.simplecov_spawn.rb +10 -0
- data/CHANGELOG.md +259 -0
- data/CONTRIBUTING.md +73 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +126 -0
- data/LICENSE +21 -0
- data/README.md +546 -0
- data/Rakefile +21 -0
- data/bin/cpflow +6 -0
- data/cpflow +6 -0
- data/cpflow.gemspec +41 -0
- data/docs/assets/grafana-alert.png +0 -0
- data/docs/assets/memcached.png +0 -0
- data/docs/assets/sidekiq-pre-stop-hook.png +0 -0
- data/docs/commands.md +454 -0
- data/docs/dns.md +15 -0
- data/docs/migrating.md +262 -0
- data/docs/postgres.md +436 -0
- data/docs/redis.md +128 -0
- data/docs/secrets-and-env-values.md +42 -0
- data/docs/tips.md +150 -0
- data/docs/troubleshooting.md +6 -0
- data/examples/circleci.yml +104 -0
- data/examples/controlplane.yml +159 -0
- data/lib/command/apply_template.rb +209 -0
- data/lib/command/base.rb +540 -0
- data/lib/command/build_image.rb +49 -0
- data/lib/command/cleanup_images.rb +136 -0
- data/lib/command/cleanup_stale_apps.rb +79 -0
- data/lib/command/config.rb +48 -0
- data/lib/command/copy_image_from_upstream.rb +108 -0
- data/lib/command/delete.rb +149 -0
- data/lib/command/deploy_image.rb +56 -0
- data/lib/command/doctor.rb +47 -0
- data/lib/command/env.rb +22 -0
- data/lib/command/exists.rb +23 -0
- data/lib/command/generate.rb +45 -0
- data/lib/command/info.rb +222 -0
- data/lib/command/latest_image.rb +19 -0
- data/lib/command/logs.rb +49 -0
- data/lib/command/maintenance.rb +42 -0
- data/lib/command/maintenance_off.rb +62 -0
- data/lib/command/maintenance_on.rb +62 -0
- data/lib/command/maintenance_set_page.rb +34 -0
- data/lib/command/no_command.rb +23 -0
- data/lib/command/open.rb +33 -0
- data/lib/command/open_console.rb +26 -0
- data/lib/command/promote_app_from_upstream.rb +38 -0
- data/lib/command/ps.rb +41 -0
- data/lib/command/ps_restart.rb +37 -0
- data/lib/command/ps_start.rb +51 -0
- data/lib/command/ps_stop.rb +82 -0
- data/lib/command/ps_wait.rb +40 -0
- data/lib/command/run.rb +573 -0
- data/lib/command/setup_app.rb +113 -0
- data/lib/command/test.rb +23 -0
- data/lib/command/version.rb +18 -0
- data/lib/constants/exit_code.rb +7 -0
- data/lib/core/config.rb +316 -0
- data/lib/core/controlplane.rb +552 -0
- data/lib/core/controlplane_api.rb +170 -0
- data/lib/core/controlplane_api_direct.rb +112 -0
- data/lib/core/doctor_service.rb +104 -0
- data/lib/core/helpers.rb +26 -0
- data/lib/core/shell.rb +100 -0
- data/lib/core/template_parser.rb +76 -0
- data/lib/cpflow/version.rb +6 -0
- data/lib/cpflow.rb +288 -0
- data/lib/deprecated_commands.json +9 -0
- data/lib/generator_templates/Dockerfile +27 -0
- data/lib/generator_templates/controlplane.yml +62 -0
- data/lib/generator_templates/entrypoint.sh +8 -0
- data/lib/generator_templates/templates/app.yml +21 -0
- data/lib/generator_templates/templates/postgres.yml +176 -0
- data/lib/generator_templates/templates/rails.yml +36 -0
- data/rakelib/create_release.rake +81 -0
- data/script/add_command +37 -0
- data/script/check_command_docs +3 -0
- data/script/check_cpln_links +45 -0
- data/script/rename_command +43 -0
- data/script/update_command_docs +62 -0
- data/templates/app.yml +13 -0
- data/templates/daily-task.yml +32 -0
- data/templates/maintenance.yml +25 -0
- data/templates/memcached.yml +24 -0
- data/templates/postgres.yml +32 -0
- data/templates/rails.yml +27 -0
- data/templates/redis.yml +21 -0
- data/templates/redis2.yml +37 -0
- data/templates/sidekiq.yml +38 -0
- metadata +341 -0
data/lib/command/test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "debug"
|
4
|
+
|
5
|
+
module Command
|
6
|
+
class Test < Base
|
7
|
+
NAME = "test"
|
8
|
+
OPTIONS = all_options
|
9
|
+
DESCRIPTION = "For debugging purposes"
|
10
|
+
LONG_DESCRIPTION = <<~DESC
|
11
|
+
- For debugging purposes
|
12
|
+
DESC
|
13
|
+
HIDE = true
|
14
|
+
VALIDATIONS = [].freeze
|
15
|
+
|
16
|
+
def call
|
17
|
+
# Modify this method to trigger the code you want to test.
|
18
|
+
# You can use `debugger` to debug.
|
19
|
+
# You can use `Cpflow::Cli.start` to simulate a command
|
20
|
+
# (e.g., `Cpflow::Cli.start(["deploy-image", "-a", "my-app-name"])`).
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class Version < Base
|
5
|
+
NAME = "version"
|
6
|
+
DESCRIPTION = "Displays the current version of the CLI"
|
7
|
+
LONG_DESCRIPTION = <<~DESC
|
8
|
+
- Displays the current version of the CLI
|
9
|
+
- Can also be done with `cpflow --version` or `cpflow -v`
|
10
|
+
DESC
|
11
|
+
WITH_INFO_HEADER = false
|
12
|
+
VALIDATIONS = [].freeze
|
13
|
+
|
14
|
+
def call
|
15
|
+
puts Cpflow::VERSION
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/core/config.rb
ADDED
@@ -0,0 +1,316 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "helpers"
|
4
|
+
|
5
|
+
class Config # rubocop:disable Metrics/ClassLength
|
6
|
+
attr_reader :org_comes_from_env, :app_comes_from_env,
|
7
|
+
# command line options
|
8
|
+
:args, :options, :required_options
|
9
|
+
|
10
|
+
include Helpers
|
11
|
+
|
12
|
+
CONFIG_FILE_LOCATION = ".controlplane/controlplane.yml"
|
13
|
+
|
14
|
+
def initialize(args, options, required_options)
|
15
|
+
@args = args
|
16
|
+
@options = options
|
17
|
+
@required_options = required_options
|
18
|
+
|
19
|
+
ensure_required_options!
|
20
|
+
|
21
|
+
warn_deprecated_options
|
22
|
+
|
23
|
+
Shell.verbose_mode(options[:verbose])
|
24
|
+
trace_mode = options[:trace]
|
25
|
+
return unless trace_mode
|
26
|
+
|
27
|
+
ControlplaneApiDirect.trace = trace_mode
|
28
|
+
Shell.warn("Trace mode is enabled, this will print sensitive information to the console.")
|
29
|
+
end
|
30
|
+
|
31
|
+
def org
|
32
|
+
@org ||= load_org_from_options || load_org_from_env || load_org_from_file
|
33
|
+
end
|
34
|
+
|
35
|
+
def app
|
36
|
+
@app ||= load_app_from_options || load_app_from_env
|
37
|
+
end
|
38
|
+
|
39
|
+
def app_prefix
|
40
|
+
current&.fetch(:name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def identity
|
44
|
+
"#{app}-identity"
|
45
|
+
end
|
46
|
+
|
47
|
+
def identity_link
|
48
|
+
"/org/#{org}/gvc/#{app}/identity/#{identity}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def secrets
|
52
|
+
current&.dig(:secrets_name) || "#{app_prefix}-secrets"
|
53
|
+
end
|
54
|
+
|
55
|
+
def secrets_policy
|
56
|
+
current&.dig(:secrets_policy_name) || "#{secrets}-policy"
|
57
|
+
end
|
58
|
+
|
59
|
+
def location
|
60
|
+
@location ||= load_location_from_options || load_location_from_env || load_location_from_file
|
61
|
+
end
|
62
|
+
|
63
|
+
def location_link
|
64
|
+
"/org/#{org}/location/#{location}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def image_link(image)
|
68
|
+
"/org/#{org}/image/#{image}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def domain
|
72
|
+
@domain ||= load_domain_from_options || load_domain_from_file
|
73
|
+
end
|
74
|
+
|
75
|
+
def [](key)
|
76
|
+
ensure_current_config!
|
77
|
+
|
78
|
+
raise "Can't find option '#{key}' for app '#{app}' in 'controlplane.yml'." unless current.key?(key)
|
79
|
+
|
80
|
+
current.fetch(key)
|
81
|
+
end
|
82
|
+
|
83
|
+
def script_path
|
84
|
+
Pathname.new(__dir__).parent.parent
|
85
|
+
end
|
86
|
+
|
87
|
+
def app_cpln_dir
|
88
|
+
"#{app_dir}/.controlplane"
|
89
|
+
end
|
90
|
+
|
91
|
+
def should_app_start_with?(app_name)
|
92
|
+
apps[app_name.to_sym]&.dig(:match_if_app_name_starts_with) || false
|
93
|
+
end
|
94
|
+
|
95
|
+
def app_dir
|
96
|
+
Pathname.new(config_file_path).parent.parent.to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
def config
|
100
|
+
@config ||= begin
|
101
|
+
global_config = YAML.safe_load_file(config_file_path, symbolize_names: true, aliases: true)
|
102
|
+
ensure_config!(global_config)
|
103
|
+
ensure_config_apps!(global_config)
|
104
|
+
|
105
|
+
global_config
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def apps
|
110
|
+
@apps ||= config[:apps].to_h do |app_name, app_options|
|
111
|
+
ensure_config_app!(app_name, app_options)
|
112
|
+
|
113
|
+
check_deprecated_options(app_options)
|
114
|
+
|
115
|
+
app_options_with_new_keys = app_options.to_h do |key, value|
|
116
|
+
new_key = new_option_keys[key]
|
117
|
+
new_key ? [new_key, value] : [key, value]
|
118
|
+
end
|
119
|
+
|
120
|
+
[app_name, app_options_with_new_keys]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def current
|
125
|
+
return unless app
|
126
|
+
|
127
|
+
@current ||= find_app_config(app)
|
128
|
+
end
|
129
|
+
|
130
|
+
def app_matches?(app_name1, app_name2, app_options)
|
131
|
+
app_name1 && app_name2 &&
|
132
|
+
(app_name1.to_s == app_name2.to_s ||
|
133
|
+
(app_options[:match_if_app_name_starts_with] && app_name1.to_s.start_with?(app_name2.to_s))
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
def find_app_config(app_name1)
|
138
|
+
@app_configs ||= {}
|
139
|
+
|
140
|
+
@app_configs[app_name1] ||= apps.filter_map do |app_name2, app_config|
|
141
|
+
next unless app_matches?(app_name1, app_name2, app_config)
|
142
|
+
|
143
|
+
app_config[:name] = app_name2
|
144
|
+
app_config
|
145
|
+
end&.last
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def ensure_current_config!
|
151
|
+
raise "Can't find current config, please specify an app." unless current
|
152
|
+
end
|
153
|
+
|
154
|
+
def ensure_config!(global_config)
|
155
|
+
raise "'controlplane.yml' is empty." unless global_config
|
156
|
+
end
|
157
|
+
|
158
|
+
def ensure_config_apps!(global_config)
|
159
|
+
raise "Can't find key 'apps' in 'controlplane.yml'." unless global_config[:apps]
|
160
|
+
end
|
161
|
+
|
162
|
+
def ensure_config_app!(app_name, app_options)
|
163
|
+
raise "Can't find config for app '#{app_name}' in 'controlplane.yml'." unless app_options
|
164
|
+
end
|
165
|
+
|
166
|
+
def ensure_app!
|
167
|
+
return if app
|
168
|
+
|
169
|
+
raise "No app provided. " \
|
170
|
+
"The app can be provided either through the CPLN_APP env var " \
|
171
|
+
"('allow_app_override_by_env' must be set to true in 'controlplane.yml'), " \
|
172
|
+
"or the --app command option."
|
173
|
+
end
|
174
|
+
|
175
|
+
def ensure_org!
|
176
|
+
return if org
|
177
|
+
|
178
|
+
raise "No org provided. " \
|
179
|
+
"The org can be provided either through the CPLN_ORG env var " \
|
180
|
+
"('allow_org_override_by_env' must be set to true in 'controlplane.yml'), " \
|
181
|
+
"the --org command option, " \
|
182
|
+
"or the 'cpln_org' key in 'controlplane.yml'."
|
183
|
+
end
|
184
|
+
|
185
|
+
def ensure_required_options! # rubocop:disable Metrics/CyclomaticComplexity
|
186
|
+
ensure_app! if required_options.include?(:app)
|
187
|
+
ensure_org! if required_options.include?(:org) || app
|
188
|
+
|
189
|
+
missing_str = required_options
|
190
|
+
.reject { |option_name| %i[org app].include?(option_name) || options.key?(option_name) }
|
191
|
+
.map { |option_name| "--#{option_name}" }
|
192
|
+
.join(", ")
|
193
|
+
|
194
|
+
raise "Required options missing: #{missing_str}" unless missing_str.empty?
|
195
|
+
end
|
196
|
+
|
197
|
+
def config_file_path # rubocop:disable Metrics/MethodLength
|
198
|
+
@config_file_path ||= ENV["CONFIG_FILE_PATH"] || begin
|
199
|
+
path = Pathname.new(".").expand_path
|
200
|
+
|
201
|
+
loop do
|
202
|
+
config_file = path + CONFIG_FILE_LOCATION
|
203
|
+
break config_file if File.file?(config_file)
|
204
|
+
|
205
|
+
path = path.parent
|
206
|
+
|
207
|
+
if path.root?
|
208
|
+
raise "Can't find project config file at 'project_folder/#{CONFIG_FILE_LOCATION}', please create it."
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def new_option_keys
|
215
|
+
{
|
216
|
+
org: :cpln_org,
|
217
|
+
location: :default_location,
|
218
|
+
prefix: :match_if_app_name_starts_with,
|
219
|
+
setup: :setup_app_templates,
|
220
|
+
old_image_retention_days: :image_retention_days
|
221
|
+
}
|
222
|
+
end
|
223
|
+
|
224
|
+
def load_app_from_env
|
225
|
+
app_from_env = strip_str_and_validate(ENV.fetch("CPLN_APP", nil))
|
226
|
+
return unless app_from_env
|
227
|
+
|
228
|
+
app_config = find_app_config(app_from_env)
|
229
|
+
ensure_config_app!(app_from_env, app_config)
|
230
|
+
|
231
|
+
key_exists = app_config.key?(:allow_app_override_by_env)
|
232
|
+
allowed_locally = key_exists && app_config[:allow_app_override_by_env]
|
233
|
+
allowed_globally = !key_exists && config[:allow_app_override_by_env]
|
234
|
+
return unless allowed_locally || allowed_globally
|
235
|
+
|
236
|
+
@app_comes_from_env = true
|
237
|
+
|
238
|
+
app_from_env
|
239
|
+
end
|
240
|
+
|
241
|
+
def load_app_from_options
|
242
|
+
app_from_options = strip_str_and_validate(options[:app])
|
243
|
+
return unless app_from_options
|
244
|
+
|
245
|
+
app_config = find_app_config(app_from_options)
|
246
|
+
ensure_config_app!(app_from_options, app_config)
|
247
|
+
|
248
|
+
app_from_options
|
249
|
+
end
|
250
|
+
|
251
|
+
def load_org_from_env
|
252
|
+
org_from_env = strip_str_and_validate(ENV.fetch("CPLN_ORG", nil))
|
253
|
+
return unless org_from_env
|
254
|
+
|
255
|
+
key_exists = current&.key?(:allow_org_override_by_env)
|
256
|
+
allowed_locally = key_exists && current[:allow_org_override_by_env]
|
257
|
+
allowed_globally = !key_exists && config[:allow_org_override_by_env]
|
258
|
+
return unless allowed_locally || allowed_globally
|
259
|
+
|
260
|
+
@org_comes_from_env = true
|
261
|
+
|
262
|
+
org_from_env
|
263
|
+
end
|
264
|
+
|
265
|
+
def load_org_from_options
|
266
|
+
strip_str_and_validate(options[:org])
|
267
|
+
end
|
268
|
+
|
269
|
+
def load_org_from_file
|
270
|
+
return unless current&.key?(:cpln_org)
|
271
|
+
|
272
|
+
strip_str_and_validate(current[:cpln_org])
|
273
|
+
end
|
274
|
+
|
275
|
+
def load_location_from_options
|
276
|
+
strip_str_and_validate(options[:location])
|
277
|
+
end
|
278
|
+
|
279
|
+
def load_location_from_env
|
280
|
+
strip_str_and_validate(ENV.fetch("CPLN_LOCATION", nil))
|
281
|
+
end
|
282
|
+
|
283
|
+
def load_location_from_file
|
284
|
+
return unless current&.key?(:default_location)
|
285
|
+
|
286
|
+
strip_str_and_validate(current.fetch(:default_location))
|
287
|
+
end
|
288
|
+
|
289
|
+
def load_domain_from_options
|
290
|
+
strip_str_and_validate(options[:domain])
|
291
|
+
end
|
292
|
+
|
293
|
+
def load_domain_from_file
|
294
|
+
return unless current&.key?(:default_domain)
|
295
|
+
|
296
|
+
strip_str_and_validate(current.fetch(:default_domain))
|
297
|
+
end
|
298
|
+
|
299
|
+
def check_deprecated_options(app_options)
|
300
|
+
@deprecated_option_keys ||= {}
|
301
|
+
|
302
|
+
new_option_keys.each do |old_key, new_key|
|
303
|
+
@deprecated_option_keys[old_key] = new_key if app_options.key?(old_key)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def warn_deprecated_options
|
308
|
+
return if !@deprecated_option_keys || @deprecated_option_keys.empty?
|
309
|
+
|
310
|
+
@deprecated_option_keys.each do |old_key, new_key|
|
311
|
+
Shell.warn_deprecated("Option '#{old_key}' is deprecated, " \
|
312
|
+
"please use '#{new_key}' instead (in 'controlplane.yml').")
|
313
|
+
end
|
314
|
+
$stderr.puts
|
315
|
+
end
|
316
|
+
end
|