lib/convox/client.rb in convox_installer-1.0.9 vs lib/convox/client.rb in convox_installer-2.0.0
- old
+ new
@@ -1,158 +1,194 @@
# frozen_string_literal: true
-require "logger"
-require "json"
-require "fileutils"
+require 'logger'
+require 'json'
+require 'fileutils'
+require 'rubygems'
module Convox
class Client
- CONVOX_DIR = File.expand_path("~/.convox").freeze
- AUTH_FILE = File.join(CONVOX_DIR, "auth")
- HOST_FILE = File.join(CONVOX_DIR, "host")
+ CONVOX_DIR = File.expand_path('~/.convox').freeze
+ AUTH_FILE = File.join(CONVOX_DIR, 'auth')
+ HOST_FILE = File.join(CONVOX_DIR, 'host')
attr_accessor :logger, :config
+ def cli_version_string
+ return @cli_version_string if @cli_version_string
+
+ cli_version_string ||= `convox --version`
+ return unless $CHILD_STATUS.success?
+
+ @cli_version_string = cli_version_string.chomp
+ end
+
+ def cli_version
+ return unless cli_version_string.is_a?(String)
+
+ if cli_version_string.match?(/^\d+\.\d+\.\d+/)
+ @cli_version ||= Gem::Version.new(version_string)
+ end
+ @cli_version
+ end
+
+ def convox_2_cli?
+ return false unless cli_version_string.is_a?(String)
+
+ cli_version_string.match?(/^20\d+$/)
+ end
+
+ def convox_3_cli?
+ return false if !cli_version_string.is_a?(String) ||
+ convox_2_cli? ||
+ !cli_version_string.match?(/^\d+\.\d+\.\d+/)
+
+ cli_version = Gem::Version.new(cli_version_string)
+ cli_version >= Gem::Version.new('3.0.0') &&
+ cli_version < Gem::Version.new('4.0.0')
+ end
+
def auth
load_auth_from_file
end
def initialize(options = {})
- @logger = Logger.new(STDOUT)
+ @logger = Logger.new($stdout)
logger.level = options[:log_level] || Logger::INFO
@config = options[:config] || {}
end
def backup_convox_host_and_rack
FileUtils.mkdir_p CONVOX_DIR
%w[host rack].each do |f|
path = File.join(CONVOX_DIR, f)
- if File.exist?(path)
- bak_file = "#{path}.bak"
- logger.info "Moving existing #{path} to #{bak_file}..."
- FileUtils.mv(path, bak_file)
- end
+ next unless File.exist?(path)
+
+ bak_file = "#{path}.bak"
+ logger.info "Moving existing #{path} to #{bak_file}..."
+ FileUtils.mv(path, bak_file)
end
end
def install_convox
- require_config(%i[ aws_region stack_name ])
+ require_config(%i[aws_region stack_name])
region = config.fetch(:aws_region)
stack_name = config.fetch(:stack_name)
if rack_already_installed?
logger.info "There is already a Convox stack named #{stack_name} " \
"in the #{region} AWS region. Using this rack. "
return true
end
require_config(%i[
- aws_region
- aws_access_key_id
- aws_secret_access_key
- stack_name
- instance_type
- ])
+ aws_region
+ aws_access_key_id
+ aws_secret_access_key
+ stack_name
+ instance_type
+ ])
logger.info "Installing Convox (#{stack_name})..."
env = {
- "AWS_REGION" => region,
- "AWS_ACCESS_KEY_ID" => config.fetch(:aws_access_key_id),
- "AWS_SECRET_ACCESS_KEY" => config.fetch(:aws_secret_access_key),
+ 'AWS_REGION' => region,
+ 'AWS_ACCESS_KEY_ID' => config.fetch(:aws_access_key_id),
+ 'AWS_SECRET_ACCESS_KEY' => config.fetch(:aws_secret_access_key)
}
- command = %Q{rack install aws \
+ command = %(rack install aws \
--name "#{config.fetch(:stack_name)}" \
"InstanceType=#{config.fetch(:instance_type)}" \
-"BuildInstance="}
+"BuildInstance=")
run_convox_command!(command, env)
end
def rack_already_installed?
- require_config(%i[ aws_region stack_name ])
+ require_config(%i[aws_region stack_name])
return unless File.exist?(AUTH_FILE)
region = config.fetch(:aws_region)
stack_name = config.fetch(:stack_name)
- auth.each do |host, password|
+ auth.each do |host, _password|
if host.match?(/^#{stack_name}-\d+\.#{region}\.elb\.amazonaws\.com$/)
return true
end
end
false
end
- def validate_convox_auth_and_set_host!
- require_config(%i[ aws_region stack_name ])
+ def validate_convox_auth_and_write_host!
+ require_config(%i[aws_region stack_name])
unless File.exist?(AUTH_FILE)
raise "Could not find auth file at #{AUTH_FILE}!"
end
region = config.fetch(:aws_region)
stack = config.fetch(:stack_name)
match_count = 0
matching_host = nil
- auth.each do |host, password|
+ auth.each do |host, _password|
if host.match?(/^#{stack}-\d+\.#{region}\.elb\.amazonaws\.com$/)
matching_host = host
match_count += 1
end
end
if match_count == 1
- set_host(matching_host)
+ write_host(matching_host)
return matching_host
end
- if match_count > 1
- error_message = "Found multiple matching hosts for "
- else
- error_message = "Could not find matching authentication for "
- end
+ error_message = if match_count > 1
+ 'Found multiple matching hosts for '
+ else
+ 'Could not find matching authentication for '
+ end
error_message += "region: #{region}, stack: #{stack}"
raise error_message
end
- def set_host(host)
+ def write_host(host)
logger.debug "Setting convox host to #{host} (in #{HOST_FILE})..."
- File.open(HOST_FILE, "w") { |f| f.puts host }
+ File.open(HOST_FILE, 'w') { |f| f.puts host }
end
def validate_convox_rack!
require_config(%i[
- aws_region
- stack_name
- instance_type
- ])
- logger.debug "Validating that convox rack has the correct attributes..."
+ aws_region
+ stack_name
+ instance_type
+ ])
+ logger.debug 'Validating that convox rack has the correct attributes...'
{
- provider: "aws",
+ provider: 'aws',
region: config.fetch(:aws_region),
type: config.fetch(:instance_type),
- name: config.fetch(:stack_name),
+ name: config.fetch(:stack_name)
}.each do |k, v|
convox_value = convox_rack_data[k.to_s]
if convox_value != v
raise "Convox data did not match! Expected #{k} to be '#{v}', " \
"but was: '#{convox_value}'"
end
end
- logger.debug "=> Convox rack has the correct attributes."
+ logger.debug '=> Convox rack has the correct attributes.'
true
end
def convox_rack_data
@convox_rack_data ||= begin
- logger.debug "Fetching convox rack attributes..."
+ logger.debug 'Fetching convox rack attributes...'
convox_output = `convox api get /system`
- raise "convox command failed!" unless $?.success?
+ raise 'convox command failed!' unless $CHILD_STATUS.success?
+
JSON.parse(convox_output)
end
end
def create_convox_app!
@@ -160,49 +196,50 @@
return true if convox_app_exists?
app_name = config.fetch(:convox_app_name)
logger.info "Creating app: #{app_name}..."
- logger.info "=> Documentation: " \
- "https://docs.convox.com/deployment/creating-an-application"
+ logger.info '=> Documentation: ' \
+ 'https://docs.convox.com/deployment/creating-an-application'
run_convox_command! "apps create #{app_name} --wait"
retries = 0
loop do
break if convox_app_exists?
+
if retries > 5
raise "Something went wrong while creating the #{app_name} app! " \
- "(Please wait a few moments and then restart the installation script.)"
+ '(Please wait a few moments and then restart the installation script.)'
end
logger.info "Waiting for #{app_name} to be ready..."
sleep 3
retries += 1
end
logger.info "=> #{app_name} app created!"
end
def set_default_app_for_directory!
- logger.info "Setting default app in ./.convox/app..."
- FileUtils.mkdir_p File.expand_path("./.convox")
- File.open(File.expand_path("./.convox/app"), "w") do |f|
+ logger.info 'Setting default app in ./.convox/app...'
+ FileUtils.mkdir_p File.expand_path('./.convox')
+ File.open(File.expand_path('./.convox/app'), 'w') do |f|
f.puts config.fetch(:convox_app_name)
end
end
def convox_app_exists?
require_config(%i[convox_app_name])
app_name = config.fetch(:convox_app_name)
logger.debug "Looking for existing #{app_name} app..."
convox_output = `convox api get /apps`
- raise "convox command failed!" unless $?.success?
+ raise 'convox command failed!' unless $CHILD_STATUS.success?
apps = JSON.parse(convox_output)
apps.each do |app|
- if app["name"] == app_name
+ if app['name'] == app_name
logger.debug "=> Found #{app_name} app."
return true
end
end
logger.debug "=> Did not find #{app_name} app."
@@ -215,76 +252,76 @@
bucket_name = config.fetch(:s3_bucket_name)
if s3_bucket_exists?
logger.info "#{bucket_name} S3 bucket already exists!"
else
logger.info "Creating S3 bucket resource (#{bucket_name})..."
- run_convox_command! "rack resources create s3 " \
+ run_convox_command! 'rack resources create s3 ' \
"--name \"#{bucket_name}\" " \
- "--wait"
+ '--wait'
retries = 0
loop do
break if s3_bucket_exists?
if retries > 10
raise "Something went wrong while creating the #{bucket_name} S3 bucket! " \
- "(Please wait a few moments and then restart the installation script.)"
+ '(Please wait a few moments and then restart the installation script.)'
end
- logger.debug "Waiting for S3 bucket to be ready..."
+ logger.debug 'Waiting for S3 bucket to be ready...'
sleep 3
retries += 1
end
- logger.debug "=> S3 bucket created!"
+ logger.debug '=> S3 bucket created!'
end
set_s3_bucket_cors_policy
end
def s3_bucket_exists?
require_config(%i[s3_bucket_name])
bucket_name = config.fetch(:s3_bucket_name)
logger.debug "Looking up S3 bucket resource: #{bucket_name}"
`convox api get /resources/#{bucket_name} 2>/dev/null`
- $?.success?
+ $CHILD_STATUS.success?
end
def s3_bucket_details
require_config(%i[s3_bucket_name])
@s3_bucket_details ||= begin
bucket_name = config.fetch(:s3_bucket_name)
logger.debug "Fetching S3 bucket resource details for #{bucket_name}..."
response = `convox api get /resources/#{bucket_name}`
- raise "convox command failed!" unless $?.success?
+ raise 'convox command failed!' unless $CHILD_STATUS.success?
bucket_data = JSON.parse(response)
- s3_url = bucket_data["url"]
+ s3_url = bucket_data['url']
matches = s3_url.match(
- /^s3:\/\/(?<access_key_id>[^:]*):(?<secret_access_key>[^@]*)@(?<bucket_name>.*)$/
+ %r{^s3://(?<access_key_id>[^:]*):(?<secret_access_key>[^@]*)@(?<bucket_name>.*)$}
)
match_keys = %i[access_key_id secret_access_key bucket_name]
unless matches && match_keys.all? { |k| matches[k].present? }
raise "#{s3_url} is an invalid S3 URL!"
end
{
access_key_id: matches[:access_key_id],
secret_access_key: matches[:secret_access_key],
- name: matches[:bucket_name],
+ name: matches[:bucket_name]
}
end
end
def set_s3_bucket_cors_policy
require_config(%i[aws_access_key_id aws_secret_access_key])
access_key_id = config.fetch(:aws_access_key_id)
secret_access_key = config.fetch(:aws_secret_access_key)
unless config.key? :s3_bucket_cors_policy
- logger.debug "No CORS policy provided in config: s3_bucket_cors_policy"
+ logger.debug 'No CORS policy provided in config: s3_bucket_cors_policy'
return
end
cors_policy_string = config.fetch(:s3_bucket_cors_policy)
bucket_name = s3_bucket_details[:name]
@@ -292,21 +329,21 @@
logger.debug "Looking up existing CORS policy for #{bucket_name}"
existing_cors_policy_string =
`AWS_ACCESS_KEY_ID=#{access_key_id} \
AWS_SECRET_ACCESS_KEY=#{secret_access_key} \
aws s3api get-bucket-cors --bucket #{bucket_name} 2>/dev/null`
- if $?.success? && existing_cors_policy_string.present?
+ if $CHILD_STATUS.success? && existing_cors_policy_string.present?
# Sort all the nested arrays so that the equality operator works
existing_cors_policy = JSON.parse(existing_cors_policy_string)
cors_policy_json = JSON.parse(cors_policy_string)
[existing_cors_policy, cors_policy_json].each do |policy_json|
- if policy_json.is_a?(Hash) && policy_json["CORSRules"]
- policy_json["CORSRules"].each do |rule|
- rule["AllowedHeaders"].sort! if rule["AllowedHeaders"]
- rule["AllowedMethods"].sort! if rule["AllowedMethods"]
- rule["AllowedOrigins"].sort! if rule["AllowedOrigins"]
- end
+ next unless policy_json.is_a?(Hash) && policy_json['CORSRules']
+
+ policy_json['CORSRules'].each do |rule|
+ rule['AllowedHeaders']&.sort!
+ rule['AllowedMethods']&.sort!
+ rule['AllowedOrigins']&.sort!
end
end
if existing_cors_policy == cors_policy_json
logger.debug "=> CORS policy is already up to date for #{bucket_name}."
@@ -315,63 +352,65 @@
end
begin
logger.info "Setting CORS policy for #{bucket_name}..."
- File.open("cors-policy.json", "w") { |f| f.puts cors_policy_string }
+ File.open('cors-policy.json', 'w') { |f| f.puts cors_policy_string }
`AWS_ACCESS_KEY_ID=#{access_key_id} \
AWS_SECRET_ACCESS_KEY=#{secret_access_key} \
aws s3api put-bucket-cors \
--bucket #{bucket_name} \
--cors-configuration "file://cors-policy.json"`
- unless $?.success?
- raise "Something went wrong while setting the S3 bucket CORS policy!"
+ unless $CHILD_STATUS.success?
+ raise 'Something went wrong while setting the S3 bucket CORS policy!'
end
+
logger.info "=> Successfully set CORS policy for #{bucket_name}."
ensure
- FileUtils.rm_f "cors-policy.json"
+ FileUtils.rm_f 'cors-policy.json'
end
end
def add_docker_registry!
require_config(%i[docker_registry_url docker_registry_username docker_registry_password])
registry_url = config.fetch(:docker_registry_url)
- logger.debug "Looking up existing Docker registries..."
+ logger.debug 'Looking up existing Docker registries...'
registries_response = `convox api get /registries`
- unless $?.success?
- raise "Something went wrong while fetching the list of registries!"
+ unless $CHILD_STATUS.success?
+ raise 'Something went wrong while fetching the list of registries!'
end
+
registries = JSON.parse(registries_response)
- if registries.any? { |r| r["server"] == registry_url }
+ if registries.any? { |r| r['server'] == registry_url }
logger.debug "=> Docker Registry already exists: #{registry_url}"
return true
end
logger.info "Adding Docker Registry: #{registry_url}..."
- logger.info "=> Documentation: " \
- "https://docs.convox.com/deployment/private-registries"
+ logger.info '=> Documentation: ' \
+ 'https://docs.convox.com/deployment/private-registries'
`convox registries add "#{registry_url}" \
"#{config.fetch(:docker_registry_username)}" \
"#{config.fetch(:docker_registry_password)}"`
- unless $?.success?
- raise "Something went wrong while adding the #{registry_url} registry!"
- end
+ return if $CHILD_STATUS.success?
+
+ raise "Something went wrong while adding the #{registry_url} registry!"
end
def default_service_domain_name
require_config(%i[convox_app_name default_service])
@default_service_domain_name ||= begin
- convox_domain = convox_rack_data["domain"]
- elb_name_and_region = convox_domain[/([^\.]*\.[^\.]*)\..*/, 1]
+ convox_domain = convox_rack_data['domain']
+ elb_name_and_region = convox_domain[/([^.]*\.[^.]*)\..*/, 1]
unless elb_name_and_region.present?
- raise "Something went wrong while parsing the ELB name and region! " \
+ raise 'Something went wrong while parsing the ELB name and region! ' \
"(#{elb_name_and_region})"
end
app = config.fetch(:convox_app_name)
service = config.fetch(:default_service)
@@ -381,20 +420,20 @@
end
def run_convox_command!(cmd, env = {})
command = "convox #{cmd}"
system env, command
- raise "Error running: #{command}" unless $?.success?
+ raise "Error running: #{command}" unless $CHILD_STATUS.success?
end
private
def load_auth_from_file
return {} unless File.exist?(AUTH_FILE)
begin
JSON.parse(File.read(AUTH_FILE))
- rescue
+ rescue StandardError
{}
end
end
def require_config(required_keys)