cpflow 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/check_cpln_links.yml +19 -0
  3. data/.github/workflows/command_docs.yml +24 -0
  4. data/.github/workflows/rspec-shared.yml +56 -0
  5. data/.github/workflows/rspec.yml +28 -0
  6. data/.github/workflows/rubocop.yml +24 -0
  7. data/.gitignore +18 -0
  8. data/.overcommit.yml +16 -0
  9. data/.rubocop.yml +22 -0
  10. data/.simplecov_spawn.rb +10 -0
  11. data/CHANGELOG.md +259 -0
  12. data/CONTRIBUTING.md +73 -0
  13. data/Gemfile +7 -0
  14. data/Gemfile.lock +126 -0
  15. data/LICENSE +21 -0
  16. data/README.md +546 -0
  17. data/Rakefile +21 -0
  18. data/bin/cpflow +6 -0
  19. data/cpflow +6 -0
  20. data/cpflow.gemspec +41 -0
  21. data/docs/assets/grafana-alert.png +0 -0
  22. data/docs/assets/memcached.png +0 -0
  23. data/docs/assets/sidekiq-pre-stop-hook.png +0 -0
  24. data/docs/commands.md +454 -0
  25. data/docs/dns.md +15 -0
  26. data/docs/migrating.md +262 -0
  27. data/docs/postgres.md +436 -0
  28. data/docs/redis.md +128 -0
  29. data/docs/secrets-and-env-values.md +42 -0
  30. data/docs/tips.md +150 -0
  31. data/docs/troubleshooting.md +6 -0
  32. data/examples/circleci.yml +104 -0
  33. data/examples/controlplane.yml +159 -0
  34. data/lib/command/apply_template.rb +209 -0
  35. data/lib/command/base.rb +540 -0
  36. data/lib/command/build_image.rb +49 -0
  37. data/lib/command/cleanup_images.rb +136 -0
  38. data/lib/command/cleanup_stale_apps.rb +79 -0
  39. data/lib/command/config.rb +48 -0
  40. data/lib/command/copy_image_from_upstream.rb +108 -0
  41. data/lib/command/delete.rb +149 -0
  42. data/lib/command/deploy_image.rb +56 -0
  43. data/lib/command/doctor.rb +47 -0
  44. data/lib/command/env.rb +22 -0
  45. data/lib/command/exists.rb +23 -0
  46. data/lib/command/generate.rb +45 -0
  47. data/lib/command/info.rb +222 -0
  48. data/lib/command/latest_image.rb +19 -0
  49. data/lib/command/logs.rb +49 -0
  50. data/lib/command/maintenance.rb +42 -0
  51. data/lib/command/maintenance_off.rb +62 -0
  52. data/lib/command/maintenance_on.rb +62 -0
  53. data/lib/command/maintenance_set_page.rb +34 -0
  54. data/lib/command/no_command.rb +23 -0
  55. data/lib/command/open.rb +33 -0
  56. data/lib/command/open_console.rb +26 -0
  57. data/lib/command/promote_app_from_upstream.rb +38 -0
  58. data/lib/command/ps.rb +41 -0
  59. data/lib/command/ps_restart.rb +37 -0
  60. data/lib/command/ps_start.rb +51 -0
  61. data/lib/command/ps_stop.rb +82 -0
  62. data/lib/command/ps_wait.rb +40 -0
  63. data/lib/command/run.rb +573 -0
  64. data/lib/command/setup_app.rb +113 -0
  65. data/lib/command/test.rb +23 -0
  66. data/lib/command/version.rb +18 -0
  67. data/lib/constants/exit_code.rb +7 -0
  68. data/lib/core/config.rb +316 -0
  69. data/lib/core/controlplane.rb +552 -0
  70. data/lib/core/controlplane_api.rb +170 -0
  71. data/lib/core/controlplane_api_direct.rb +112 -0
  72. data/lib/core/doctor_service.rb +104 -0
  73. data/lib/core/helpers.rb +26 -0
  74. data/lib/core/shell.rb +100 -0
  75. data/lib/core/template_parser.rb +76 -0
  76. data/lib/cpflow/version.rb +6 -0
  77. data/lib/cpflow.rb +288 -0
  78. data/lib/deprecated_commands.json +9 -0
  79. data/lib/generator_templates/Dockerfile +27 -0
  80. data/lib/generator_templates/controlplane.yml +62 -0
  81. data/lib/generator_templates/entrypoint.sh +8 -0
  82. data/lib/generator_templates/templates/app.yml +21 -0
  83. data/lib/generator_templates/templates/postgres.yml +176 -0
  84. data/lib/generator_templates/templates/rails.yml +36 -0
  85. data/rakelib/create_release.rake +81 -0
  86. data/script/add_command +37 -0
  87. data/script/check_command_docs +3 -0
  88. data/script/check_cpln_links +45 -0
  89. data/script/rename_command +43 -0
  90. data/script/update_command_docs +62 -0
  91. data/templates/app.yml +13 -0
  92. data/templates/daily-task.yml +32 -0
  93. data/templates/maintenance.yml +25 -0
  94. data/templates/memcached.yml +24 -0
  95. data/templates/postgres.yml +32 -0
  96. data/templates/rails.yml +27 -0
  97. data/templates/redis.yml +21 -0
  98. data/templates/redis2.yml +37 -0
  99. data/templates/sidekiq.yml +38 -0
  100. metadata +341 -0
@@ -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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExitCode
4
+ SUCCESS = 0
5
+ ERROR_DEFAULT = 64
6
+ INTERRUPT = 130
7
+ end
@@ -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