tugboat 0.0.7 → 0.0.8

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: acb3d5fff3be496ee45a238c9219bea4c159ca4e
4
+ data.tar.gz: a88aea864325b1d5b0e7d37642b42418e79ed905
5
+ SHA512:
6
+ metadata.gz: e53bbe9b2bf1e3bd5bef1f748249857045da696f57e250b2819aa7261db9bdd769ed53b21e60258de6f1c5090e05de8906b0f47de4f313cacf62c20c74357c9a
7
+ data.tar.gz: 0806ce448db80b8a5436c647f56e144a4b94394fbc866bc4a676cea6f45bed21d4b1649339c1b3f55ca13c462fa89b08f251b539ab92d3b4358405162a69181a
data/.travis.yml CHANGED
@@ -3,3 +3,6 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.3
5
5
  - 2.0.0
6
+ matrix:
7
+ allow_failures:
8
+ - rvm: 1.8.7
data/CHANGELOG.md CHANGED
@@ -1,6 +1,26 @@
1
- ## 0.0.7 (unreleased)
1
+ ## 0.0.8 (Unreleased)
2
+
3
+ FEATURES:
4
+
5
+ - [Pete](https://github.com/petems) added configuration defaults
6
+ that you can set. [GH-61]
7
+ - [Pete](https://github.com/petems) added log filtering to debug output.
8
+ `DEBUG=1` now gives you filtered logs, `DEBUG=2`, raw. [GH-58]
2
9
 
3
10
  IMPROVEMENTS:
11
+
12
+ - Error messages are now caught at the Faraday level and displayed
13
+ back to the user.
14
+
15
+ BUG FIXES:
16
+
17
+ - [Ørjan](https://github.com/blom) added a color reset on the `list`
18
+ command. [GH-57]
19
+
20
+ ## 0.0.7 (August 2, 2013)
21
+
22
+ IMPROVEMENTS:
23
+
4
24
  - [Pete](https://github.com/petems) made it clearer to the user
5
25
  if they don't have any droplets or images. [GH-48], [GH-49]
6
26
 
data/README.md CHANGED
@@ -19,6 +19,15 @@ Run the configuration utility, `tugboat authorize`. You can grab your keys
19
19
  Enter your SSH key path (optional, defaults to ~/.ssh/id_rsa):
20
20
  Enter your SSH user (optional, defaults to jack):
21
21
  Enter your SSH port number (optional, defaults to 22):
22
+
23
+ To retrieve region, image, size and key ID's, you can use the corresponding tugboat command, such as `tugboat images`.
24
+ Defaults can be changed at any time in your ~/.tugboat configuration file.
25
+
26
+ Enter your default region ID (optional, defaults to 1 (New York)):
27
+ Enter your default image ID (optional, defaults to 284203 (Ubuntu 12.04 x64)):
28
+ Enter your default size ID (optional, defaults to 66 (512MB)):
29
+ Enter your default ssh key ID (optional, defaults to none):
30
+
22
31
  Authentication with DigitalOcean was successful!
23
32
 
24
33
  ## Usage
@@ -180,6 +189,15 @@ For a complete overview of all of the available commands, run:
180
189
 
181
190
  $ tugboat help
182
191
 
192
+ ## Reporting Bugs
193
+
194
+ Yes, please!
195
+
196
+ It's very helpful if you can run `DEBUG=1 tugboat ...` with the command
197
+ that is causing you issues, and then include that in the issue.
198
+
199
+ You can create a new issue [here](https://github.com/pearkes/tugboat/issues/new). Thank you!
200
+
183
201
  ## Contributing
184
202
 
185
203
  See the [contributing guide](CONTRIBUTING.md).
data/lib/tugboat/cli.rb CHANGED
@@ -36,6 +36,15 @@ module Tugboat
36
36
  Middleware.sequence_authorize.call({})
37
37
  end
38
38
 
39
+ desc "verify", "Check your DigitalOcean credentials"
40
+ long_desc "This tests that your credentials created by the \`authorize\`
41
+ command that are stored in ~/.tugboat are correct and allow you to connect
42
+ to the API without errors.
43
+ "
44
+ def verify
45
+ Middleware.sequence_verify.call({})
46
+ end
47
+
39
48
  desc "droplets", "Retrieve a list of your droplets"
40
49
  def droplets
41
50
  Middleware.sequence_list_droplets.call({})
@@ -89,17 +98,14 @@ module Tugboat
89
98
  method_option "size",
90
99
  :type => :numeric,
91
100
  :aliases => "-s",
92
- :default => 66,
93
101
  :desc => "The size_id of the droplet"
94
102
  method_option "image",
95
103
  :type => :numeric,
96
104
  :aliases => "-i",
97
- :default => 284203,
98
105
  :desc => "The image_id of the droplet"
99
106
  method_option "region",
100
107
  :type => :numeric,
101
108
  :aliases => "-r",
102
- :default => 1,
103
109
  :desc => "The region_id of the droplet"
104
110
  method_option "keys",
105
111
  :type => :string,
@@ -12,6 +12,10 @@ module Tugboat
12
12
  FILE_NAME = '.tugboat'
13
13
  DEFAULT_SSH_KEY_PATH = '.ssh/id_rsa'
14
14
  DEFAULT_SSH_PORT = '22'
15
+ DEFAULT_REGION = '1'
16
+ DEFAULT_IMAGE = '284203'
17
+ DEFAULT_SIZE = '66'
18
+ DEFAULT_SSH_KEY = ''
15
19
 
16
20
  def initialize
17
21
  @path = ENV["TUGBOAT_CONFIG_PATH"] || File.join(File.expand_path("~"), FILE_NAME)
@@ -42,11 +46,27 @@ module Tugboat
42
46
  def ssh_user
43
47
  @data['ssh']['ssh_user']
44
48
  end
45
-
49
+
46
50
  def ssh_port
47
51
  @data['ssh']['ssh_port']
48
52
  end
49
53
 
54
+ def default_region
55
+ @data['defaults'].nil? ? DEFAULT_REGION : @data['defaults']['region']
56
+ end
57
+
58
+ def default_image
59
+ @data['defaults'].nil? ? DEFAULT_IMAGE : @data['defaults']['image']
60
+ end
61
+
62
+ def default_size
63
+ @data['defaults'].nil? ? DEFAULT_SIZE : @data['defaults']['size']
64
+ end
65
+
66
+ def default_ssh_key
67
+ @data['defaults'].nil? ? DEFAULT_SSH_KEY : @data['defaults']['ssh_key']
68
+ end
69
+
50
70
  # Re-runs initialize
51
71
  def reset!
52
72
  self.send(:initialize)
@@ -58,7 +78,7 @@ module Tugboat
58
78
  end
59
79
 
60
80
  # Writes a config file
61
- def create_config_file(client, api, ssh_key_path, ssh_user, ssh_port)
81
+ def create_config_file(client, api, ssh_key_path, ssh_user, ssh_port, region, image, size, ssh_key)
62
82
  # Default SSH Key path
63
83
  if ssh_key_path.empty?
64
84
  ssh_key_path = File.join(File.expand_path("~"), DEFAULT_SSH_KEY_PATH)
@@ -72,10 +92,29 @@ module Tugboat
72
92
  ssh_port = DEFAULT_SSH_PORT
73
93
  end
74
94
 
95
+ if region.empty?
96
+ region = DEFAULT_REGION
97
+ end
98
+
99
+ if image.empty?
100
+ image = DEFAULT_IMAGE
101
+ end
102
+
103
+ if size.empty?
104
+ size = DEFAULT_SIZE
105
+ end
106
+
107
+ if ssh_key.empty?
108
+ default_ssh_key = DEFAULT_SSH_KEY
109
+ end
110
+
75
111
  require 'yaml'
76
112
  File.open(@path, File::RDWR|File::TRUNC|File::CREAT, 0600) do |file|
77
- data = {"authentication" => { "client_key" => client, "api_key" => api },
78
- "ssh" => { "ssh_user" => ssh_user, "ssh_key_path" => ssh_key_path , "ssh_port" => ssh_port}}
113
+ data = {
114
+ "authentication" => { "client_key" => client, "api_key" => api },
115
+ "ssh" => { "ssh_user" => ssh_user, "ssh_key_path" => ssh_key_path , "ssh_port" => ssh_port},
116
+ "defaults" => { "region" => region, "image" => image, "size" => size, "ssh_key" => ssh_key }
117
+ }
79
118
  file.write data.to_yaml
80
119
  end
81
120
  end
@@ -42,6 +42,16 @@ module Tugboat
42
42
  end
43
43
  end
44
44
 
45
+ # This checks that the credentials in ~/.tugboat are valid
46
+ def self.sequence_verify
47
+ ::Middleware::Builder.new do
48
+ use InjectConfiguration
49
+ use CheckConfiguration
50
+ use InjectClient
51
+ use CheckCredentials
52
+ end
53
+ end
54
+
45
55
  # Display a list of droplets
46
56
  def self.sequence_list_droplets
47
57
  ::Middleware::Builder.new do
@@ -10,9 +10,17 @@ module Tugboat
10
10
  ssh_key_path = ask "Enter your SSH key path (optional, defaults to ~/.ssh/id_rsa):"
11
11
  ssh_user = ask "Enter your SSH user (optional, defaults to #{ENV['USER']}):"
12
12
  ssh_port = ask "Enter your SSH port number (optional, defaults to 22):"
13
+ say
14
+ say "To retrieve region, image, size and key ID's, you can use the corresponding tugboat command, such as `tugboat images`."
15
+ say "Defaults can be changed at any time in your ~/.tugboat configuration file."
16
+ say
17
+ region = ask "Enter your default region ID (optional, defaults to 1 (New York)):"
18
+ image = ask "Enter your default image ID (optional, defaults to 284203 (Ubuntu 12.04 x64)):"
19
+ size = ask "Enter your default size ID (optional, defaults to 66 (512MB)):"
20
+ ssh_key = ask "Enter your default ssh key ID (optional, defaults to none):"
13
21
 
14
22
  # Write the config file.
15
- env['config'].create_config_file(client_key, api_key, ssh_key_path, ssh_user, ssh_port)
23
+ env['config'].create_config_file(client_key, api_key, ssh_key_path, ssh_user, ssh_port, region, image, size, ssh_key)
16
24
  env['config'].reload!
17
25
 
18
26
  @app.call(env)
@@ -0,0 +1,40 @@
1
+ module Tugboat
2
+ class AuthenticationMiddleware < Faraday::Middleware
3
+ extend Forwardable
4
+ def_delegators :'Faraday::Utils', :parse_query, :build_query
5
+ RED = "\e[31m"
6
+ CLEAR = "\e[0m"
7
+
8
+ def initialize(app, client_id, api_key)
9
+ @client_id = client_id
10
+ @api_key = api_key
11
+
12
+ super(app)
13
+ end
14
+
15
+ def call(env)
16
+ params = { 'client_id' => @client_id, 'api_key' => @api_key }.update query_params(env[:url])
17
+
18
+ env[:url].query = build_query params
19
+
20
+ begin
21
+ @app.call(env)
22
+ rescue Faraday::Error::ClientError => e
23
+ puts "#{RED}#{e}!#{CLEAR}\n"
24
+ if env[:body].status == "ERROR"
25
+ puts "\n#{RED}#{env[:body].error_message}#{CLEAR}\n\n"
26
+ end
27
+ puts "Double-check your parameters and configuration (in your ~/.tugboat file)"
28
+ exit 1
29
+ end
30
+ end
31
+
32
+ def query_params(url)
33
+ if url.query.nil? or url.query.empty?
34
+ {}
35
+ else
36
+ parse_query url.query
37
+ end
38
+ end
39
+ end
40
+ end
@@ -9,7 +9,7 @@ module Tugboat
9
9
  # work.
10
10
  begin
11
11
  env["ocean"].droplets.list
12
- rescue Faraday::Error::ParsingError
12
+ rescue Faraday::Error::ClientError => e
13
13
  say "Authentication with DigitalOcean failed. Run `tugboat authorize`", :red
14
14
  exit 1
15
15
  end
@@ -6,11 +6,27 @@ module Tugboat
6
6
 
7
7
  say "Queueing creation of droplet '#{env["create_droplet_name"]}'...", nil, false
8
8
 
9
- req = ocean.droplets.create :name => env["create_droplet_name"],
10
- :size_id => env["create_droplet_size_id"],
11
- :image_id => env["create_droplet_image_id"],
12
- :region_id => env["create_droplet_region_id"],
13
- :ssh_key_ids => env["create_droplet_ssh_key_ids"]
9
+ env["create_droplet_region_id"] ?
10
+ droplet_region_id = env["create_droplet_region_id"] :
11
+ droplet_region_id = env["config"].default_region
12
+
13
+ env["create_droplet_image_id"] ?
14
+ droplet_image_id = env["create_droplet_image_id"] :
15
+ droplet_image_id = env["config"].default_image
16
+
17
+ env["create_droplet_size_id"] ?
18
+ droplet_size_id = env["create_droplet_size_id"] :
19
+ droplet_size_id = env["config"].default_size
20
+
21
+ env["create_droplet_ssh_key_ids"] ?
22
+ droplet_ssh_key_id = env["create_droplet_ssh_key_ids"] :
23
+ droplet_ssh_key_id = env["config"].default_ssh_key
24
+
25
+ req = ocean.droplets.create :name => env["create_droplet_name"],
26
+ :size_id => droplet_size_id,
27
+ :image_id => droplet_image_id,
28
+ :region_id => droplet_region_id,
29
+ :ssh_key_ids => droplet_ssh_key_id
14
30
 
15
31
  if req.status == "ERROR"
16
32
  say req.error_message, :red
@@ -0,0 +1,72 @@
1
+ module Tugboat
2
+ class CustomLogger < Faraday::Middleware
3
+ extend Forwardable
4
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal
5
+
6
+ def initialize(app, options = {})
7
+ @app = app
8
+ @logger = options.fetch(:logger) {
9
+ require 'logger'
10
+ ::Logger.new(STDOUT)
11
+ }
12
+ end
13
+
14
+ def call(env)
15
+ start_time = Time.now
16
+ info { request_info(env) }
17
+ debug { request_debug(env) }
18
+ @app.call(env).on_complete do
19
+ end_time = Time.now
20
+ response_time = end_time - start_time
21
+ info { response_info(env, response_time) }
22
+ debug { response_debug(env) }
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def filter(output)
29
+ unless ENV['DEBUG'] == '2'
30
+ output = output.to_s.gsub(/client_id=[a-zA-Z0-9]*/,'client_id=[CLIENT-ID]')
31
+ output = output.to_s.gsub(/api_key=[a-zA-Z0-9]*/,'api_key=[API-KEY]')
32
+ output = output.to_s.gsub(/_digitalocean2_session_v2=[a-zA-Z0-9%-]*/,'_digitalocean2_session_v2=[SESSION_COOKIE]')
33
+ else
34
+ output
35
+ end
36
+ end
37
+
38
+ def request_info(env)
39
+ "Started %s request to: %s" % [ env[:method].to_s.upcase, filter(env[:url]) ]
40
+ end
41
+
42
+ def response_info(env, response_time)
43
+ "Response from %s; Status: %d; Time: %.1fms" % [ filter(env[:url]), env[:status], (response_time * 1_000.0) ]
44
+ end
45
+
46
+ def request_debug(env)
47
+ debug_message("Request", env[:request_headers], env[:body])
48
+ end
49
+
50
+ def response_debug(env)
51
+ debug_message("Response", env[:response_headers], env[:body])
52
+ end
53
+
54
+ def debug_message(name, headers, body)
55
+ <<-MESSAGE.gsub(/^ +([^ ])/m, '\\1')
56
+ #{name} Headers:
57
+ ----------------
58
+ #{format_headers(headers)}
59
+
60
+ #{name} Body:
61
+ -------------
62
+ #{body}
63
+ MESSAGE
64
+ end
65
+
66
+ def format_headers(headers)
67
+ length = headers.map {|k,v| k.to_s.size }.max
68
+ headers.map { |name, value| "#{name.to_s.ljust(length)} : #{filter(value)}" }.join("\n")
69
+ end
70
+
71
+ end
72
+ end
@@ -1,15 +1,34 @@
1
1
  require 'digital_ocean'
2
+ require File.expand_path('../authentication_middleware', __FILE__)
3
+ require File.expand_path('../custom_logger', __FILE__)
4
+
2
5
  module Tugboat
3
6
  module Middleware
4
7
  # Inject the digital ocean client into the environment
5
8
  class InjectClient < Base
9
+
10
+ def tugboat_faraday
11
+ Faraday.new(:url => 'https://api.digitalocean.com/') do |faraday|
12
+ faraday.use AuthenticationMiddleware, @client_id, @api_key
13
+ faraday.use Faraday::Response::RaiseError
14
+ faraday.use CustomLogger if ENV['DEBUG']
15
+ faraday.request :url_encoded
16
+ faraday.response :rashify
17
+ faraday.response :json
18
+ faraday.adapter Faraday.default_adapter
19
+ end
20
+ end
21
+
6
22
  def call(env)
7
23
  # Sets the digital ocean client into the environment for use
8
24
  # later.
25
+ @client_id = env["config"].client_key
26
+ @api_key = env["config"].api_key
27
+
9
28
  env["ocean"] = DigitalOcean::API.new \
10
- :client_id => env["config"].client_key,
11
- :api_key => env["config"].api_key,
12
- :debug => ENV['DEBUG'] || false
29
+ :client_id => @client_id,
30
+ :api_key => @api_key,
31
+ :faraday => tugboat_faraday
13
32
 
14
33
  @app.call(env)
15
34
  end
@@ -9,7 +9,7 @@ module Tugboat
9
9
 
10
10
  if droplet_list.empty?
11
11
  say "You don't appear to have any droplets.", :red
12
- say "Try creating one with #{GREEN}\`tugboat create\`"
12
+ say "Try creating one with #{GREEN}\`tugboat create\`#{CLEAR}"
13
13
  else
14
14
  droplet_list.each do |droplet|
15
15
 
@@ -1,3 +1,3 @@
1
1
  module Tugboat
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
@@ -3,10 +3,12 @@ require 'spec_helper'
3
3
  describe Tugboat::CLI do
4
4
  include_context "spec"
5
5
 
6
+ let(:tmp_path) { project_path + "/tmp/tugboat" }
7
+
6
8
  describe "authorize" do
7
9
  before do
8
10
  stub_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}").
9
- to_return(:status => 200)
11
+ to_return(:status => 200)
10
12
  end
11
13
 
12
14
  it "asks the right questions and checks credentials" do
@@ -22,10 +24,51 @@ describe Tugboat::CLI do
22
24
  $stdin.should_receive(:gets).and_return(ssh_user)
23
25
  $stdout.should_receive(:print).with("Enter your SSH port number (optional, defaults to 22): ")
24
26
  $stdin.should_receive(:gets).and_return(ssh_port)
27
+ $stdout.should_receive(:print).with("Enter your default region ID (optional, defaults to 1 (New York)): ")
28
+ $stdin.should_receive(:gets).and_return(region)
29
+ $stdout.should_receive(:print).with("Enter your default image ID (optional, defaults to 284203 (Ubuntu 12.04 x64)): ")
30
+ $stdin.should_receive(:gets).and_return(image)
31
+ $stdout.should_receive(:print).with("Enter your default size ID (optional, defaults to 66 (512MB)): ")
32
+ $stdin.should_receive(:gets).and_return(size)
33
+ $stdout.should_receive(:print).with("Enter your default ssh key ID (optional, defaults to none): ")
34
+ $stdin.should_receive(:gets).and_return(ssh_key_id)
25
35
 
26
36
  @cli.authorize
27
37
 
28
38
  expect(a_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}")).to have_been_made
39
+
40
+ File.read(tmp_path).should include "image: '#{image}'", "region: '#{region}'", "size: '#{size}'", "ssh_user: #{ssh_user}", "ssh_key_path: #{ssh_key_path}", "ssh_port: '#{ssh_port}'", "ssh_key: '#{ssh_key_id}'"
41
+
42
+ end
43
+
44
+ it "sets defaults if no input given" do
45
+
46
+ $stdout.should_receive(:print).exactly(6).times
47
+ $stdout.should_receive(:print).with("Enter your client key: ")
48
+ $stdin.should_receive(:gets).and_return(client_key)
49
+ $stdout.should_receive(:print).with("Enter your API key: ")
50
+ $stdin.should_receive(:gets).and_return(api_key)
51
+ $stdout.should_receive(:print).with("Enter your SSH key path (optional, defaults to ~/.ssh/id_rsa): ")
52
+ $stdin.should_receive(:gets).and_return(ssh_key_path)
53
+ $stdout.should_receive(:print).with("Enter your SSH user (optional, defaults to #{ENV['USER']}): ")
54
+ $stdin.should_receive(:gets).and_return('')
55
+ $stdout.should_receive(:print).with("Enter your SSH port number (optional, defaults to 22): ")
56
+ $stdin.should_receive(:gets).and_return('')
57
+ $stdout.should_receive(:print).with("Enter your default region ID (optional, defaults to 1 (New York)): ")
58
+ $stdin.should_receive(:gets).and_return('')
59
+ $stdout.should_receive(:print).with("Enter your default image ID (optional, defaults to 284203 (Ubuntu 12.04 x64)): ")
60
+ $stdin.should_receive(:gets).and_return('')
61
+ $stdout.should_receive(:print).with("Enter your default size ID (optional, defaults to 66 (512MB)): ")
62
+ $stdin.should_receive(:gets).and_return('')
63
+ $stdout.should_receive(:print).with("Enter your default ssh key ID (optional, defaults to none): ")
64
+ $stdin.should_receive(:gets).and_return('')
65
+
66
+ @cli.authorize
67
+
68
+ expect(a_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}")).to have_been_made
69
+
70
+ File.read(tmp_path).should include "image: '284203'", "region: '1'", "size: '66'", "ssh_user: #{ENV['USER']}", "ssh_key_path: ~/.ssh/id_rsa", "ssh_port: '22'", "ssh_key: ''"
71
+
29
72
  end
30
73
  end
31
74
 
@@ -4,8 +4,8 @@ describe Tugboat::CLI do
4
4
  include_context "spec"
5
5
 
6
6
  describe "create a droplet" do
7
- it "with a name" do
8
- stub_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id&name=#{droplet_name}&region_id&size_id&ssh_key_ids").
7
+ it "with a name, uses defaults from configuration" do
8
+ stub_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id=#{image}&name=#{droplet_name}&region_id=#{region}&size_id=#{size}&ssh_key_ids=#{ssh_key_id}").
9
9
  to_return(:status => 200, :body => '{"status":"OK"}')
10
10
 
11
11
  @cli.create(droplet_name)
@@ -13,21 +13,21 @@ describe Tugboat::CLI do
13
13
  expect($stdout.string).to eq <<-eos
14
14
  Queueing creation of droplet '#{droplet_name}'...done
15
15
  eos
16
- expect(a_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id&name=#{droplet_name}&region_id&size_id&ssh_key_ids")).to have_been_made
16
+ expect(a_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id=#{image}&name=#{droplet_name}&region_id=#{region}&size_id=#{size}&ssh_key_ids=#{ssh_key_id}")).to have_been_made
17
17
  end
18
18
 
19
- it "with args" do
20
- stub_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id=2672&name=#{droplet_name}&region_id=2&size_id=64&ssh_key_ids=1234").
19
+ it "with args does not use defaults from configuration" do
20
+ stub_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id=555&name=foo&region_id=3&size_id=666&ssh_key_ids=4321").
21
21
  to_return(:status => 200, :body => '{"status":"OK"}')
22
22
 
23
- @cli.options = @cli.options.merge(:image => 2672, :size => 64, :region => 2, :keys => "1234")
23
+ @cli.options = @cli.options.merge(:image => '555', :size => '666', :region => '3', :keys => '4321')
24
24
  @cli.create(droplet_name)
25
25
 
26
26
  expect($stdout.string).to eq <<-eos
27
27
  Queueing creation of droplet '#{droplet_name}'...done
28
28
  eos
29
29
 
30
- expect(a_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id=2672&name=foo&region_id=2&size_id=64&ssh_key_ids=1234")).to have_been_made
30
+ expect(a_request(:get, "https://api.digitalocean.com/droplets/new?api_key=#{api_key}&client_id=#{client_key}&image_id=555&name=foo&region_id=3&size_id=666&ssh_key_ids=4321")).to have_been_made
31
31
  end
32
32
  end
33
33
 
@@ -27,7 +27,7 @@ foo (ip: 33.33.33.10, status: \e[32mactive\e[0m, region: 1, id: 100823)
27
27
 
28
28
  expect($stdout.string).to eq <<-eos
29
29
  You don't appear to have any droplets.
30
- Try creating one with \e[32m`tugboat create`
30
+ Try creating one with \e[32m`tugboat create`\e[0m
31
31
  eos
32
32
 
33
33
  expect(a_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}")).to have_been_made
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tugboat::CLI do
4
+ include_context "spec"
5
+
6
+ describe "verify" do
7
+ it "returns confirmation text when verify passes" do
8
+ stub_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}").
9
+ to_return(:status => 200)
10
+ @cli.verify
11
+ expect($stdout.string).to eq "Authentication with DigitalOcean was successful.\n"
12
+ expect(a_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}")).to have_been_made
13
+ end
14
+
15
+ it "returns error string when verify fails" do
16
+ stub_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}").
17
+ to_return(:status => 401, :body => '{"status":"ERROR", "error_message":"Access Denied"}')
18
+ expect { @cli.verify }.to raise_error(SystemExit)
19
+ expect($stdout.string).to eq "\e[31mthe server responded with status 401!\e[0m\n\n\e[31mAccess Denied\e[0m\n\nDouble-check your parameters and configuration (in your ~/.tugboat file)\n"
20
+ expect(a_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}")).to have_been_made
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
data/spec/config_spec.rb CHANGED
@@ -29,12 +29,16 @@ describe Tugboat::Configuration do
29
29
  let(:ssh_key_path) { "~/.ssh/id_rsa2" }
30
30
  let(:ssh_key_path) { "~/.ssh/id_rsa2.pub" }
31
31
  let(:ssh_port) { "22" }
32
+ let(:region) { "2" }
33
+ let(:image) { "345791" }
34
+ let(:size) { "66" }
35
+ let(:ssh_key_id) { '1234' }
32
36
 
33
37
  let(:config) { config = Tugboat::Configuration.instance }
34
38
 
35
39
  before :each do
36
40
  # Create a temporary file
37
- config.create_config_file(client_key, api_key, ssh_key_path, ssh_user, ssh_port)
41
+ config.create_config_file(client_key, api_key, ssh_key_path, ssh_user, ssh_port, region, image, size, ssh_key_id)
38
42
  end
39
43
 
40
44
  it "can be created" do
@@ -47,39 +51,89 @@ describe Tugboat::Configuration do
47
51
  end
48
52
 
49
53
  describe "the file format"
50
- let(:data) { YAML.load_file(tmp_path) }
51
-
52
- it "should have authentication at the top level" do
53
- expect(data).to have_key("authentication")
54
- end
55
-
56
- it "should have ssh at the top level" do
57
- expect(data).to have_key("ssh")
58
- end
59
-
60
- it "should have a client key" do
61
- auth = data["authentication"]
62
- expect(auth).to have_key("client_key")
63
- end
64
-
65
- it "should have an api key" do
66
- auth = data["authentication"]
67
- expect(auth).to have_key("api_key")
68
- end
69
-
70
- it "should have an ssh key path" do
71
- ssh = data["ssh"]
72
- expect(ssh).to have_key("ssh_key_path")
73
- end
74
-
75
- it "should have an ssh user" do
76
- ssh = data["ssh"]
77
- expect(ssh).to have_key("ssh_user")
78
- end
79
-
80
- it "should have an ssh port" do
81
- ssh = data["ssh"]
82
- expect(ssh).to have_key("ssh_port")
83
- end
54
+ let(:data) { YAML.load_file(tmp_path) }
55
+
56
+ it "should have authentication at the top level" do
57
+ expect(data).to have_key("authentication")
58
+ end
59
+
60
+ it "should have ssh at the top level" do
61
+ expect(data).to have_key("ssh")
62
+ end
63
+
64
+ it "should have a client key" do
65
+ auth = data["authentication"]
66
+ expect(auth).to have_key("client_key")
67
+ end
68
+
69
+ it "should have an api key" do
70
+ auth = data["authentication"]
71
+ expect(auth).to have_key("api_key")
72
+ end
73
+
74
+ it "should have an ssh key path" do
75
+ ssh = data["ssh"]
76
+ expect(ssh).to have_key("ssh_key_path")
77
+ end
78
+
79
+ it "should have an ssh user" do
80
+ ssh = data["ssh"]
81
+ expect(ssh).to have_key("ssh_user")
82
+ end
83
+
84
+ it "should have an ssh port" do
85
+ ssh = data["ssh"]
86
+ expect(ssh).to have_key("ssh_port")
87
+ end
88
+ end
89
+ describe "backwards compatible" do
90
+ let(:client_key) { "foo" }
91
+ let(:api_key) { "bar" }
92
+ let(:ssh_user) { "baz" }
93
+ let(:ssh_key_path) { "~/.ssh/id_rsa2" }
94
+ let(:ssh_key_path) { "~/.ssh/id_rsa2.pub" }
95
+ let(:ssh_port) { "22" }
96
+
97
+ let(:config) { config = Tugboat::Configuration.instance }
98
+ let(:config_default_region) { Tugboat::Configuration::DEFAULT_REGION }
99
+ let(:config_default_image) { Tugboat::Configuration::DEFAULT_IMAGE }
100
+ let(:config_default_size) { Tugboat::Configuration::DEFAULT_SIZE }
101
+ let(:config_default_ssh_key) { Tugboat::Configuration::DEFAULT_SSH_KEY }
102
+ let(:backwards_config) {
103
+ {
104
+ "authentication" => { "client_key" => client_key, "api_key" => api_key },
105
+ "ssh" => { "ssh_user" => ssh_user, "ssh_key_path" => ssh_key_path , "ssh_port" => ssh_port},
106
+ }
107
+ }
108
+
109
+ before :each do
110
+ config.instance_variable_set(:@data, backwards_config)
111
+ end
112
+
113
+ it "should load a backwards compatible config file" do
114
+ data_file = config.instance_variable_get(:@data)
115
+ expect(data_file).to eql backwards_config
116
+ end
117
+
118
+ it "should use default region if not in configuration" do
119
+ region = config.default_region
120
+ expect(region).to eql config_default_region
121
+ end
122
+
123
+ it "should use default image if not in configuration" do
124
+ image = config.default_image
125
+ expect(image).to eql config_default_image
126
+ end
127
+
128
+ it "should use default size if not in configuration" do
129
+ size = config.default_size
130
+ expect(size).to eql config_default_size
131
+ end
132
+
133
+ it "should use default ssh key if not in configuration" do
134
+ ssh_key = config.default_ssh_key
135
+ expect(ssh_key).to eql config_default_ssh_key
136
+ end
137
+
84
138
  end
85
139
  end
@@ -4,7 +4,7 @@ describe Tugboat::Middleware::SSHDroplet do
4
4
  include_context "spec"
5
5
 
6
6
  before do
7
- Kernel.stub!(:exec)
7
+ Kernel.stub(:exec)
8
8
  end
9
9
 
10
10
  describe ".call" do
@@ -6,11 +6,15 @@ shared_context "spec" do
6
6
  let(:client_key) { "foo" }
7
7
  let(:api_key) { "bar" }
8
8
  let(:ssh_user) { "baz" }
9
- let(:ssh_port) { "22" }
9
+ let(:ssh_port) { "33" }
10
10
  let(:ssh_key_path) { "~/.ssh/id_rsa2" }
11
11
  let(:droplet_name) { "foo" }
12
12
  let(:droplet_ip) { "33.33.33.10" }
13
13
  let(:droplet_id) { 1234 }
14
+ let(:region) { '3' }
15
+ let(:image) { '345791'}
16
+ let(:size) { '67'}
17
+ let(:ssh_key_id) { '1234' }
14
18
  let(:ocean) { DigitalOcean::API.new :client_id => client_key, :api_key =>api_key }
15
19
  let(:app) { lambda { |env| } }
16
20
  let(:env) { {} }
@@ -22,7 +26,7 @@ shared_context "spec" do
22
26
  @cli = Tugboat::CLI.new
23
27
 
24
28
  # Set a temprary project path and create fake config.
25
- config.create_config_file(client_key, api_key, ssh_key_path, ssh_user, ssh_port)
29
+ config.create_config_file(client_key, api_key, ssh_key_path, ssh_user, ssh_port, region, image, size, ssh_key_id)
26
30
  config.reload!
27
31
 
28
32
  # Keep track of the old stderr / out
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tugboat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
5
- prerelease:
4
+ version: 0.0.8
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jack Pearkes
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-08-02 00:00:00.000000000 Z
11
+ date: 2013-09-07 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: thor
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: digital_ocean
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: middleware
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
@@ -62,23 +55,20 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rake
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: rspec-core
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ~>
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ~>
92
81
  - !ruby/object:Gem::Version
@@ -94,7 +83,6 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rspec-expectations
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
87
  - - ~>
100
88
  - !ruby/object:Gem::Version
@@ -102,7 +90,6 @@ dependencies:
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
94
  - - ~>
108
95
  - !ruby/object:Gem::Version
@@ -110,7 +97,6 @@ dependencies:
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rspec-mocks
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
101
  - - ~>
116
102
  - !ruby/object:Gem::Version
@@ -118,7 +104,6 @@ dependencies:
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
108
  - - ~>
124
109
  - !ruby/object:Gem::Version
@@ -126,7 +111,6 @@ dependencies:
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: webmock
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
115
  - - ~>
132
116
  - !ruby/object:Gem::Version
@@ -134,7 +118,6 @@ dependencies:
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
122
  - - ~>
140
123
  - !ruby/object:Gem::Version
@@ -142,7 +125,6 @@ dependencies:
142
125
  - !ruby/object:Gem::Dependency
143
126
  name: coveralls
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
129
  - - ~>
148
130
  - !ruby/object:Gem::Version
@@ -150,7 +132,6 @@ dependencies:
150
132
  type: :development
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
136
  - - ~>
156
137
  - !ruby/object:Gem::Version
@@ -177,6 +158,7 @@ files:
177
158
  - lib/tugboat/config.rb
178
159
  - lib/tugboat/middleware.rb
179
160
  - lib/tugboat/middleware/ask_for_credentials.rb
161
+ - lib/tugboat/middleware/authentication_middleware.rb
180
162
  - lib/tugboat/middleware/base.rb
181
163
  - lib/tugboat/middleware/check_configuration.rb
182
164
  - lib/tugboat/middleware/check_credentials.rb
@@ -184,6 +166,7 @@ files:
184
166
  - lib/tugboat/middleware/check_droplet_inactive.rb
185
167
  - lib/tugboat/middleware/confirm_action.rb
186
168
  - lib/tugboat/middleware/create_droplet.rb
169
+ - lib/tugboat/middleware/custom_logger.rb
187
170
  - lib/tugboat/middleware/destroy_droplet.rb
188
171
  - lib/tugboat/middleware/find_droplet.rb
189
172
  - lib/tugboat/middleware/halt_droplet.rb
@@ -220,6 +203,7 @@ files:
220
203
  - spec/cli/snapshot_cli_spec.rb
221
204
  - spec/cli/ssh_cli_spec.rb
222
205
  - spec/cli/start_cli_spec.rb
206
+ - spec/cli/verify_cli_spec.rb
223
207
  - spec/cli/version_cli_spec.rb
224
208
  - spec/cli/wait_cli_spec.rb
225
209
  - spec/config_spec.rb
@@ -249,27 +233,26 @@ files:
249
233
  - tugboat.gemspec
250
234
  homepage: https://github.com/pearkes/tugboat
251
235
  licenses: []
236
+ metadata: {}
252
237
  post_install_message:
253
238
  rdoc_options: []
254
239
  require_paths:
255
240
  - lib
256
241
  required_ruby_version: !ruby/object:Gem::Requirement
257
- none: false
258
242
  requirements:
259
- - - ! '>='
243
+ - - '>='
260
244
  - !ruby/object:Gem::Version
261
245
  version: '0'
262
246
  required_rubygems_version: !ruby/object:Gem::Requirement
263
- none: false
264
247
  requirements:
265
- - - ! '>='
248
+ - - '>='
266
249
  - !ruby/object:Gem::Version
267
250
  version: '0'
268
251
  requirements: []
269
252
  rubyforge_project:
270
- rubygems_version: 1.8.24
253
+ rubygems_version: 2.0.3
271
254
  signing_key:
272
- specification_version: 3
255
+ specification_version: 4
273
256
  summary: A command line tool for interacting with your DigitalOcean droplets.
274
257
  test_files:
275
258
  - spec/cli/authorize_cli_spec.rb
@@ -289,6 +272,7 @@ test_files:
289
272
  - spec/cli/snapshot_cli_spec.rb
290
273
  - spec/cli/ssh_cli_spec.rb
291
274
  - spec/cli/start_cli_spec.rb
275
+ - spec/cli/verify_cli_spec.rb
292
276
  - spec/cli/version_cli_spec.rb
293
277
  - spec/cli/wait_cli_spec.rb
294
278
  - spec/config_spec.rb
@@ -314,3 +298,4 @@ test_files:
314
298
  - spec/middleware/ssh_droplet_spec.rb
315
299
  - spec/shared/environment.rb
316
300
  - spec/spec_helper.rb
301
+ has_rdoc: