Skip to content

Commit

Permalink
Merge branch 'main' into add-generator
Browse files Browse the repository at this point in the history
  • Loading branch information
justin808 authored Jan 4, 2024
2 parents 0170241 + 23d1d9f commit d3c42f3
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 24 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ _Please add entries here for your pull requests that are not yet released._

- Fixed issue where `info` command does not respect `CPLN_ORG` env var. [PR 88](https://github.com/shakacode/heroku-to-control-plane/pull/88) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
- Fixed issues with running `cpl --version` and `cpl --help` where no configuration file exists. [PR 100](https://github.com/shakacode/heroku-to-control-plane/pull/100) by [Mostafa Ahangarha](https://github.com/ahangarha).
- Fixed issue where `delete` command fails to delete apps with volumesets. [PR 123](https://github.com/shakacode/heroku-to-control-plane/pull/123) by [Rafael Gomes](https://github.com/rafaelgomesxyz).

### Added

Expand All @@ -27,6 +28,8 @@ _Please add entries here for your pull requests that are not yet released._
- Added option to only use `CPLN_ORG` and `CPLN_APP` env vars if `allow_org_override_by_env` and `allow_app_override_by_env` configs are set to `true` in `controlplane.yml`. [PR 109](https://github.com/shakacode/heroku-to-control-plane/pull/109) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
- Added `CPLN_LOCATION` env variable and `--location` option for `apply-template`, `ps`, `run`, `run:detached`. [PR 105](https://github.com/shakacode/heroku-to-control-plane/pull/105) by [Mostafa Ahangarha](https://github.com/ahangarha).
- Added `generate` command for creating basic Control Plane configuration directory. [PR 116](https://github.com/shakacode/heroku-to-control-plane/pull/116) by [Mostafa Ahangarhga](https://github.com/ahangarha).
- Added `--trace` option to all commands for more detailed logs. [PR 124](https://github.com/shakacode/heroku-to-control-plane/pull/124) by [justin808](https://github.com/justin808)
- Added better error message to check the org name in case of a 403 error. [PR 124](https://github.com/justin808) by [justin808](https://github.com/justin808)

### Changed

Expand Down
8 changes: 2 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ git clone https://github.com/shakacode/heroku-to-control-plane
alias cpl="~/projects/heroku-to-control-plane/bin/cpl"
```

Or set the path of the Ruby gem in your Gemfile.

```ruby
gem 'cpl', path: '~/projects/heroku-to-control-plane'
```

## Linting/Testing

Before committing or pushing code, be sure to:
Expand All @@ -36,6 +30,8 @@ overcommit --install

## Debugging

1. Use the `--verbose` option to see more detailed logs.
2. Use the `--trace` option to see full logging of HTTP requests. Warning, this will display keys to your logs or console.
1. Add a breakpoint (`debugger`) to any line of code you want to debug.
2. Modify the `lib/command/test.rb` file to trigger the code you want to test. To simulate a command, you can use
`Cpl::Cli.start` (e.g., `Cpl::Cli.start(["deploy-image", "-a", "my-app-name"])` would be the same as running
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ npm update -g @controlplane/cli

5. Run `cpln image docker-login --org <your-org>` to ensure that you have access to the Control Plane Docker registry.

6. Install Heroku to Control Plane `cpl` CLI, either as a [Ruby gem](https://rubygems.org/gems/cpl) or a local clone.
For information on the latter, see [CONTRIBUTING.md](CONTRIBUTING.md). You may also install `cpl` in your project's Gemfile.
6. Install Heroku to Control Plane `cpl` CLI as a [Ruby gem](https://rubygems.org/gems/cpl): `gem install cpl`. If you want to hack on the source code, see [CONTRIBUTING.md](CONTRIBUTING.md).

7. You can use [this Dockerfile](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/Dockerfile) as an example for your project. Ensure that you have Docker running.

Expand Down
2 changes: 1 addition & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --ima

### `delete`

- Deletes the whole app (GVC with all workloads and all images)
- Deletes the whole app (GVC with all workloads, all volumesets and all images)
- Will ask for explicit user confirmation

```sh
Expand Down
14 changes: 13 additions & 1 deletion lib/command/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def self.all_commands
end

def self.common_options
[org_option, verbose_option]
[org_option, verbose_option, trace_option]
end

def self.org_option(required: false)
Expand Down Expand Up @@ -207,6 +207,17 @@ def self.verbose_option(required: false)
}
end

def self.trace_option(required: false)
{
name: :trace,
params: {
desc: "Shows trace of API calls. WARNING: may contain sensitive data",
type: :boolean,
required: required
}
}
end

def self.all_options
methods.grep(/_option$/).map { |method| send(method.to_s) }
end
Expand Down Expand Up @@ -258,6 +269,7 @@ def latest_image(app = config.app, org = config.org)
end

def latest_image_next(app = config.app, org = config.org, commit: nil)
# debugger
commit ||= config.options[:commit]

@latest_image_next ||= {}
Expand Down
51 changes: 40 additions & 11 deletions lib/command/delete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,45 @@ class Delete < Base
app_option(required: true),
skip_confirm_option
].freeze
DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images)"
DESCRIPTION = "Deletes the whole app (GVC with all workloads, all volumesets and all images)"
LONG_DESCRIPTION = <<~DESC
- Deletes the whole app (GVC with all workloads and all images)
- Deletes the whole app (GVC with all workloads, all volumesets and all images)
- Will ask for explicit user confirmation
DESC

def call
return progress.puts("App '#{config.app}' does not exist.") if cp.fetch_gvc.nil?

check_volumesets
check_images
return unless confirm_delete

delete_volumesets
delete_gvc
delete_images
end

private

def check_volumesets
@volumesets = cp.fetch_volumesets["items"]
return progress.puts("No volumesets to delete.") unless @volumesets.any?

message = "The following volumesets will be deleted along with the app:"
volumesets_list = @volumesets.map { |volumeset| "- #{volumeset['name']}" }.join("\n")
progress.puts("#{Shell.color(message, :red)}\n#{volumesets_list}\n\n")
end

def check_images
@images = cp.query_images["items"]
.select { |image| image["name"].start_with?("#{config.app}:") }
return progress.puts("No images to delete.") unless @images.any?

message = "The following images will be deleted along with the app:"
images_list = @images.map { |image| "- #{image['name']}" }.join("\n")
progress.puts("#{Shell.color(message, :red)}\n#{images_list}\n\n")
end

def confirm_delete
return true if config.options[:yes]

Expand All @@ -33,22 +57,27 @@ def confirm_delete
end

def delete_gvc
return progress.puts("App '#{config.app}' does not exist.") if cp.fetch_gvc.nil?

step("Deleting app '#{config.app}'") do
cp.gvc_delete
end
end

def delete_images
images = cp.query_images["items"]
.filter_map { |item| item["name"] if item["name"].start_with?("#{config.app}:") }
def delete_volumesets
@volumesets.each do |volumeset|
step("Deleting volumeset '#{volumeset['name']}'") do
# If the volumeset is attached to a workload, we need to delete the workload first
workload = volumeset.dig("status", "usedByWorkload")&.split("/")&.last
cp.delete_workload(workload) if workload

return progress.puts("No images to delete.") unless images.any?
cp.delete_volumeset(volumeset["name"])
end
end
end

images.each do |image|
step("Deleting image '#{image}'") do
cp.image_delete(image)
def delete_images
@images.each do |image|
step("Deleting image '#{image['name']}'") do
cp.image_delete(image["name"])
end
end
end
Expand Down
5 changes: 5 additions & 0 deletions lib/core/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ def initialize(args, options, required_options)
ensure_required_options!

Shell.verbose_mode(options[:verbose])
trace_mode = options[:trace]
return unless trace_mode

ControlplaneApiDirect.trace = trace_mode
Shell.warn("Trace mode is enabled, this will print sensitive information to the console.")
end

def org
Expand Down
11 changes: 11 additions & 0 deletions lib/core/controlplane.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def image_build(image, dockerfile:, build_args: [], push: true)
# https://docs.controlplane.com/guides/push-image#step-2
# Might need to use `docker buildx build` if compatiblitity issues arise
cmd = "docker build --platform=linux/amd64 -t #{image} -f #{dockerfile}"
cmd += " --progress=plain" if ControlplaneApiDirect.trace

build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
cmd += " #{config.app_dir}"
Expand Down Expand Up @@ -235,6 +236,16 @@ def workload_exec(workload, location:, container: nil, command: nil)
perform!(cmd)
end

# volumeset

def fetch_volumesets(a_gvc = gvc)
api.list_volumesets(org: org, gvc: a_gvc)
end

def delete_volumeset(volumeset, a_gvc = gvc)
api.delete_volumeset(org: org, gvc: a_gvc, volumeset: volumeset)
end

# domain

def find_domain_route(data)
Expand Down
10 changes: 9 additions & 1 deletion lib/core/controlplane_api.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

class ControlplaneApi
class ControlplaneApi # rubocop:disable Metrics/ClassLength
def gvc_list(org:)
api_json("/org/#{org}/gvc", method: :get)
end
Expand Down Expand Up @@ -86,6 +86,14 @@ def delete_workload(org:, gvc:, workload:)
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :delete)
end

def list_volumesets(org:, gvc:)
api_json("/org/#{org}/gvc/#{gvc}/volumeset", method: :get)
end

def delete_volumeset(org:, gvc:, volumeset:)
api_json("/org/#{org}/gvc/#{gvc}/volumeset/#{volumeset}", method: :delete)
end

def list_domains(org:)
api_json("/org/#{org}/domain", method: :get)
end
Expand Down
20 changes: 18 additions & 2 deletions lib/core/controlplane_api_direct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ class ControlplaneApiDirect

API_TOKEN_REGEX = /^[\w\-._]+$/.freeze

def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLength
class << self
attr_accessor :trace
end

def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
trace = ControlplaneApiDirect.trace
uri = URI("#{api_host(host)}#{url}")
request = API_METHODS[method].new(uri)
request["Content-Type"] = "application/json"
Expand All @@ -26,7 +31,11 @@ def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLe

Shell.debug(method.upcase, "#{uri} #{body&.to_json}")

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }
http = Net::HTTP.new(uri.hostname, uri.port)
http.use_ssl = uri.scheme == "https"
http.set_debug_output($stdout) if trace

response = http.start { |ht| ht.request(request) }

case response
when Net::HTTPOK
Expand All @@ -35,6 +44,9 @@ def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLe
true
when Net::HTTPNotFound
nil
when Net::HTTPForbidden
org = self.class.parse_org(url)
raise("Double check your org #{org}. #{response} #{response.body}")
else
raise("#{response} #{response.body}")
end
Expand Down Expand Up @@ -65,4 +77,8 @@ def self.reset_api_token
remove_class_variable(:@@api_token) if defined?(@@api_token)
end
# rubocop:enable Style/ClassVars

def self.parse_org(url)
url.match(%r{^/org/([^/]+)})[1]
end
end
8 changes: 8 additions & 0 deletions spec/core/controlplane_api_direct_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,12 @@
end.to raise_error(RuntimeError, message)
end
end

describe ".parse_org" do
it "returns correct org" do
url = "/org/org1/gvc/gvc1"
org = described_instance.class.parse_org(url)
expect(org).to eq("org1")
end
end
end

0 comments on commit d3c42f3

Please sign in to comment.