cpflow 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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