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.
- 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
@@ -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
|