deployto 0.9.2 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,18 +2,10 @@
2
2
 
3
3
  Deployment tool for Platform-as-a-Service providers, with special support for multi-stage deploys (e.g. for staging environments).
4
4
 
5
- Platforms currently supported:
6
- * Efficient Cloud Platforms
7
-
8
- Platform support planned:
9
- * Heroku
10
- * Cloud Foundry Platforms
11
-
12
5
  ## Deploying a Ruby on Rails app
13
6
 
14
- gem install deploytool
15
- deploy add production portfolio.heroku.com
16
- deploy add staging portfolio.vcap.me
7
+ gem install deployto
8
+ deploy add production young-samurai-4@example.org
17
9
  deploy production
18
10
 
19
11
  ## Config file
@@ -22,6 +14,6 @@ deploy keeps a local config file .deployrc within the top-level sourcecode direc
22
14
 
23
15
  ## Legalese
24
16
 
25
- _Copyright 2011, Efficient Cloud Ltd.
17
+ Copyright 2011, Efficient Cloud Ltd.
26
18
 
27
19
  Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
@@ -1,22 +1,29 @@
1
+ require 'deploytool/version'
2
+
1
3
  class DeployTool::Command
2
4
  COMMANDS = ["to", "logs", "import", "export", "config"]
3
5
 
4
6
  def self.print_help
5
- puts "Deploytool Usage Instructions"
7
+ puts "Deploytool Version #{DeployTool::VERSION} Usage Instructions"
6
8
  puts ""
7
9
  puts "Add a target:"
8
- puts " deploy add production app1@demo.efficientcloud.com"
9
- puts " deploy add staging myapp.heroku.com"
10
- puts " deploy add failover api.cloudfoundry.com"
10
+ puts " deploy add production young-samurai-4@example.org"
11
+ puts " deploy add staging green-flower-2@example.org"
11
12
  puts ""
12
13
  puts "Deploy the current directory to the target:"
13
- puts " deploy to production"
14
+ puts " deploy production"
14
15
  end
15
16
 
16
17
  def self.run(command, args)
17
18
  if args.include?("--debug")
18
19
  args.delete("--debug")
19
20
  $logger.level = Logger::DEBUG
21
+ elsif args.include?("-d")
22
+ args.delete("-d")
23
+ $logger.level = Logger::DEBUG
24
+ elsif args.include?("-v")
25
+ args.delete("-v")
26
+ $logger.level = Logger::DEBUG
20
27
  else
21
28
  $logger.level = Logger::INFO
22
29
  end
@@ -75,8 +82,10 @@ class DeployTool::Command
75
82
  target.push(opts)
76
83
  rescue => e
77
84
  puts e
85
+ puts "\nPlease contact %s support: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
78
86
  exit 2
79
87
  end
88
+ DeployTool::Config[args[0]] = target.to_h
80
89
  end
81
90
 
82
91
  DeployTool::Config.save
@@ -51,6 +51,7 @@ class DeployTool::Target
51
51
  $logger.debug "Calling '%s' took longer than 5s, skipping" % [url, res.code, res.body]
52
52
  return nil
53
53
  end
54
+ return nil if res.nil?
54
55
  if res.code != '200'
55
56
  $logger.debug "Calling '%s' returned %s, skipping" % [url, res.code, res.body]
56
57
  return nil
@@ -1,7 +1,15 @@
1
1
  require 'highline'
2
-
3
2
  class DeployTool::Target::EfficientCloud < DeployTool::Target
4
- SUPPORTED_API_VERSION = 2
3
+ SUPPORTED_API_VERSION = 3
4
+
5
+ def self.cloud_name
6
+ @cloud_name || 'Efficient Cloud'
7
+ end
8
+
9
+ def self.support_email
10
+ @support_email || 'team@efficientcloud.com'
11
+ end
12
+
5
13
  def self.parse_target_spec(target_spec)
6
14
  server, app_name = target_spec.split('@').reverse
7
15
  if app_name.nil?
@@ -22,7 +30,12 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
22
30
  end
23
31
 
24
32
  def to_h
25
- {:type => "EfficientCloud", :api_server => @api_client.server, :app_name => @api_client.app_name, :email => @api_client.email, :password => @api_client.password}
33
+ x = {:type => "EfficientCloud", :api_server => @api_client.server, :app_name => @api_client.app_name,}
34
+ if @api_client.auth_method == :refresh_token
35
+ x.merge({:refresh_token => @api_client.refresh_token})
36
+ else
37
+ x
38
+ end
26
39
  end
27
40
 
28
41
  def to_s
@@ -31,7 +44,8 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
31
44
 
32
45
  def initialize(options)
33
46
  @api_server = options['api_server']
34
- @api_client = ApiClient.new(options['api_server'], options['app_name'], options['email'], options['password'])
47
+ auth = options.has_key?('refresh_token') ? {:refresh_token => options['refresh_token']} : {:email => options['email'], :password => options['password']}
48
+ @api_client = ApiClient.new(options['api_server'], options['app_name'], auth)
35
49
  end
36
50
 
37
51
  def self.check_version(api_server)
@@ -47,15 +61,13 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
47
61
  $logger.error "This version of deploytool is outdated.\nThis server requires at least API Version #{info['api_version']}."
48
62
  return false
49
63
  end
64
+ @cloud_name = info['cloud_name']
50
65
  return true
51
66
  end
52
67
 
53
68
  def self.create(target_spec)
54
- $logger.info "Please specify your controlpanel login information"
55
- email = HighLine.new.ask("E-mail: ")
56
- password = HighLine.new.ask("Password: ") {|q| q.echo = "*" }
57
69
  app_name, api_server = parse_target_spec(target_spec)
58
- EfficientCloud.new('api_server' => api_server, 'app_name' => app_name, 'email' => email, 'password' => password)
70
+ EfficientCloud.new('api_server' => api_server, 'app_name' => app_name)
59
71
  end
60
72
 
61
73
  def verify
@@ -69,8 +81,12 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
69
81
  $logger.error "Authentication failed (password wrong?)"
70
82
  elsif e.message.include?("404 ")
71
83
  $logger.error "Application does not exist"
84
+ elsif e.message.start_with?("ERROR ")
85
+ puts e.message
86
+ $logger.info "\nPlease contact %s support and include the above output: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
72
87
  else
73
88
  $logger.error "Remote server said: %s" % [e.message]
89
+ $logger.info "\nPlease contact %s support and include the above output: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
74
90
  end
75
91
  end
76
92
  exit 5
@@ -83,9 +99,22 @@ class DeployTool::Target::EfficientCloud < DeployTool::Target
83
99
  $logger.error info[:blocking_deployment]
84
100
  exit 4
85
101
  end
102
+ if info[:warn_deployment]
103
+ $logger.info info[:warn_deployment]
104
+ exit 5 if HighLine.new.ask("Deploy anyway? (y/N)").downcase.strip != 'y'
105
+ end
106
+
86
107
  code_token = @api_client.upload
87
108
  deploy_token = @api_client.deploy(code_token)
88
- @api_client.deploy_status(deploy_token, opts) # Blocks till deploy is done
109
+ begin
110
+ @api_client.deploy_status(deploy_token, opts) # Blocks till deploy is done
111
+ rescue => e
112
+ if e.message.start_with?("ERROR ")
113
+ puts e.message
114
+ else
115
+ puts "some error happened, sorry!"
116
+ end
117
+ end
89
118
  end
90
119
  end
91
120
 
@@ -5,39 +5,135 @@ require 'net/http/post/multipart'
5
5
  require 'fileutils'
6
6
  require 'tempfile'
7
7
  require 'zip'
8
+ require 'oauth2'
9
+ require 'multi_json'
10
+ require 'highline'
11
+
12
+ CLIENT_ID = 'com.efficientcloud.api.deploytool'
13
+ CLIENT_SECRET = '11d6b5cc70e4bc9563a3b8dd50dd34f6'
8
14
 
9
15
  class DeployTool::Target::EfficientCloud
10
16
  class ApiClient
11
- attr_reader :server, :app_name, :email, :password
12
- def initialize(server, app_name, email, password)
17
+ attr_reader :server, :app_name, :email, :password, :refresh_token, :auth_method
18
+ def initialize(server, app_name, auth)
13
19
  @app_name = app_name
14
20
  @server = server
15
- @email = email
16
- @password = password
21
+ if auth.has_key? :refresh_token
22
+ @refresh_token = auth[:refresh_token]
23
+ @auth_method = :refresh_token
24
+ elsif auth.has_key? :email
25
+ @auth_method = :password
26
+ @email = auth[:email]
27
+ @password = auth[:password]
28
+ else
29
+ @auth_method = :password
30
+ end
31
+ end
32
+
33
+ def re_auth
34
+ @auth_method = :password
17
35
  end
18
36
 
19
37
  def call(method, method_name, data = {})
20
38
  url = Addressable::URI.parse("http://#{@server}/api/cli/v1/apps/#{@app_name}/#{method_name}")
21
- data = data.merge(:email => @email, :password => @password)
22
- if method == :post
23
- res = Net::HTTP.start(url.host, url.port) do |http|
24
- http.request Net::HTTP::Post::Multipart.new(url.path, data)
39
+ client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET, :site => "http://#{server}/", :token_url => '/oauth2/token', :raise_errors => false) do |builder|
40
+ builder.use Faraday::Request::Multipart
41
+ builder.use Faraday::Request::UrlEncoded
42
+ builder.adapter :net_http
43
+ end
44
+ auth = false
45
+ tries = 0
46
+ while not auth
47
+ token = nil
48
+ handled_error = false
49
+ begin
50
+ if @auth_method == :password
51
+ if tries != 0 && HighLine.new.ask("Would you like to try again? (y/n): ") != 'y'
52
+ return
53
+ end
54
+ if !@email.nil? && !@password.nil?
55
+ # Upgrade from previous configuration file
56
+ print "Logging in..."
57
+ begin
58
+ token = client.password.get_token(@email, @password, :raise_errors => true)
59
+ token = token.refresh!
60
+ @email = nil
61
+ @password = nil
62
+ rescue StandardError => e
63
+ @email = nil
64
+ @password = nil
65
+ tries = 0
66
+ retry
67
+ ensure
68
+ print "\r"
69
+ end
70
+ else
71
+ tries += 1
72
+ $logger.info "Please specify your %s login data" % [DeployTool::Target::EfficientCloud.cloud_name]
73
+ email = HighLine.new.ask("E-mail: ")
74
+ password = HighLine.new.ask("Password: ") {|q| q.echo = "*" }
75
+ print "Authorizing..."
76
+ begin
77
+ token = client.password.get_token(email, password, :raise_errors => true)
78
+ token = token.refresh!
79
+ ensure
80
+ print "\r"
81
+ end
82
+ puts "Authorization succeeded."
83
+ end
84
+ else
85
+ params = {:client_id => client.id,
86
+ :client_secret => client.secret,
87
+ :grant_type => 'refresh_token',
88
+ :refresh_token => @refresh_token
89
+ }
90
+ token = client.get_token(params)
91
+ end
92
+ rescue OAuth2::Error => e
93
+ handled_error = true
94
+ print "Authorization failed"
95
+ token = nil
96
+ details = MultiJson.decode(e.response.body) rescue nil
97
+ if details
98
+ puts ": #{details['error_description']}"
99
+ re_auth if details['error']
100
+ else
101
+ puts "."
102
+ end
103
+ rescue EOFError
104
+ exit 1
105
+ rescue Interrupt
106
+ exit 1
107
+ rescue StandardError => e
108
+ puts "ERROR: #{e.inspect}"
109
+ puts "\nPlease contact %s support: %s" % [EfficientCloud.cloud_name, EfficientCloud.support_email]
110
+ puts ""
111
+ end
112
+ auth = token
113
+ if not token and not handled_error
114
+ puts "Authorization failed."
25
115
  end
26
- else
27
- url.query_values = data
28
- res = Net::HTTP.get_response(url)
29
116
  end
30
- case res
31
- when Net::HTTPSuccess, Net::HTTPRedirection
32
- res.body
33
- else
34
- res.error!
117
+
118
+ @refresh_token = token.refresh_token
119
+ @auth_method = :refresh_token
120
+
121
+ response = token.request(method, url.path, method==:post ? {:body => data} : {:params => data})
122
+ if response.status != 200
123
+ details = MultiJson.decode(response.body) rescue nil
124
+ raise "#{response.status} #{details}"
35
125
  end
126
+ response
127
+ end
128
+
129
+ def to_h
130
+ {:server => @server, :app_name => @app_name, :email => email, :password => @password, :refresh_token => @refresh_token, :auth_method => @auth_method}
36
131
  end
37
132
 
38
133
  def info
39
134
  response = call :get, 'info'
40
- doc = REXML::Document.new response
135
+ return nil if not response
136
+ doc = REXML::Document.new response.body
41
137
  data = {}
42
138
  doc.elements["app"].each_element do |el|
43
139
  data[el.name.gsub('-','_').to_sym] = el.text
@@ -74,25 +170,15 @@ class DeployTool::Target::EfficientCloud
74
170
  end
75
171
 
76
172
  puts "-----> Uploading %s code tarball..." % human_filesize(tempfile.path)
77
- initial_response = call :post, 'upload', {:code => UploadIO.new(tempfile, "application/zip", "ecli-upload.zip")}
78
- doc = REXML::Document.new initial_response
173
+ initial_response = call :post, 'upload', {:code => Faraday::UploadIO.new(tempfile, "application/zip")}
174
+ doc = REXML::Document.new initial_response.body
79
175
  doc.elements["code/code-token"].text
80
- rescue Net::HTTPServerException => e
81
- case e.response
82
- when Net::HTTPNotFound
83
- $logger.error "Application %s couldn't be found." % @app_name
84
- when Net::HTTPUnauthorized
85
- $logger.error "You're not authorized to update %s." % @app_name
86
- else
87
- raise e
88
- end
89
- $logger.info "\nPlease check the controlpanel for update instructions."
90
- exit 2
91
176
  end
92
177
 
93
178
  def deploy(code_token)
94
179
  initial_response = call :post, 'deploy', {:code_token => code_token}
95
- doc = REXML::Document.new initial_response
180
+ return nil if not initial_response
181
+ doc = REXML::Document.new initial_response.body
96
182
  deploy_token = doc.elements["deploy/token"].text
97
183
  deploy_token
98
184
  end
@@ -112,7 +198,7 @@ class DeployTool::Target::EfficientCloud
112
198
  while true
113
199
  sleep 1
114
200
  resp = call :get, 'deploy_status', {:deploy_token => deploy_token}
115
- doc = REXML::Document.new resp
201
+ doc = REXML::Document.new resp.body
116
202
 
117
203
  if doc.elements["deploy/message"].nil?
118
204
  puts resp
@@ -1,3 +1,3 @@
1
1
  module DeployTool
2
- VERSION = "0.9.2"
2
+ VERSION = "0.9.4"
3
3
  end
@@ -15,29 +15,17 @@ describe DeployTool::Target do
15
15
 
16
16
  # TODO: Mock HTTP get method
17
17
  end
18
-
19
- ["api.cloudfoundry.com", "cloudfoundry.com", "awesomeapp.cloudfoundry.com"].each do |target_spec| #, "api.cloud.1and1.com", "cloud.1and1.com", "awesomeapp.cloud.1and1.com"].each do
20
- it "should detect #{target_spec} as a CloudFoundry target" do
21
- DeployTool::Target.find(target_spec).class.should == DeployTool::Target::CloudFoundry
22
- end
23
- end
24
-
25
- ["app10000@api.srv.io", "app10000@srv.io", "app10000@app123.srv.io", "app10000.srv.io"].each do |target_spec|
18
+
19
+ ["app10000@api.hostingstack.com", "app10000@hostingstack.com", "app10000@app123.hostingstack.com", "app10000.hostingstack.com"].each do |target_spec|
26
20
  it "should detect #{target_spec} as an Efficient Cloud target" do
27
21
  DeployTool::Target.find(target_spec).class.should == DeployTool::Target::EfficientCloud
28
22
  end
29
23
  end
30
-
31
- ["heroku.com", "awesomeapp.heroku.com"].each do |target_spec|
32
- it "should detect #{target_spec} as an Heroku target" do
33
- DeployTool::Target.find(target_spec).class.should == DeployTool::Target::Heroku
34
- end
35
- end
36
-
24
+
37
25
  ["gandi.net", "1and1.com"].each do |target_spec|
38
26
  it "should return an error with #{target_spec} as target" do
39
27
  DeployTool::Target.find(target_spec).class.should == NilClass
40
28
  end
41
29
  end
42
30
  end
43
- end
31
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deployto
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
5
- prerelease: false
4
+ hash: 51
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 2
10
- version: 0.9.2
9
+ - 4
10
+ version: 0.9.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Efficient Cloud Ltd
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-31 00:00:00 +02:00
19
- default_executable:
18
+ date: 2011-09-28 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: inifile
@@ -70,10 +69,12 @@ dependencies:
70
69
  requirements:
71
70
  - - ">="
72
71
  - !ruby/object:Gem::Version
73
- hash: 3
72
+ hash: 11
74
73
  segments:
75
- - 0
76
- version: "0"
74
+ - 1
75
+ - 6
76
+ - 2
77
+ version: 1.6.2
77
78
  type: :runtime
78
79
  version_requirements: *id004
79
80
  - !ruby/object:Gem::Dependency
@@ -91,7 +92,7 @@ dependencies:
91
92
  type: :runtime
92
93
  version_requirements: *id005
93
94
  - !ruby/object:Gem::Dependency
94
- name: heroku
95
+ name: json_pure
95
96
  prerelease: false
96
97
  requirement: &id006 !ruby/object:Gem::Requirement
97
98
  none: false
@@ -105,7 +106,7 @@ dependencies:
105
106
  type: :runtime
106
107
  version_requirements: *id006
107
108
  - !ruby/object:Gem::Dependency
108
- name: json_pure
109
+ name: oauth2
109
110
  prerelease: false
110
111
  requirement: &id007 !ruby/object:Gem::Requirement
111
112
  none: false
@@ -133,15 +134,12 @@ files:
133
134
  - lib/deploytool/command.rb
134
135
  - lib/deploytool/config.rb
135
136
  - lib/deploytool/target.rb
136
- - lib/deploytool/target/cloudfoundry.rb
137
137
  - lib/deploytool/target/efficientcloud.rb
138
138
  - lib/deploytool/target/efficientcloud/api_client.rb
139
- - lib/deploytool/target/heroku.rb
140
139
  - lib/deploytool/version.rb
141
140
  - spec/spec.opts
142
141
  - spec/spec_helper.rb
143
142
  - spec/target_spec.rb
144
- has_rdoc: true
145
143
  homepage: http://platformdirectory.com/
146
144
  licenses: []
147
145
 
@@ -171,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
169
  requirements: []
172
170
 
173
171
  rubyforge_project:
174
- rubygems_version: 1.3.7
172
+ rubygems_version: 1.8.8
175
173
  signing_key:
176
174
  specification_version: 3
177
175
  summary: Multi-platform deployment tool.
@@ -1,31 +0,0 @@
1
- class DeployTool::Target::CloudFoundry < DeployTool::Target
2
- def self.parse_target_spec(target_spec)
3
- app_name, server = target_spec.split('.', 2)
4
- return false if server.nil? or app_name.nil?
5
- # Test through multiple versions of API server URLs
6
- [target_spec, 'api.' + target_spec, 'api.' + server].each do |api_server|
7
- begin
8
- if get_json_resource("http://%s/info" % api_server)['name'] == "vcap"
9
- app_name = nil if server.gsub('api.') == api_server.gsub('api.')
10
- return [app_name, api_server]
11
- end
12
- rescue => e
13
- $logger.debug "Exception: %s\n%s" % [e.message, e.backtrace.join("\n")]
14
- end
15
- end
16
- false
17
- end
18
-
19
- def self.matches?(target_spec)
20
- return true if parse_target_spec(target_spec)
21
- end
22
-
23
- def initialize(options)
24
- # FIXME
25
- end
26
-
27
- def self.create(target_spec)
28
- app_name, api_server = parse_target_spec(target_spec)
29
- CloudFoundry.new('api_server' => api_server, 'app_name' => app_name)
30
- end
31
- end
@@ -1,30 +0,0 @@
1
- class DeployTool::Target::Heroku < DeployTool::Target
2
- def self.matches?(target_spec)
3
- target_spec[/(^|\.)heroku\.com$/]
4
- end
5
-
6
- def to_h
7
- {:type => "Heroku", :app_name => @app_name}
8
- end
9
-
10
- def to_s
11
- "%s.heroku.com (Heroku)" % [@app_name]
12
- end
13
-
14
- def initialize(options)
15
- @app_name = options['app_name']
16
- end
17
-
18
- def self.create(target_name)
19
- app_name = target_name.gsub('.heroku.com', '')
20
- # TODO: Require current directory to be a git repository
21
- # TODO: Ask for app name if app name is nil or www
22
- puts `heroku create #{app_name}`
23
- Heroku.new('app_name' => app_name)
24
- end
25
-
26
- def push(opts)
27
- puts `git push -f git@heroku.com:#{@app_name}.git master`
28
- $?.exitstatus
29
- end
30
- end