Skip to content

Commit

Permalink
Small improvements (#83)
Browse files Browse the repository at this point in the history
* feat: add open-console command

* feat: show help when calling cpl with no command

* feat: allow setting org with CPLN_ORG env var

* feat: add --verbose option

* docs: update changelog

* docs: update readme
  • Loading branch information
rafaelgomesxyz authored Oct 25, 2023
1 parent 267a488 commit 97be1a1
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 24 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ _Please add entries here for your pull requests that are not yet released._

- Fixed failed build on MacOS by adding platform flag and fixed multiple files in yaml document for template. [PR 81](https://github.com/shakacode/heroku-to-control-plane/pull/81) by [justin808](https://github.com/justin808).

### Added

- Added `open-console` command to open the app console on Control Plane. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
- Added option to set the org with a `CPLN_ORG` env var. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
- Added `--verbose` option to all commands for more detailed logs. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).

### Changed

- Calling `cpl` with no command now shows the help menu. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).

## [1.1.1] - 2023-09-23

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ For the typical Rails app, this means:

## Installation

1. Ensure your [Control Plane](https://controlplane.com) account is set up. Set up an `organization` <your-org> for testing in that account and modify the value for `aliases.common.cpln_org` in `.controlplane/controlplane.yml`. If you need an organization, please [contact Shakacode](mailto:[email protected]).
1. Ensure your [Control Plane](https://controlplane.com) account is set up. Set up an `organization` <your-org> for testing in that account and modify the value for `aliases.common.cpln_org` in `.controlplane/controlplane.yml`, or you can also set it with the `CPLN_ORG` environment variable. If you need an organization, please [contact Shakacode](mailto:[email protected]).

2. Install [Node.js](https://nodejs.org/en) (required for Control Plane CLI).

Expand Down Expand Up @@ -138,7 +138,7 @@ The `cpl` gem is based on several configuration files within a `/.controlplane`
├─ entrypoint.sh
```

1. `controlplane.yml` describes the overall application. Be sure to have <your-org> as the value for `aliases.common.cpln_org`.
1. `controlplane.yml` describes the overall application. Be sure to have <your-org> as the value for `aliases.common.cpln_org`, or set it with the `CPLN_ORG` environment variable.
2. `Dockerfile` builds the production application. `entrypoint.sh` is an _example_ entrypoint script for the production application, referenced in your Dockerfile.
3. `templates` directory contains the templates for the various workloads, such as `rails.yml` and `postgres.yml`.
4. `templates/gvc.yml` defines your project's GVC (like a Heroku app). More importantly, it contains ENV values for the app.
Expand Down
9 changes: 9 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ cpl open -a $APP_NAME
cpl open -a $APP_NAME -w $WORKLOAD_NAME
```
### `open-console`
- Opens the app console on Control Plane in the default browser
- Can also go directly to a workload page if `--workload` is provided
```sh
cpl open-console -a $APP_NAME
```
### `promote-app-from-upstream`
- Copies the latest image from upstream, runs a release script (optional), and deploys the image
Expand Down
12 changes: 12 additions & 0 deletions lib/command/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ def self.wait_option(title = "", required: false)
}
end

def self.verbose_option(required: false)
{
name: :verbose,
params: {
aliases: ["-d"],
desc: "Shows detailed logs",
type: :boolean,
required: required
}
}
end

def self.all_options
methods.grep(/_option$/).map { |method| send(method.to_s) }
end
Expand Down
5 changes: 5 additions & 0 deletions lib/command/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ class Config < Base
EX

def call # rubocop:disable Metrics/MethodLength
if config.org_comes_from_env
puts Shell.color("Org comes from CPLN_ORG env var", :yellow)
puts
end

if config.app
puts "#{Shell.color("Current config (app '#{config.app}')", :blue)}:"
puts pretty_print(config.current)
Expand Down
7 changes: 5 additions & 2 deletions lib/command/copy_image_from_upstream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def call # rubocop:disable Metrics/MethodLength
ensure_docker_running!

@upstream = config[:upstream]
@upstream_org = config.apps[@upstream.to_sym][:cpln_org]
@upstream_org = config.apps[@upstream.to_sym][:cpln_org] || ENV.fetch("CPLN_ORG_UPSTREAM", nil)
ensure_upstream_org!

create_upstream_profile
Expand All @@ -51,7 +51,10 @@ def ensure_docker_running!
end

def ensure_upstream_org!
raise "Can't find option 'cpln_org' for app '#{@upstream}' in 'controlplane.yml'." unless @upstream_org
return if @upstream_org

raise "Can't find option 'cpln_org' for app '#{@upstream}' in 'controlplane.yml', " \
"and CPLN_ORG_UPSTREAM env var is not set."
end

def create_upstream_profile
Expand Down
7 changes: 5 additions & 2 deletions lib/command/info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,20 @@ def fetch_app_workloads(org) # rubocop:disable Metrics/MethodLength
end
end

def orgs # rubocop:disable Metrics/MethodLength
def orgs # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
result = []

if config.options[:org]
result.push(config.options[:org])
else
org_from_env = ENV.fetch("CPLN_ORG", nil)
result.push(org_from_env) if org_from_env

config.apps.each do |app_name, app_options|
next if config.app && !app_matches?(config.app, app_name, app_options)

org = app_options[:cpln_org]
result.push(org) unless result.include?(org)
result.push(org) if org && !result.include?(org)
end
end

Expand Down
8 changes: 5 additions & 3 deletions lib/command/no_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ class NoCommand < Base
HIDE = true

def call
return unless config.options[:version]

Cpl::Cli.start(["version"])
if config.options[:version]
Cpl::Cli.start(["version"])
else
Cpl::Cli.start(["help"])
end
end
end
end
26 changes: 26 additions & 0 deletions lib/command/open_console.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module Command
class OpenConsole < Base
NAME = "open-console"
OPTIONS = [
app_option(required: true),
workload_option
].freeze
DESCRIPTION = "Opens the app console on Control Plane in the default browser"
LONG_DESCRIPTION = <<~DESC
- Opens the app console on Control Plane in the default browser
- Can also go directly to a workload page if `--workload` is provided
DESC

def call
workload = config.options[:workload]
url = "https://console.cpln.io/console/org/#{config.org}/gvc/#{config.app}"
url += "/workload/#{workload}" if workload
url += "/-info"
opener = `which xdg-open open`.split("\n").grep_v("not found").first

exec %(#{opener} "#{url}")
end
end
end
23 changes: 20 additions & 3 deletions lib/core/config.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# frozen_string_literal: true

class Config
class Config # rubocop:disable Metrics/ClassLength
attr_reader :config, :current,
:org, :app, :apps, :app_dir,
:org, :org_comes_from_env, :app, :apps, :app_dir,
# command line options
:args, :options

Expand All @@ -12,10 +12,13 @@ def initialize(args, options)
@args = args
@options = options
@org = options[:org]
@org_comes_from_env = false
@app = options[:app]

load_app_config
load_apps

Shell.verbose_mode(options[:verbose])
end

def [](key)
Expand Down Expand Up @@ -48,6 +51,13 @@ def ensure_current_config_app!(app_name)
raise "Can't find app '#{app_name}' in 'controlplane.yml'." unless current
end

def ensure_current_config_org!(app_name)
return if @org

raise "Can't find option 'cpln_org' for app '#{app_name}' in 'controlplane.yml', " \
"and CPLN_ORG env var is not set."
end

def ensure_config!
raise "'controlplane.yml' is empty." unless config
end
Expand All @@ -66,8 +76,15 @@ def app_matches_current?(app_name, app_options)

def pick_current_config(app_name, app_options)
@current = app_options
@org = self[:cpln_org]
ensure_current_config_app!(app_name)

if current.key?(:cpln_org)
@org = current.fetch(:cpln_org)
else
@org = ENV.fetch("CPLN_ORG", nil)
@org_comes_from_env = true
end
ensure_current_config_org!(app_name)
end

def load_apps # rubocop:disable Metrics/MethodLength
Expand Down
35 changes: 25 additions & 10 deletions lib/core/controlplane.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ def profile_exists?(profile)

def profile_create(profile, token)
cmd = "cpln profile create #{profile} --token #{token}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

def profile_delete(profile)
cmd = "cpln profile delete #{profile}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

Expand Down Expand Up @@ -61,25 +61,25 @@ def image_delete(image)

def image_login(org_name = config.org)
cmd = "cpln image docker-login --org #{org_name}"
cmd += " > /dev/null 2>&1" if Shell.tmp_stderr
cmd += " > /dev/null 2>&1" if Shell.should_hide_output?
perform!(cmd)
end

def image_pull(image)
cmd = "docker pull #{image}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

def image_tag(old_tag, new_tag)
cmd = "docker tag #{old_tag} #{new_tag}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

def image_push(image)
cmd = "docker push #{image}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

Expand Down Expand Up @@ -148,7 +148,11 @@ def workload_get_replicas(workload, location:)
end

def workload_get_replicas_safely(workload, location:)
cmd = "cpln workload get-replicas #{workload} #{gvc_org} --location #{location} -o yaml 2> /dev/null"
cmd = "cpln workload get-replicas #{workload} #{gvc_org} --location #{location} -o yaml"
cmd += " 2> /dev/null" if Shell.should_hide_output?

Shell.debug("CMD", cmd)

result = `#{cmd}`
$CHILD_STATUS.success? ? YAML.safe_load(result) : nil
end
Expand Down Expand Up @@ -180,7 +184,7 @@ def workload_deployments_ready?(workload, expected_status:)
def workload_set_image_ref(workload, container:, image:)
cmd = "cpln workload update #{workload} #{gvc_org}"
cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

Expand Down Expand Up @@ -208,7 +212,7 @@ def set_workload_suspend(workload, value)

def workload_force_redeployment(workload)
cmd = "cpln workload force-redeployment #{workload} #{gvc_org}"
cmd += " > /dev/null" if Shell.tmp_stderr
cmd += " > /dev/null" if Shell.should_hide_output?
perform!(cmd)
end

Expand Down Expand Up @@ -282,10 +286,15 @@ def apply_template(data) # rubocop:disable Metrics/MethodLength
f.rewind
cmd = "cpln apply #{gvc_org} --file #{f.path}"
if Shell.tmp_stderr
cmd += " 2> #{Shell.tmp_stderr.path}"
cmd += " 2> #{Shell.tmp_stderr.path}" if Shell.should_hide_output?

Shell.debug("CMD", cmd)

result = `#{cmd}`
$CHILD_STATUS.success? ? parse_apply_result(result) : false
else
Shell.debug("CMD", cmd)

result = `#{cmd}`
$CHILD_STATUS.success? ? parse_apply_result(result) : exit(false)
end
Expand Down Expand Up @@ -332,14 +341,20 @@ def parse_apply_result(result) # rubocop:disable Metrics/CyclomaticComplexity, M
private

def perform(cmd)
Shell.debug("CMD", cmd)

system(cmd)
end

def perform!(cmd)
Shell.debug("CMD", cmd)

system(cmd) || exit(false)
end

def perform_yaml(cmd)
Shell.debug("CMD", cmd)

result = `#{cmd}`
$CHILD_STATUS.success? ? YAML.safe_load(result) : exit(false)
end
Expand Down
2 changes: 2 additions & 0 deletions lib/core/controlplane_api_direct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLe
request["Authorization"] = api_token
request.body = body.to_json if body

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) }

case response
Expand Down
14 changes: 13 additions & 1 deletion lib/core/shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class Shell
class << self
attr_reader :tmp_stderr
attr_reader :tmp_stderr, :verbose
end

def self.shell
Expand Down Expand Up @@ -50,4 +50,16 @@ def self.warn_deprecated(message)
def self.abort(message)
Kernel.abort(color("ERROR: #{message}", :red))
end

def self.verbose_mode(verbose)
@verbose = verbose
end

def self.debug(prefix, message)
stderr.puts("\n[#{color(prefix, :red)}] #{message}") if verbose
end

def self.should_hide_output?
tmp_stderr && !verbose
end
end
2 changes: 1 addition & 1 deletion lib/cpl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def self.all_base_commands
usage = command_class::USAGE.empty? ? name : command_class::USAGE
requires_args = command_class::REQUIRES_ARGS
default_args = command_class::DEFAULT_ARGS
command_options = command_class::OPTIONS
command_options = command_class::OPTIONS + [::Command::Base.verbose_option]
description = command_class::DESCRIPTION
long_description = command_class::LONG_DESCRIPTION
examples = command_class::EXAMPLES
Expand Down

0 comments on commit 97be1a1

Please sign in to comment.