dpl 1.4.2 → 1.4.3

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
  SHA1:
3
- metadata.gz: af89a10a5a1d66462990934a93215af341bbbfcf
4
- data.tar.gz: 5400021aba1ffcea9549d910773ad0c90afd2ad7
3
+ metadata.gz: b842f79343d961133954e00f0083315e5d99c8bf
4
+ data.tar.gz: f4a1be0a2e6a3a3f1c8cea71c5c9914ca421a94b
5
5
  SHA512:
6
- metadata.gz: dfa28b8549431d85202aa2b5148bd8afdb1e1243c0454682484bf9e7df19ac701c1ddb98170bf964fbe5341d11ab1a3765b301ff0fb36ef7e2f632cc60501cc3
7
- data.tar.gz: b109321326bec28b89d239a387f74008593161e887b96738bc1228ad03147d0b904cb2b910b16a9de109eef56c90a98c0350caa1911f0cde0f2f38d8fc5e17e5
6
+ metadata.gz: c86c355aaa1b52cbd8e1b303de16e29c1ee29c69ef994d5f9741c346b3aca8769fb4f0591dc74b0e83e5ee70ee0063f065069c5d51156161dd3c0a7930a7bf71
7
+ data.tar.gz: 8f9f27bac251d9a2e8b4cf1fa88ba58d2c5a224aba68af981d49acc26dfd369d7cae4b31e101ecf9194970d0046252da3d8754f289ec45034affc3c99fe6d699
data/README.md CHANGED
@@ -3,12 +3,14 @@ Deploy tool made for Continuous Deployment.
3
3
  Usage:
4
4
 
5
5
  dpl --provider=heroku --api-key=`heroku auth:token`
6
+ dpl --provider=cloudControl --deployment='<application>/<deployment>' --email=<email> --password=<password>
6
7
 
7
8
  Supported providers:
8
9
 
9
10
  * Heroku
10
11
  * Nodejitsu
11
12
  * Openshift
12
- * Engine Yard (experimental)
13
+ * cloudControl
14
+ * RubyGems
15
+ * Engine Yard
13
16
  * dotCloud (experimental)
14
- * RubyGems (experimental)
@@ -5,12 +5,13 @@ module DPL
5
5
  class Provider
6
6
  include FileUtils
7
7
 
8
- autoload :Heroku, 'dpl/provider/heroku'
9
- autoload :EngineYard, 'dpl/provider/engine_yard'
10
- autoload :DotCloud, 'dpl/provider/dot_cloud'
11
- autoload :Nodejitsu, 'dpl/provider/nodejitsu'
12
- autoload :Openshift, 'dpl/provider/openshift'
13
- autoload :RubyGems, 'dpl/provider/rubygems'
8
+ autoload :Heroku, 'dpl/provider/heroku'
9
+ autoload :EngineYard, 'dpl/provider/engine_yard'
10
+ autoload :DotCloud, 'dpl/provider/dot_cloud'
11
+ autoload :Nodejitsu, 'dpl/provider/nodejitsu'
12
+ autoload :Openshift, 'dpl/provider/openshift'
13
+ autoload :RubyGems, 'dpl/provider/rubygems'
14
+ autoload :CloudControl, 'dpl/provider/cloudcontrol'
14
15
 
15
16
  def self.new(context, options)
16
17
  return super if self < Provider
@@ -0,0 +1,96 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'net/https'
4
+
5
+ module DPL
6
+ class Provider
7
+ class CloudControl < Provider
8
+ attr_accessor :app_name
9
+ attr_accessor :dep_name
10
+
11
+ def initialize(context, options)
12
+ super
13
+ option(:email) && option(:password) && option(:deployment)
14
+ @app_name, @dep_name = options[:deployment].split('/')
15
+
16
+ @http = Net::HTTP.new('api.cloudcontrol.com', 443)
17
+ @http.use_ssl = true
18
+ end
19
+
20
+ def check_auth
21
+ headers_with_token
22
+ end
23
+
24
+ def check_app
25
+ response = api_call('GET', "/app/#{ app_name }/deployment/#{ dep_name }")
26
+ raise 'ERROR: application check failed' if response.code != '200'
27
+ @repository = JSON.parse(response.body)["branch"]
28
+ end
29
+
30
+ def setup_key(file)
31
+ data = { 'key' => File.read(file).chomp }
32
+ response = api_call('POST', "/user/#{ user['username'] }/key", JSON.dump(data))
33
+ raise 'ERROR: adding key failed' if response.code != '200'
34
+ key = JSON.parse response.body
35
+ @ssh_key_id = key['key_id']
36
+ end
37
+
38
+ def remove_key
39
+ response = api_call('DELETE', "/user/#{ user['username']}/key/#{ @ssh_key_id }")
40
+ raise 'ERROR: key removal failed' if response.code != '204'
41
+ end
42
+
43
+ def push_app
44
+ branch = (dep_name == 'default') ? 'master' : dep_name
45
+ context.shell "git push #{ @repository } #{ branch };"
46
+ deploy_app
47
+ end
48
+
49
+ private
50
+
51
+ def get_token
52
+ request = Net::HTTP::Post.new '/token/'
53
+ request.basic_auth options[:email], options[:password]
54
+ response = @http.request(request)
55
+ raise 'ERROR: authorization failed' if response.code != '200'
56
+ return JSON.parse response.body
57
+ end
58
+
59
+ def headers_with_token(options = {})
60
+ @token = get_token if options[:new_token] || @token.nil?
61
+ return {
62
+ 'Authorization' => %Q|cc_auth_token="#{ @token['token'] }"|,
63
+ 'Content-Type' => 'application/json'
64
+ }
65
+ end
66
+
67
+ def get_headers
68
+ headers = headers_with_token
69
+ response = api_call('GET', '/user/', nil, headers)
70
+ return headers if response.code == '200'
71
+
72
+ return headers_with_token :new_token => true
73
+ end
74
+
75
+ def api_call(method, path, data = nil, headers = nil)
76
+ return @http.send_request(method, path, data, headers || get_headers)
77
+ end
78
+
79
+ def deploy_app
80
+ data = {'version' => -1}
81
+ response = api_call('PUT', "/app/#{ app_name }/deployment/#{ dep_name }", JSON.dump(data))
82
+ raise 'ERROR: deployment failed' if response.code != '200'
83
+ end
84
+
85
+ def user
86
+ if @user.nil?
87
+ response = api_call('GET', '/user/')
88
+ raise 'ERROR: can not find the user' if response.code != '200'
89
+ users = JSON.parse response.body
90
+ @user = users[0]
91
+ end
92
+ return @user
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,9 +1,8 @@
1
+ require 'time'
2
+
1
3
  module DPL
2
4
  class Provider
3
5
  class EngineYard < Provider
4
- experimental "Engine Yard"
5
-
6
- requires 'engineyard'
7
6
  requires 'engineyard-cloud-client'
8
7
 
9
8
  def token
@@ -15,7 +14,7 @@ module DPL
15
14
  end
16
15
 
17
16
  def api
18
- @api ||= EY::CloudClient.new(token, EY::CLI::UI.new)
17
+ @api ||= EY::CloudClient.new(:token => token)
19
18
  end
20
19
 
21
20
  def check_auth
@@ -23,47 +22,67 @@ module DPL
23
22
  end
24
23
 
25
24
  def check_app
26
- @app = EY::CloudClient::App.all(api).detect do |app|
27
- app.name == option(:app)
25
+ remotes = `git remote -v`.scan(/\t[^\s]+\s/).map { |c| c.strip }.uniq
26
+ @current_sha = `git rev-parse HEAD`.chomp
27
+ resolver = api.resolve_app_environments(
28
+ :app_name => options[:app],
29
+ :account_name => options[:account],
30
+ :environment_name => options[:environment],
31
+ :remotes => remotes)
32
+ resolver.one_match { @app_env = resolver.matches.first }
33
+ resolver.no_matches { error resolver.errors.join("\n").inspect }
34
+ resolver.many_matches do |matches|
35
+ message = "Multiple matches possible, please be more specific:\n\n"
36
+ matches.each do |appenv|
37
+ message << "environment: '#{appenv.environment.name}' account: '#{appenv.environment.account.name}'\n"
38
+ end
39
+ error message
28
40
  end
41
+ @app_env
29
42
  end
30
43
 
31
- def setup_key(file)
32
- @key = EY::CloudClient::Keypair.create(api, {
33
- "name" => option(:key_name),
34
- "public_key" => File.read(file)
35
- })
44
+ def needs_key?
45
+ false
36
46
  end
37
47
 
38
- def remove_key
39
- @key.destroy if @key
48
+ def cleanup
49
+ #DONT
40
50
  end
41
51
 
42
52
  def push_app
43
- fail
44
- EY::CloudClient::Deployment.deploy(api, option(:environment), deploy_args)
45
- end
46
-
47
- def deploy_args
48
- deploy_args = { :ref => `git log --format="%H" -1`.chop }
49
- if options[:run]
50
- deploy_args[:migrate] = true
51
- deploy_args[:migrate_command] = Array(options[:run]).map { |c| "(#{c})" }.join(" && ")
52
- elsif options.include? :migrate
53
- deploy_args[:migrate] = options[:migrate]
53
+ deploy_opts = {:ref => @current_sha}
54
+ if command = options[:migrate]
55
+ if command === true || command === "true"
56
+ error("\"true\" doesn't look like a migration command, try --migrate=\"rake db:migrate\"")
57
+ end
58
+ deploy_opts[:migrate] = true
59
+ deploy_opts[:migration_command] = command
60
+ end
61
+ print "deploying "
62
+ deployment = EY::CloudClient::Deployment.deploy(api, @app_env, deploy_opts)
63
+ result = poll_for_result(deployment)
64
+ unless result.successful
65
+ error "Deployment failed (see logs on Engine Yard)"
54
66
  end
55
- deploy_args
56
67
  end
57
68
 
58
- def run(command)
59
- # commands run by deployment
69
+ def poll_for_result(deployment)
70
+ until deployment.finished?
71
+ sleep 5
72
+ #TODO: configurable timeout?
73
+ print "."
74
+ deployment = EY::CloudClient::Deployment.get(api, deployment.app_environment, deployment.id)
75
+ end
76
+ puts "DONE: https://cloud.engineyard.com/apps/#{deployment.app.id}/environments/#{deployment.environment.id}/deployments/#{deployment.id}/pretty"
77
+ deployment
60
78
  end
61
79
 
62
80
  def deploy
63
81
  super
64
- rescue EY::Error => error
65
- raise Error, error.message, error.backtrace
82
+ rescue EY::CloudClient::Error => e
83
+ error(e.message)
66
84
  end
85
+
67
86
  end
68
87
  end
69
88
  end
@@ -1,8 +1,6 @@
1
1
  module DPL
2
2
  class Provider
3
3
  class RubyGems < Provider
4
- experimental "RubyGems"
5
-
6
4
  requires 'gems'
7
5
 
8
6
  def setup_auth
@@ -1,3 +1,3 @@
1
1
  module DPL
2
- VERSION = '1.4.2'
2
+ VERSION = '1.4.3'
3
3
  end
@@ -0,0 +1,192 @@
1
+ require 'spec_helper'
2
+ require 'dpl/provider/cloudcontrol'
3
+
4
+ describe DPL::Provider::CloudControl do
5
+ subject :provider do
6
+ described_class.new(DummyContext.new, :deployment => 'foo_app/default', :email => 'foo@test.com', :password => 'password')
7
+ end
8
+
9
+ its(:app_name) { should == 'foo_app' }
10
+ its(:dep_name) { should == 'default' }
11
+
12
+ its(:needs_key?) { should be true }
13
+
14
+ describe 'constructor' do
15
+ it 'with wrong arguments' do
16
+ expect {
17
+ $stdout.should receive(:write).at_least(3).times
18
+ described_class.new(DummyContext.new, :foo_dep => 'foo_app/default', :email => 'foo@test.com', :password => 'password')
19
+ }.to raise_error(DPL::Error)
20
+ end
21
+ end
22
+
23
+ it '#check_auth should call #headers_with_token' do
24
+ provider.should receive(:headers_with_token)
25
+ provider.check_auth.should
26
+ end
27
+
28
+ describe '#check_app' do
29
+ it 'on deployment found' do
30
+ provider.should receive(:api_call).and_return double(
31
+ :code => '200',
32
+ :body => '{"branch":"foo_repo.git"}'
33
+ )
34
+ provider.instance_variable_get(:@repository).should be_nil
35
+ provider.check_app
36
+ provider.instance_variable_get(:@repository).should == 'foo_repo.git'
37
+ end
38
+
39
+ it 'on deployment not found' do
40
+ provider.should receive(:api_call).and_return double(:code => '410')
41
+ expect { provider.check_app }.to raise_error 'ERROR: application check failed'
42
+ end
43
+ end
44
+
45
+ describe '#setup_key' do
46
+ before do
47
+ File.should receive(:read).with('file').and_return('foo_key')
48
+ provider.should receive(:user).and_return({ 'username' => 'foo_user' })
49
+ end
50
+
51
+ it 'on api success' do
52
+ provider.should receive(:api_call).with('POST', '/user/foo_user/key', '{"key":"foo_key"}').and_return double(
53
+ :code => '200',
54
+ :body => '{ "key": "foo_key", "key_id": "foo_key_id"}'
55
+ )
56
+
57
+ provider.instance_variable_get(:@ssh_key_id).should be_nil
58
+ provider.setup_key 'file'
59
+ provider.instance_variable_get(:@ssh_key_id).should == 'foo_key_id'
60
+ end
61
+
62
+ it 'on api failure' do
63
+ provider.should receive(:api_call).with('POST', '/user/foo_user/key', '{"key":"foo_key"}').and_return double(:code => '401')
64
+
65
+ expect { provider.setup_key 'file' }.to raise_error 'ERROR: adding key failed'
66
+ end
67
+ end
68
+
69
+ describe '#remove_key' do
70
+ before do
71
+ provider.instance_variable_set(:@ssh_key_id, 'foo_key_id')
72
+ provider.should receive(:user).and_return({ 'username' => 'foo_user' })
73
+ end
74
+
75
+ it 'on api success' do
76
+ provider.should receive(:api_call).with('DELETE', '/user/foo_user/key/foo_key_id').and_return double(:code => '204')
77
+ provider.remove_key
78
+ end
79
+
80
+ it 'on api failure' do
81
+ provider.should receive(:api_call).with('DELETE', '/user/foo_user/key/foo_key_id').and_return double(:code => '410')
82
+ expect { provider.remove_key }.to raise_error 'ERROR: key removal failed'
83
+ end
84
+ end
85
+
86
+ it '#push_app shuld deploy the app' do
87
+ provider.instance_variable_set(:@repository, 'foo_repo.git')
88
+ context = double(:shell)
89
+ context.should receive(:shell).with("git push foo_repo.git master;")
90
+ provider.should receive(:context).and_return context
91
+ provider.should receive(:deploy_app)
92
+
93
+ provider.push_app
94
+ end
95
+
96
+ describe 'private method' do
97
+ describe '#get_token' do
98
+ it 'on api success' do
99
+ request = double()
100
+ request.should receive(:basic_auth).with('foo@test.com', 'password')
101
+ Net::HTTP::Post.should receive(:new).with('/token/').and_return request
102
+
103
+ provider.instance_variable_get(:@http).should receive(:request).and_return double(
104
+ :code => '200',
105
+ :body => '{ "token": "foo_token"}'
106
+ )
107
+
108
+ provider.instance_eval { get_token }.should == { 'token' => 'foo_token' }
109
+ end
110
+
111
+ it 'on api failure' do
112
+ provider.instance_variable_get(:@http).should receive(:request).and_return double(:code => '401')
113
+
114
+ expect do
115
+ provider.instance_eval { get_token }
116
+ end.to raise_error 'ERROR: authorization failed'
117
+ end
118
+ end
119
+
120
+ it '#headers_with_token should return headers' do
121
+ provider.should receive(:get_token).and_return({ 'token' => 'foo_token' })
122
+ expected_return = {
123
+ 'Authorization' => 'cc_auth_token="foo_token"',
124
+ 'Content-Type' => 'application/json'
125
+ }
126
+
127
+ provider.instance_eval { headers_with_token }.should == expected_return
128
+ end
129
+
130
+ describe '#get_headers' do
131
+ let(:expected_args) { [ 'GET', '/user/', nil, {'foo' => 'headers'} ] }
132
+
133
+ before do
134
+ provider.should receive(:headers_with_token).and_return({ 'foo' => 'headers' })
135
+ end
136
+
137
+ it 'on token valid' do
138
+ provider.should receive(:api_call).with(*expected_args).and_return double(:code => '200')
139
+ provider.instance_eval { get_headers }.should == { 'foo' => 'headers' }
140
+ end
141
+
142
+ it 'on token expired' do
143
+ provider.should receive(:api_call).with(*expected_args).and_return double(:code => '401')
144
+ provider.should receive(:headers_with_token).with({ :new_token => true})
145
+
146
+ provider.instance_eval { get_headers }
147
+ end
148
+ end
149
+
150
+ it '#api_call should send request' do
151
+ expected_args = [ "foo_method", "foo_path", "\"foo\":\"data\"", {"foo"=>"headers"} ]
152
+ provider.instance_variable_get(:@http).should receive(:send_request).with(*expected_args)
153
+
154
+ provider.instance_eval do
155
+ api_call('foo_method', 'foo_path', '"foo":"data"', { 'foo' => 'headers'})
156
+ end
157
+ end
158
+
159
+ describe '#deploy_app' do
160
+ it 'on api success' do
161
+ provider.should receive(:api_call).with('PUT', '/app/foo_app/deployment/default', '{"version":-1}').and_return double(:code => '200')
162
+ provider.instance_eval { deploy_app }
163
+ end
164
+
165
+ it 'on api failure' do
166
+ provider.should receive(:api_call).with('PUT', '/app/foo_app/deployment/default', '{"version":-1}').and_return double(:code => '410')
167
+ expect do
168
+ provider.instance_eval { deploy_app }
169
+ end.to raise_error 'ERROR: deployment failed'
170
+ end
171
+ end
172
+
173
+ describe '#user' do
174
+ it 'on api success' do
175
+ provider.should receive(:api_call).with('GET', '/user/').and_return double(
176
+ :code => '200',
177
+ :body => '["foo_user"]'
178
+ )
179
+
180
+ provider.instance_eval { user }.should == 'foo_user'
181
+ end
182
+
183
+ it 'on api failure' do
184
+ provider.should receive(:api_call).with('GET', '/user/').and_return double(:code => '410')
185
+
186
+ expect do
187
+ provider.instance_eval { user }
188
+ end.to raise_error 'ERROR: can not find the user'
189
+ end
190
+ end
191
+ end
192
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dpl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Haase
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-07 00:00:00.000000000 Z
11
+ date: 2013-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -85,6 +85,7 @@ files:
85
85
  - lib/dpl/cli.rb
86
86
  - lib/dpl/error.rb
87
87
  - lib/dpl/provider.rb
88
+ - lib/dpl/provider/cloudcontrol.rb
88
89
  - lib/dpl/provider/dot_cloud.rb
89
90
  - lib/dpl/provider/engine_yard.rb
90
91
  - lib/dpl/provider/heroku.rb
@@ -98,6 +99,7 @@ files:
98
99
  - notes/engine_yard.md
99
100
  - notes/heroku.md
100
101
  - spec/cli_spec.rb
102
+ - spec/provider/cloudcontrol_spec.rb
101
103
  - spec/provider/dotcloud_spec.rb
102
104
  - spec/provider/heroku_anvil_spec.rb
103
105
  - spec/provider/heroku_git_spec.rb
@@ -131,6 +133,7 @@ specification_version: 4
131
133
  summary: deploy tool
132
134
  test_files:
133
135
  - spec/cli_spec.rb
136
+ - spec/provider/cloudcontrol_spec.rb
134
137
  - spec/provider/dotcloud_spec.rb
135
138
  - spec/provider/heroku_anvil_spec.rb
136
139
  - spec/provider/heroku_git_spec.rb