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,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class ApplyTemplate < Base # rubocop:disable Metrics/ClassLength
5
+ NAME = "apply-template"
6
+ USAGE = "apply-template TEMPLATE [TEMPLATE] ... [TEMPLATE]"
7
+ REQUIRES_ARGS = true
8
+ OPTIONS = [
9
+ app_option(required: true),
10
+ location_option,
11
+ skip_confirm_option,
12
+ add_app_identity_option
13
+ ].freeze
14
+ DESCRIPTION = "Applies application-specific configs from templates"
15
+ LONG_DESCRIPTION = <<~DESC
16
+ - Applies application-specific configs from templates (e.g., for every review-app)
17
+ - Publishes (creates or updates) those at Control Plane infrastructure
18
+ - Picks templates from the `.controlplane/templates` directory
19
+ - Templates are ordinary Control Plane templates but with variable preprocessing
20
+
21
+ **Preprocessed template variables:**
22
+
23
+ ```
24
+ {{APP_ORG}} - organization name
25
+ {{APP_NAME}} - GVC/app name
26
+ {{APP_LOCATION}} - location, per YML file, ENV, or command line arg
27
+ {{APP_LOCATION_LINK}} - full link for location, ready to be used for the value of `staticPlacement.locationLinks` in the templates
28
+ {{APP_IMAGE}} - latest app image
29
+ {{APP_IMAGE_LINK}} - full link for latest app image, ready to be used for the value of `containers[].image` in the templates
30
+ {{APP_IDENTITY}} - default identity
31
+ {{APP_IDENTITY_LINK}} - full link for identity, ready to be used for the value of `identityLink` in the templates
32
+ ```
33
+ DESC
34
+ EXAMPLES = <<~EX
35
+ ```sh
36
+ # Applies single template.
37
+ cpflow apply-template redis -a $APP_NAME
38
+
39
+ # Applies several templates (practically creating full app).
40
+ cpflow apply-template app postgres redis rails -a $APP_NAME
41
+ ```
42
+ EX
43
+ VALIDATIONS = %w[config templates].freeze
44
+
45
+ def call # rubocop:disable Metrics/MethodLength
46
+ @template_parser = TemplateParser.new(config)
47
+ @names_to_filenames = config.args.to_h do |name|
48
+ [name, @template_parser.template_filename(name)]
49
+ end
50
+
51
+ ensure_templates!
52
+
53
+ @created_items = []
54
+ @failed_templates = []
55
+ @skipped_templates = []
56
+
57
+ templates = @template_parser.parse(@names_to_filenames.values)
58
+ pending_templates = confirm_templates(templates)
59
+ add_app_identity_template(pending_templates) if config.options[:add_app_identity]
60
+ pending_templates.each do |template|
61
+ apply_template(template)
62
+ end
63
+
64
+ print_created_items
65
+ print_failed_templates
66
+ print_skipped_templates
67
+
68
+ exit(ExitCode::ERROR_DEFAULT) if @failed_templates.any?
69
+ end
70
+
71
+ private
72
+
73
+ def template_kind(template)
74
+ case template["kind"]
75
+ when "gvc"
76
+ "app"
77
+ else
78
+ template["kind"]
79
+ end
80
+ end
81
+
82
+ def ensure_templates!
83
+ missing_templates = @names_to_filenames.reject { |_, filename| File.exist?(filename) }
84
+ return if missing_templates.empty?
85
+
86
+ missing_templates_str = missing_templates.map do |name, filename|
87
+ " - #{name} (#{filename})"
88
+ end.join("\n")
89
+ progress.puts("#{Shell.color('Missing templates:', :red)}\n#{missing_templates_str}\n\n")
90
+
91
+ raise "Can't find templates above, please create them."
92
+ end
93
+
94
+ def confirm_apply(message)
95
+ return true if config.options[:yes]
96
+
97
+ @asked_for_confirmation = true
98
+ Shell.confirm(message)
99
+ end
100
+
101
+ def confirm_app(template)
102
+ app = cp.fetch_gvc(template["name"])
103
+ return true unless app
104
+
105
+ confirmed = confirm_apply("App '#{template['name']}' already exists, do you want to re-create it?")
106
+ return true if confirmed
107
+
108
+ report_skipped(template)
109
+ false
110
+ end
111
+
112
+ def confirm_workload(template)
113
+ workload = cp.fetch_workload(template["name"])
114
+ return true unless workload
115
+
116
+ confirmed = confirm_apply("Workload '#{template['name']}' already exists, do you want to re-create it?")
117
+ return true if confirmed
118
+
119
+ report_skipped(template)
120
+ false
121
+ end
122
+
123
+ def confirm_templates(templates) # rubocop:disable Metrics/MethodLength
124
+ @asked_for_confirmation = false
125
+
126
+ pending_templates = templates.select do |template|
127
+ case template["kind"]
128
+ when "gvc"
129
+ confirm_app(template)
130
+ when "workload"
131
+ confirm_workload(template)
132
+ else
133
+ true
134
+ end
135
+ end
136
+
137
+ progress.puts if @asked_for_confirmation
138
+
139
+ pending_templates
140
+ end
141
+
142
+ def add_app_identity_template(templates)
143
+ app_template_index = templates.index { |template| template["name"] == config.app }
144
+ app_identity_template_index = templates.index { |template| template["name"] == config.identity }
145
+
146
+ return unless app_template_index && app_identity_template_index.nil?
147
+
148
+ # Adding the identity template right after the app template is important since:
149
+ # a) we can't create the identity at the beginning because the app doesn't exist yet
150
+ # b) we also can't create it at the end because any workload templates associated with it will fail to apply
151
+ templates.insert(app_template_index + 1, build_app_identity_hash)
152
+ end
153
+
154
+ def build_app_identity_hash
155
+ {
156
+ "kind" => "identity",
157
+ "name" => config.identity
158
+ }
159
+ end
160
+
161
+ def apply_template(template) # rubocop:disable Metrics/MethodLength
162
+ step("Applying template for #{template_kind(template)} '#{template['name']}'", abort_on_error: false) do
163
+ items = cp.apply_hash(template)
164
+ unless items
165
+ report_failure(template)
166
+ next false
167
+ end
168
+
169
+ items.each do |item|
170
+ report_success(item)
171
+ end
172
+ true
173
+ end
174
+ end
175
+
176
+ def report_success(item)
177
+ @created_items.push(item)
178
+ end
179
+
180
+ def report_failure(template)
181
+ @failed_templates.push(template)
182
+ end
183
+
184
+ def report_skipped(template)
185
+ @skipped_templates.push(template)
186
+ end
187
+
188
+ def print_created_items
189
+ return unless @created_items.any?
190
+
191
+ created = @created_items.map { |item| " - [#{item[:kind]}] #{item[:name]}" }.join("\n")
192
+ progress.puts("\n#{Shell.color('Created items:', :green)}\n#{created}")
193
+ end
194
+
195
+ def print_failed_templates
196
+ return unless @failed_templates.any?
197
+
198
+ failed = @failed_templates.map { |template| " - [#{template_kind(template)}] #{template['name']}" }.join("\n")
199
+ progress.puts("\n#{Shell.color('Failed to apply templates:', :red)}\n#{failed}")
200
+ end
201
+
202
+ def print_skipped_templates
203
+ return unless @skipped_templates.any?
204
+
205
+ skipped = @skipped_templates.map { |template| " - [#{template_kind(template)}] #{template['name']}" }.join("\n")
206
+ progress.puts("\n#{Shell.color('Skipped templates (already exist):', :blue)}\n#{skipped}")
207
+ end
208
+ end
209
+ end