Skip to content

Commit

Permalink
Merge pull request #1060 from travis-ci/sf-ey-core
Browse files Browse the repository at this point in the history
Port engineyard provider to use the ey-core gem
  • Loading branch information
svenfuchs committed Aug 21, 2019
2 parents 6c93d8d + fc22405 commit ffc4f2f
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 112 deletions.
4 changes: 0 additions & 4 deletions .travis/providers/engineyard/prepare
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ end

servers.each do |server|
run "EY_TOKEN=$ENGINEYARD_TOKEN ./ey servers start #{server['provisioned_id']}"
# uri = URI.parse("https://api.engineyard.com/servers/#{server['provisioned_id']}/start")
# http = Net::HTTP.new(uri.host)
# http.set_debug_output($stdout)
# http.put(uri.path, '', headers)
end


Expand Down
2 changes: 1 addition & 1 deletion lib/dpl/providers/bintray.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Bintray < Provider
tbd
str

gem 'json', '~> 2.2.0'
gem 'json'

opt '--user USER', 'Bintray user', required: true
opt '--key KEY', 'Bintray API key', required: true, secret: true
Expand Down
123 changes: 62 additions & 61 deletions lib/dpl/providers/engineyard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,100 @@ class Engineyard < Provider
tbd
str

gem 'engineyard-cloud-client', '~> 2.1.0'
gem 'ey-core', '~> 3.5'

required :api_key, [:email, :password]

opt '--api_key KEY', 'Engine Yard API key', secret: true
opt '--email EMAIL', 'Engine Yard account email'
opt '--password PASS', 'Engine Yard password', secret: true
opt '--app APP', 'Engine Yard application name', default: :repo_name
opt '--environment ENV', 'Engine Yard application environment'
opt '--migrate CMD', 'Engine Yard migration commands'
opt '--account NAME', 'Engine Yard account name'

msgs deploy: 'Deploying ...',
authenticated: 'Authenticated as %s',
invalid_migrate: 'Invalid migration command, try --migrate="rake db:migrate"',
multiple_envs: 'Multiple matches possible, please be more specific: %s',
env_entry: 'environment=%s account=%s',
deploy_done: 'Done: https://cloud.engineyard.com/apps/%s/environments/%s/deployments/%s/pretty',
deploy_failed: 'Deployment failed (see logs on Engine Yard)'

attr_reader :env, :token
opt '--api_key KEY', 'Engine Yard API key', secret: true
opt '--email EMAIL', 'Engine Yard account email'
opt '--password PASS', 'Engine Yard password', secret: true
opt '--app APP', 'Engine Yard application name', default: :repo_name
opt '--env ENV', 'Engine Yard application environment', alias: :environment
opt '--migrate CMD', 'Engine Yard migration commands'
opt '--account NAME', 'Engine Yard account name'

msgs deploy: 'Deploying ...',
login: 'Authenticating via email and password ...',
write_rc: 'Authenticating via api token ...',
authenticated: 'Authenticated as %{name}',
invalid_migrate: 'Invalid migration command, try --migrate="rake db:migrate"',
envs: 'Checking environment ...',
no_env: 'No matching environment found',
too_many_envs: 'Multiple environments match, please be more specific: %s',
env_entry: 'environment=%s account=%s'

cmds login: "ey-core login << str\n%{email}\n%{password}\nstr",
whoami: 'ey-core whoami',
envs: 'ey-core environments',
deploy: 'ey-core deploy %{deploy_opts}'

def login
authenticate
info :authenticated, api.current_user.email
api_key? ? write_rc : authenticate
info :authenticated, name: whoami
end

def validate
@env ||= resolve(envs)
error :invalid_migrate if invalid_migrate?
env
end

def deploy
print :deploy
poll_for_result(deployment).successful || error(:deploy_failed)
shell :deploy
end

private

def invalid_migrate?
migrate.is_a?(TrueClass) || migrate == 'true'
def authenticate
shell :login, echo: false, capture: true
end

def authenticate
@token ||= api_key || EY::CloudClient.new.authenticate!(email, password)
rescue EY::CloudClient::Error => e
error e.message
def whoami
shell(:whoami, echo: false, capture: true) =~ /email\s*:\s*"(.+)"/ && $1
end

def api
@api ||= EY::CloudClient.new(token: token)
rescue EY::CloudClient::Error => e
error e.message
def write_rc
info :write_rc
write_file '~/.ey-core', "https://api.engineyard.com/: #{api_key}"
end

def deployment
opts = { ref: git_sha }
opts = opts.merge(migrate: true, migration_command: migrate) if migrate?
EY::CloudClient::Deployment.deploy(api, env, opts)
rescue EY::CloudClient::Error => e
error e.message
def invalid_migrate?
migrate.is_a?(TrueClass) || migrate == 'true'
end

def envs
api.resolve_app_environments(
app_name: app,
account_name: account,
environment_name: environment,
remotes: git_remote_urls
)
def deploy_opts
opts = [%(--ref="#{git_sha}" --environment="#{env}")]
opts << opts_for(%i(app account))
opts << migrate_opt
opts.join(' ')
end

def resolve(envs)
envs.one_match { return envs.matches.first }
envs.no_matches { error envs.errors.join("\n").inspect }
envs.many_matches { |envs| multiple_env_matches(envs) }
def migrate_opt
migrate? ? opts_for(%i(migrate)) : '--no-migrate'
end

def multiple_env_matches(envs)
envs = envs.map { |env| msg(:env_entry) % [env.environment.name, env.environment.account.name] }
error msg(:multiple_envs) % envs.join(', ')
def env
@env ||= super || detect_env(envs)
end

def poll_for_result(deployment)
deployment = refresh(deployment) until deployment.finished?
info :deploy_done, deployment.app.id, deployment.environment.id, deployment.id
deployment
def detect_env(envs)
case envs.size
when 1 then envs.first[:name]
when 0 then error :no_env
else too_many_envs(envs)
end
end

def envs
lines = shell(:envs, echo: false, capture: true).split("\n")[2..-1] || []
envs = lines.map { |line| line.split('|')[1..-1].map(&:strip) }
envs = envs.map { |pair| %i(name account).zip(pair).to_h }
envs.select { |env| env[:name] == opts[:env] } if env?
envs
end

def refresh(deployment)
sleep 5
print '.'
EY::CloudClient::Deployment.get(api, deployment.app_environment, deployment.id)
def too_many_envs(envs)
envs = envs.map { |env| msg(:env_entry) % env.values_at(:name, :account) }
error msg(:too_many_envs) % envs.join(', ')
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/dpl/providers/heroku.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def self.new(ctx, args)
end

gem 'faraday', '~> 0.9.2'
gem 'json', '~> 2.2.0'
gem 'json'
gem 'netrc', '~> 0.11.0'
gem 'rendezvous', '~> 0.1.3'

Expand Down
2 changes: 1 addition & 1 deletion lib/dpl/providers/npm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Npm < Provider
tbd
str

gem 'json', '~> 2.2.0'
gem 'json'

opt '--email EMAIL', 'npm account email'
opt '--api_token TOKEN', 'npm api token', alias: :api_key, required: true, secret: true, note: 'can be retrieved from your local ~/.npmrc file', see: 'https://docs.npmjs.com/creating-and-viewing-authentication-tokens'
Expand Down
2 changes: 1 addition & 1 deletion lib/dpl/providers/surge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Surge < Provider

node_js '>= 8.8.1'

gem 'json', '~> 2.2.0'
gem 'json'
npm :surge
env :surge

Expand Down
2 changes: 1 addition & 1 deletion lib/dpl/providers/testfairy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Testfairy < Provider
tbd
str

gem 'json', '~> 2.2.0'
gem 'json'
gem 'multipart-post', '~> 2.0.0', require: 'net/http/post/multipart'

opt '--api_key KEY', 'TestFairy API key', required: true, secret: true
Expand Down
103 changes: 61 additions & 42 deletions spec/dpl/providers/engineyard_spec.rb
Original file line number Diff line number Diff line change
@@ -1,68 +1,87 @@
describe Dpl::Providers::Engineyard do
let(:args) { |e| args_from_description(e) }
let(:args) { |e| %w(--api_key key) + args_from_description(e) }
let(:headers) { { content_type: 'application/json' } }

def response(key)
{ body: fixture(:engineyard, "#{key}.json"), status: 200, headers: headers }
let(:envs) do
<<-str.gsub(/^\s+/, '')
ID | Name | Account
---|------|-----------
1 | env | account
str
end

let(:whoami) do
<<-str.gsub(/^\s+/, '')
User:f7ecb9e2-946c-47ae-8ae1-2ef44aab3486 {
email : "dpl-test@travis-ci.org"
}
str
end

before do
stub_request(:post, %r(/authenticate)).to_return(response(:auth))
stub_request(:get, %r(/current_user)).to_return(response(:user))
stub_request(:get, %r(/app_environments)).to_return(response(:app_env_one))
stub_request(:post, %r(/deploy)).to_return(response(:deploy))
stub_request(:get, %r(/deployments/\d+)).to_return(response(:deployment))
ctx.stdout.update(
envs: envs,
whoami: whoami
)
end

before { |c| subject.run unless c.example_group.metadata[:run].is_a?(FalseClass) }

describe 'given --api_key key' do
before { subject.run }
it { should have_run '[info] Authenticated as test@test.test' }
it { should have_run '[print] Deploying ...' }
it { should have_run '[print] .' }
it { should have_run '[info] Done: https://cloud.engineyard.com/apps/7/environments/7/deployments/2/pretty' }
it { should have_run '[info] Authenticating via api token ...' }
it { should have_run 'ey-core whoami' }
it { should have_run '[info] Authenticated as dpl-test@travis-ci.org' }
it { should have_run '[info] Setting the build environment up for the deployment' }
it { should have_run '[info] Checking environment ...' }
it { should have_run 'ey-core environments' }
it { should have_run '[info] Deploying ...' }
it { should have_run '[info] $ ey-core deploy --ref="sha" --environment="env" --app="dpl" --no-migrate' }
it { should have_run 'ey-core deploy --ref="sha" --environment="env" --app="dpl" --no-migrate' }
it { should have_written '~/.ey-core', 'https://api.engineyard.com/: key' }
end

describe 'given --email email --password password' do
before { subject.run }
it { should have_run '[info] Authenticated as test@test.test' }
let(:args) { |e| args_from_description(e) }
it { should have_run '[info] Authenticating via email and password ...' }
it { should have_run "ey-core login << str\nemail\npassword\nstr" }
end

describe 'no credentials' do
describe 'no credentials', run: false do
let(:args) { [] }
it { expect { subject.run }.to raise_error 'Missing options: api_key, or email and password' }
end

# These are rather hard to test in a more meaningful way, as they are passed
# to the EY Ruby client, which returns an OOP representation of the API
# response. This would require lots of stubbing ...
context do
let(:args) { |e| %w(--api_key key) + args_from_description(e) }
before { subject.run }

describe 'given --app app' do
it { should have_attributes app: 'app' }
end
describe 'given --app app' do
it { should have_run 'ey-core deploy --ref="sha" --environment="env" --app="app" --no-migrate' }
end

describe 'given --environment env' do
it { should have_attributes environment: 'env' }
end
describe 'given --env other' do
it { should have_run 'ey-core deploy --ref="sha" --environment="other" --app="dpl" --no-migrate' }
end

describe 'given --migrate cmd' do
it { should have_attributes migrate: 'cmd' }
end
describe 'given --account account' do
it { should have_run 'ey-core deploy --ref="sha" --environment="env" --app="dpl" --account="account" --no-migrate' }
end

describe 'given --account account' do
it { should have_attributes account: 'account' }
end
describe 'given --migrate cmd' do
it { should have_run 'ey-core deploy --ref="sha" --environment="env" --app="dpl" --migrate="cmd"' }
end

describe 'no env matches, given --api_key key' do
before { stub_request(:get, %r(/app_environments)).to_return(response(:app_env_none)) }
it { expect { subject.run }.to raise_error /No environment found matching/ }
describe 'no env matches', run: false do
let(:envs) { '' }
it { expect { subject.run }.to raise_error /No matching environment found/ }
end

describe 'multiple envs match, given --api_key key' do
before { stub_request(:get, %r(/app_environments)).to_return(response(:app_env_multi)) }
it { expect { subject.run }.to raise_error /Multiple matches possible/ }
describe 'multiple envs match', run: false do
let(:envs) do
<<-str.gsub(/^\s+/, '')
ID | Name | Account
---|------|-----------
1 | one | account
2 | two | account
str
end

it { expect { subject.run }.to raise_error 'Multiple environments match, please be more specific: environment=one account=account, environment=two account=account' }
end
end

0 comments on commit ffc4f2f

Please sign in to comment.