cpl 2.0.0.rc.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '03793c0046fc403a4fb42583e57c3449394a52da4c03191d3af845e2e5e0b53f'
4
- data.tar.gz: c882856e066cc3a63f0d8f52738e8e432a72cec33dc565224159a554949a2f5b
3
+ metadata.gz: c304577e2c02b5778a463b82ec9a72792bd10b727687402d66a504d390673fae
4
+ data.tar.gz: dc28bb00740914362abba26bc61cee999f7ac2d955218dca0ea6dbeef58d465e
5
5
  SHA512:
6
- metadata.gz: 788fa61c7d5710e72ea00db402770d3fcfd037938ce0ce1ef8134e0614a024cddb7e4629223598a509f33abf2dec499e64bf23a52b0b86b823eafe59b3522cd5
7
- data.tar.gz: 6ba9d8cdab48da090c7efd86eb9e14f05845ed1f219c6d251ce18ad636e34d678562d092ad52806dae3a4d106cd4760402463c823d9e66158f14e6cd806fc520
6
+ metadata.gz: 4812e651d14114d8d7bccf893cf50107dc9ba24b35679d633fc36d37589dfbae9d93eb8e8dc172bc66e4feb6039d6ab7c2093c0f35b1a544acab01b55c722c68
7
+ data.tar.gz: 53013a61c2bcedc3ad10664f8d4a8874731013bb740abdb42fdac34dd82c3dc960b2643e9dea8227335040d2c2730eff6ed0cf47973d9344ddf5c5fc1bba7a99
data/CHANGELOG.md CHANGED
@@ -14,14 +14,19 @@ Changes since the last non-beta release.
14
14
 
15
15
  _Please add entries here for your pull requests that are not yet released._
16
16
 
17
- ## [2.0.0.rc.0] - 2024-05-10
17
+ ## [2.0.0.rc.1] - 2024-05-11
18
18
 
19
19
  ### BREAKING CHANGES
20
20
 
21
21
  - Commands that finished with a failure now exit with code `64` instead of `1`. [PR 132](https://github.com/shakacode/heroku-to-control-plane/pull/132) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
22
+ - Bumped minimum `cpln` version to `2.0.1` (`cpln workload cron get` is required). [PR 171](https://github.com/shakacode/heroku-to-control-plane/pull/171) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
22
23
  - `run:cleanup` command has been removed. [PR 151](https://github.com/shakacode/heroku-to-control-plane/pull/151) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
23
24
  - `deploy-image` command now runs the release script in the context of the `run` command. [PR 151](https://github.com/shakacode/heroku-to-control-plane/pull/151) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
24
25
 
26
+ ### Fixed
27
+
28
+ - Fixed race conditions when using latest image in `run` command. [PR 163](https://github.com/shakacode/heroku-to-control-plane/pull/163) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
29
+
25
30
  ### Added
26
31
 
27
32
  - Added options to `run` command to override the workload container's `--cpu`, `--memory`, and `--entrypoint`. [PR 151](https://github.com/shakacode/heroku-to-control-plane/pull/151) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
@@ -32,9 +37,12 @@ _Please add entries here for your pull requests that are not yet released._
32
37
 
33
38
  ### Changed
34
39
 
40
+ - An error is now raised if the org does not exist. [PR 167](https://github.com/shakacode/heroku-to-control-plane/pull/167) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
41
+ - Common options are now shown in help. [PR 169](https://github.com/shakacode/heroku-to-control-plane/pull/169) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
35
42
  - `run` command now uses a single reusable cron workload and works for both interactive and non-interactive jobs. [PR 151](https://github.com/shakacode/heroku-to-control-plane/pull/151) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
36
43
  - `run:detached` command has been deprecated in favor of `run`. [PR 151](https://github.com/shakacode/heroku-to-control-plane/pull/151) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
37
44
  - `deploy-image` command now raises an error if image does not exist. [PR 153](https://github.com/shakacode/heroku-to-control-plane/pull/153) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
45
+ - `delete` command now unbinds identity from policy (if bound) when deleting app. [PR 170](https://github.com/shakacode/heroku-to-control-plane/pull/170) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
38
46
 
39
47
  ## [1.4.0] - 2024-03-20
40
48
 
@@ -178,8 +186,8 @@ _Please add entries here for your pull requests that are not yet released._
178
186
 
179
187
  - Initial release
180
188
 
181
- [Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v2.0.0.rc.0...HEAD
182
- [2.0.0.rc.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.4.0...v2.0.0.rc.0
189
+ [Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v2.0.0.rc.1...HEAD
190
+ [2.0.0.rc.1]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.4.0...v2.0.0.rc.1
183
191
  [1.4.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.3.0...v1.4.0
184
192
  [1.3.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.2.0...v1.3.0
185
193
  [1.2.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.2...v1.2.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cpl (2.0.0.rc.1)
4
+ cpl (2.0.0)
5
5
  debug (~> 1.7.1)
6
6
  dotenv (~> 2.8.1)
7
7
  jwt (~> 2.8.1)
@@ -52,7 +52,7 @@ GEM
52
52
  rdoc (6.6.3.1)
53
53
  psych (>= 4.0.0)
54
54
  regexp_parser (2.9.0)
55
- reline (0.5.6)
55
+ reline (0.5.7)
56
56
  io-console (~> 0.5)
57
57
  rexml (3.2.6)
58
58
  rspec (3.12.0)
data/docs/commands.md CHANGED
@@ -106,6 +106,7 @@ cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --ima
106
106
  ### `delete`
107
107
 
108
108
  - Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload
109
+ - Also unbinds the app from the secrets policy, as long as both the identity and the policy exist (and are bound)
109
110
  - Will ask for explicit user confirmation
110
111
 
111
112
  ```sh
data/docs/redis.md CHANGED
@@ -1,15 +1,31 @@
1
- # Migrating Redis database from Heroku infrastructure
1
+ # Migrating Redis databases
2
2
 
3
- **General considerations:**
3
+ There are two templates examples in this repo:
4
+ - `redis` - basic non-persistent template. It is good for review-apps or staging or where no persistence is required
5
+ - `redis2` - basic persistent template. Good for production where persistence is needed, but cluster is overkill.
6
+
7
+ ## Option 1: use SLAVEOF (easier way)
8
+
9
+ 1. create a redis workload that will accept data
10
+ 2. execute `SLAVEOF source_host source_port`, if needed use `masterauth` to provide auth details
11
+ 3. wait for replication to pick up all changes (usually quickly), use `INFO` or `DBSIZE` to check progress
12
+ 4. stop app completely and ensure nothing is writing to any of redises
13
+ 5. execute `SLAVEOF no one` to disconnect replication
14
+ 6. switch `REDIS_URL` in the app to point to new server
15
+ 7. start the app
16
+
17
+ ## Option 2: use Redis-RIOT (harder way, where option 1 is not possible)
18
+
19
+ ### General considerations:
4
20
 
5
21
  1. Heroku uses self-signed TLS certificates, which are not verifiable. It needs special handling by setting
6
- TLS verification to `none`, otherwise most apps are not able to connect.
22
+ The tool that satisfies those criteria is [Redis-RIOT](https://developer.redis.com/riot/riot-redis/index.html)
7
23
 
8
24
  2. We are moving to private Redis that don't have a public URL, so have to do it from a Control Plane GVC container.
9
25
 
10
26
  The tool that satisfies those criteria is [Redis-RIOT](https://developer.redis.com/riot/riot-redis/index.html)
11
27
 
12
- **Heroku Redis:**
28
+ ### Heroku Redis:
13
29
 
14
30
  As Redis-RIOT says, master redis should have keyspace-notifications set to `KA` to be able to do live replication.
15
31
  To do that:
@@ -23,7 +39,7 @@ Connect to heroku Redis CLI:
23
39
  heroku redis:cli -a my-app
24
40
  ```
25
41
 
26
- **Control Plane Redis:**
42
+ ### Control Plane Redis:
27
43
 
28
44
  Connect to Control Plane Redis CLI:
29
45
 
@@ -36,10 +52,10 @@ apt-get update
36
52
  apt-get install redis -y
37
53
 
38
54
  # connect to local cloud Redis
39
- redis-cli -u MY_CONTROL_PLANE_REDIS_URL
55
+ redis-cli -u MY_CONTROL_PLANE_REDIS_URL -p 6379
40
56
  ```
41
57
 
42
- **Useful Redis CLI commands:**
58
+ ### Useful Redis CLI commands:
43
59
 
44
60
  Quick-check keys qty:
45
61
  ```
@@ -49,7 +65,7 @@ info keyspace
49
65
  db0:keys=9496,expires=2941,avg_ttl=77670114535
50
66
  ```
51
67
 
52
- **Create a Control Plane sync workload**
68
+ ### Create a Control Plane sync workload
53
69
 
54
70
  ```
55
71
  name: riot-redis
@@ -76,7 +92,7 @@ command args:
76
92
  live
77
93
  ```
78
94
 
79
- **Sync process**
95
+ ### Sync process
80
96
 
81
97
  1. open 1st terminal window with heroku redis CLI, check keys qty
82
98
  2. open 2nd terminal window with controlplane redis CLI, check keys qty
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Command
4
- class Delete < Base
4
+ class Delete < Base # rubocop:disable Metrics/ClassLength
5
5
  NAME = "delete"
6
6
  OPTIONS = [
7
7
  app_option(required: true),
@@ -11,6 +11,7 @@ module Command
11
11
  DESCRIPTION = "Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload"
12
12
  LONG_DESCRIPTION = <<~DESC
13
13
  - Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload
14
+ - Also unbinds the app from the secrets policy, as long as both the identity and the policy exist (and are bound)
14
15
  - Will ask for explicit user confirmation
15
16
  DESC
16
17
  EXAMPLES = <<~EX
@@ -48,6 +49,7 @@ module Command
48
49
  check_images
49
50
  return unless confirm_delete(config.app)
50
51
 
52
+ unbind_identity_from_policy
51
53
  delete_volumesets
52
54
  delete_gvc
53
55
  delete_images
@@ -113,5 +115,21 @@ module Command
113
115
  end
114
116
  end
115
117
  end
118
+
119
+ def unbind_identity_from_policy
120
+ return if cp.fetch_identity(app_identity).nil?
121
+
122
+ policy = cp.fetch_policy(app_secrets_policy)
123
+ return if policy.nil?
124
+
125
+ is_bound = policy["bindings"].any? do |binding|
126
+ binding["principalLinks"].any? { |link| link == app_identity_link }
127
+ end
128
+ return unless is_bound
129
+
130
+ step("Unbinding identity from policy") do
131
+ cp.unbind_identity_from_policy(app_identity_link, app_secrets_policy)
132
+ end
133
+ end
116
134
  end
117
135
  end
@@ -8,6 +8,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
8
8
  @api = ControlplaneApi.new
9
9
  @gvc = config.app
10
10
  @org = config.org
11
+
12
+ ensure_org_exists! if org
11
13
  end
12
14
 
13
15
  # profile
@@ -339,6 +341,11 @@ class Controlplane # rubocop:disable Metrics/ClassLength
339
341
  perform!(cmd)
340
342
  end
341
343
 
344
+ def unbind_identity_from_policy(identity_link, policy)
345
+ cmd = "cpln policy remove-binding #{policy} --org #{org} --identity #{identity_link} --permission reveal"
346
+ perform!(cmd)
347
+ end
348
+
342
349
  # apply
343
350
  def apply_template(data) # rubocop:disable Metrics/MethodLength
344
351
  Tempfile.create do |f|
@@ -404,6 +411,18 @@ class Controlplane # rubocop:disable Metrics/ClassLength
404
411
 
405
412
  private
406
413
 
414
+ def org_exists?
415
+ items = api.list_orgs["items"]
416
+ items.any? { |item| item["name"] == org }
417
+ end
418
+
419
+ def ensure_org_exists!
420
+ return if org_exists?
421
+
422
+ raise "Can't find org '#{org}', please create it in the Control Plane dashboard " \
423
+ "or ensure that the name is correct."
424
+ end
425
+
407
426
  # `output_mode` can be :all, :errors_only or :none.
408
427
  # If not provided, it will be determined based on the `HIDE_COMMAND_OUTPUT` env var
409
428
  # or the return value of `Shell.should_hide_output?`.
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ControlplaneApi # rubocop:disable Metrics/ClassLength
4
+ def list_orgs
5
+ api_json("/org", method: :get)
6
+ end
7
+
4
8
  def gvc_list(org:)
5
9
  api_json("/org/#{org}/gvc", method: :get)
6
10
  end
data/lib/cpl/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpl
4
- VERSION = "2.0.0.rc.1"
5
- MIN_CPLN_VERSION = "0.0.71"
4
+ VERSION = "2.0.0"
5
+ MIN_CPLN_VERSION = "2.0.1"
6
6
  end
data/lib/cpl.rb CHANGED
@@ -146,9 +146,22 @@ module Cpl
146
146
  ::Command::Base.all_commands.merge(deprecated_commands)
147
147
  end
148
148
 
149
+ def self.process_option_params(params)
150
+ # Ensures that if no value is provided for a non-boolean option (e.g., `cpl command --option`),
151
+ # it defaults to an empty string instead of the option name (which is the default Thor behavior)
152
+ params[:lazy_default] ||= "" if params[:type] != :boolean
153
+
154
+ params
155
+ end
156
+
149
157
  @commands_with_required_options = []
150
158
  @commands_with_extra_options = []
151
159
 
160
+ ::Command::Base.common_options.each do |option|
161
+ params = process_option_params(option[:params])
162
+ class_option(option[:name], **params)
163
+ end
164
+
152
165
  all_base_commands.each do |command_key, command_class| # rubocop:disable Metrics/BlockLength
153
166
  deprecated = deprecated_commands[command_key]
154
167
 
@@ -157,7 +170,7 @@ module Cpl
157
170
  usage = command_class::USAGE.empty? ? name : command_class::USAGE
158
171
  requires_args = command_class::REQUIRES_ARGS
159
172
  default_args = command_class::DEFAULT_ARGS
160
- command_options = command_class::OPTIONS + ::Command::Base.common_options
173
+ command_options = command_class::OPTIONS
161
174
  accepts_extra_options = command_class::ACCEPTS_EXTRA_OPTIONS
162
175
  description = command_class::DESCRIPTION
163
176
  long_description = command_class::LONG_DESCRIPTION
@@ -175,12 +188,7 @@ module Cpl
175
188
  long_desc(long_description)
176
189
 
177
190
  command_options.each do |option|
178
- params = option[:params]
179
-
180
- # Ensures that if no value is provided for a non-boolean option (e.g., `cpl command --option`),
181
- # it defaults to an empty string instead of the option name (which is the default Thor behavior)
182
- params[:lazy_default] ||= "" if params[:type] != :boolean
183
-
191
+ params = process_option_params(option[:params])
184
192
  method_option(option[:name], **params)
185
193
  end
186
194
 
@@ -208,7 +216,7 @@ module Cpl
208
216
  end
209
217
 
210
218
  begin
211
- Cpl::Cli.validate_options!(options, command_options)
219
+ Cpl::Cli.validate_options!(options)
212
220
 
213
221
  config = Config.new(args, options, required_options)
214
222
 
@@ -227,11 +235,11 @@ module Cpl
227
235
  check_unknown_options!(except: @commands_with_extra_options)
228
236
  stop_on_unknown_option!
229
237
 
230
- def self.validate_options!(options, command_options)
238
+ def self.validate_options!(options)
231
239
  options.each do |name, value|
232
240
  raise "No value provided for option '#{name}'." if value.to_s.strip.empty?
233
241
 
234
- params = command_options.find { |option| option[:name].to_s == name }[:params]
242
+ params = ::Command::Base.all_options.find { |option| option[:name].to_s == name }[:params]
235
243
  next unless params[:valid_regex]
236
244
 
237
245
  raise "Invalid value provided for option '#{name}'." unless value.match?(params[:valid_regex])
@@ -4,8 +4,8 @@ spec:
4
4
  type: standard
5
5
  containers:
6
6
  - name: memcached
7
- cpu: 3m
8
- memory: 10Mi
7
+ cpu: 25m
8
+ memory: 32Mi
9
9
  args:
10
10
  - "-l"
11
11
  - 0.0.0.0
@@ -15,7 +15,7 @@ spec:
15
15
  protocol: tcp
16
16
  defaultOptions:
17
17
  autoscaling:
18
- metric: latency
18
+ metric: disabled
19
19
  minScale: 1
20
20
  maxScale: 1
21
21
  capacityAI: false
@@ -23,7 +23,7 @@ spec:
23
23
  uri: "scratch://postgres-vol"
24
24
  defaultOptions:
25
25
  autoscaling:
26
- metric: latency
26
+ metric: disabled
27
27
  minScale: 1
28
28
  maxScale: 1
29
29
  capacityAI: false
data/templates/redis.yml CHANGED
@@ -4,15 +4,15 @@ spec:
4
4
  type: standard
5
5
  containers:
6
6
  - name: redis
7
- cpu: 3m
8
- memory: 20Mi
7
+ cpu: 25m
8
+ memory: 32Mi
9
9
  image: "redis:latest"
10
10
  ports:
11
11
  - number: 6379
12
12
  protocol: tcp
13
13
  defaultOptions:
14
14
  autoscaling:
15
- metric: latency
15
+ metric: disabled
16
16
  minScale: 1
17
17
  maxScale: 1
18
18
  capacityAI: false
@@ -0,0 +1,37 @@
1
+ kind: volumeset
2
+ name: redis-data
3
+ spec:
4
+ fileSystemType: ext4
5
+ initialCapacity: 10
6
+ performanceClass: general-purpose-ssd
7
+ ---
8
+ kind: workload
9
+ name: redis2
10
+ spec:
11
+ type: stateful
12
+ containers:
13
+ - name: redis
14
+ args:
15
+ - '--appendonly'
16
+ - 'yes'
17
+ - '--maxmemory'
18
+ - 25mb
19
+ cpu: 25m
20
+ memory: 32Mi
21
+ image: "redis:latest"
22
+ ports:
23
+ - number: 6379
24
+ protocol: tcp
25
+ volumes:
26
+ - path: /data
27
+ recoveryPolicy: retain
28
+ uri: cpln://volumeset/redis-data
29
+ defaultOptions:
30
+ autoscaling:
31
+ metric: disabled
32
+ minScale: 1
33
+ maxScale: 1
34
+ capacityAI: false
35
+ firewallConfig:
36
+ internal:
37
+ inboundAllowType: same-gvc
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpl
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.rc.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-05-11 00:00:00.000000000 Z
12
+ date: 2024-05-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: debug
@@ -310,6 +310,7 @@ files:
310
310
  - templates/postgres.yml
311
311
  - templates/rails.yml
312
312
  - templates/redis.yml
313
+ - templates/redis2.yml
313
314
  - templates/secrets.yml
314
315
  - templates/sidekiq.yml
315
316
  homepage: https://github.com/shakacode/heroku-to-control-plane
@@ -328,9 +329,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
328
329
  version: 2.7.0
329
330
  required_rubygems_version: !ruby/object:Gem::Requirement
330
331
  requirements:
331
- - - ">"
332
+ - - ">="
332
333
  - !ruby/object:Gem::Version
333
- version: 1.3.1
334
+ version: '0'
334
335
  requirements: []
335
336
  rubygems_version: 3.4.21
336
337
  signing_key: