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