dpl 1.4.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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