tugboat 0.2.0 → 1.0.0

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -4
  4. data/CHANGELOG.md +325 -102
  5. data/CHANGELOG_old.md +150 -0
  6. data/Gemfile +6 -0
  7. data/README.md +12 -6
  8. data/Rakefile +7 -3
  9. data/features/step_definitions/steps.rb +0 -0
  10. data/features/support/env.rb +9 -0
  11. data/features/vagrant-adam/config_current_directory.feature +27 -0
  12. data/lib/tugboat/cli.rb +31 -3
  13. data/lib/tugboat/config.rb +13 -5
  14. data/lib/tugboat/middleware.rb +8 -0
  15. data/lib/tugboat/middleware/ask_for_credentials.rb +5 -4
  16. data/lib/tugboat/middleware/authentication_middleware.rb +2 -2
  17. data/lib/tugboat/middleware/config.rb +29 -0
  18. data/lib/tugboat/middleware/custom_logger.rb +2 -2
  19. data/lib/tugboat/middleware/find_droplet.rb +4 -0
  20. data/lib/tugboat/middleware/info_droplet.rb +5 -0
  21. data/lib/tugboat/middleware/inject_client.rb +1 -1
  22. data/lib/tugboat/middleware/list_droplets.rb +5 -1
  23. data/lib/tugboat/middleware/list_regions.rb +2 -2
  24. data/lib/tugboat/middleware/ssh_droplet.rb +17 -2
  25. data/lib/tugboat/version.rb +1 -1
  26. data/spec/cli/add_key_spec.rb +9 -9
  27. data/spec/cli/authorize_cli_spec.rb +76 -52
  28. data/spec/cli/create_cli_spec.rb +39 -4
  29. data/spec/cli/debug_cli_spec.rb +44 -0
  30. data/spec/cli/destroy_cli_spec.rb +30 -11
  31. data/spec/cli/destroy_image_cli_spec.rb +11 -11
  32. data/spec/cli/droplets_cli_spec.rb +6 -6
  33. data/spec/cli/halt_cli_spec.rb +9 -9
  34. data/spec/cli/images_cli_spec.rb +6 -6
  35. data/spec/cli/info_cli_spec.rb +42 -7
  36. data/spec/cli/info_image_cli_spec.rb +6 -6
  37. data/spec/cli/keys_cli_spec.rb +1 -1
  38. data/spec/cli/password_reset_cli_spec.rb +8 -8
  39. data/spec/cli/rebuild_cli_spec.rb +49 -49
  40. data/spec/cli/regions_cli_spec.rb +4 -4
  41. data/spec/cli/resize_cli_spec.rb +8 -8
  42. data/spec/cli/restart_cli_spec.rb +8 -8
  43. data/spec/cli/sizes_cli_spec.rb +1 -1
  44. data/spec/cli/snapshot_cli_spec.rb +7 -7
  45. data/spec/cli/ssh_cli_spec.rb +4 -4
  46. data/spec/cli/start_cli_spec.rb +7 -7
  47. data/spec/cli/verify_cli_spec.rb +10 -2
  48. data/spec/cli/wait_cli_spec.rb +6 -6
  49. data/spec/fixtures/500.html +68 -0
  50. data/spec/fixtures/show_droplet.json +1 -0
  51. data/spec/fixtures/show_droplet_fuzzy.json +13 -0
  52. data/spec/fixtures/show_droplet_inactive.json +1 -0
  53. data/spec/fixtures/show_droplets.json +2 -0
  54. data/spec/fixtures/show_droplets_fuzzy.json +35 -0
  55. data/spec/fixtures/show_droplets_inactive.json +2 -0
  56. data/spec/fixtures/show_regions.json +9 -6
  57. data/spec/middleware/base_spec.rb +1 -1
  58. data/spec/middleware/check_credentials_spec.rb +1 -1
  59. data/spec/middleware/inject_client_spec.rb +29 -0
  60. data/spec/middleware/inject_configuration_spec.rb +1 -1
  61. data/spec/middleware/ssh_droplet_spec.rb +30 -7
  62. data/spec/shared/environment.rb +1 -0
  63. data/spec/spec_helper.rb +11 -4
  64. data/tugboat.gemspec +9 -7
  65. metadata +64 -33
@@ -6,10 +6,10 @@ describe Tugboat::CLI do
6
6
  describe "wait" do
7
7
  it "waits for a droplet with a fuzzy name" do
8
8
  stub_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}").
9
- to_return(:status => 200, :body => fixture("show_droplets"))
9
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => fixture("show_droplets"))
10
10
 
11
11
  stub_request(:get, "https://api.digitalocean.com/droplets/100823?api_key=#{api_key}&client_id=#{client_key}").
12
- to_return(:status => 200, :body => fixture("show_droplet"))
12
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => fixture("show_droplet"))
13
13
 
14
14
  @cli.options = @cli.options.merge(:state => "active")
15
15
  @cli.wait("foo")
@@ -25,10 +25,10 @@ eos
25
25
 
26
26
  it "waits for a droplet with an id" do
27
27
  stub_request(:get, "https://api.digitalocean.com/droplets/#{droplet_id}?api_key=#{api_key}&client_id=#{client_key}").
28
- to_return(:status => 200, :body => fixture("show_droplet"))
28
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => fixture("show_droplet"))
29
29
 
30
30
  stub_request(:get, "https://api.digitalocean.com/droplets/100823?api_key=#{api_key}&client_id=#{client_key}").
31
- to_return(:status => 200, :body => fixture("show_droplet"))
31
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => fixture("show_droplet"))
32
32
 
33
33
  @cli.options = @cli.options.merge(:id => droplet_id, :state => "active")
34
34
  @cli.wait
@@ -44,10 +44,10 @@ Waiting for droplet to become active..done\e[0m (0s)
44
44
 
45
45
  it "waits for a droplet with a name" do
46
46
  stub_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}").
47
- to_return(:status => 200, :body => fixture("show_droplets"))
47
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => fixture("show_droplets"))
48
48
 
49
49
  stub_request(:get, "https://api.digitalocean.com/droplets/100823?api_key=#{api_key}&client_id=#{client_key}").
50
- to_return(:status => 200, :body => fixture("show_droplet"))
50
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => fixture("show_droplet"))
51
51
 
52
52
  @cli.options = @cli.options.merge(:name => droplet_name, :state => "active")
53
53
  @cli.wait
@@ -0,0 +1,68 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>DigitalOcean - Seems we've encountered a problem!</title>
5
+ <style type="text/css">
6
+ body {
7
+ background-color: #27a1e3;
8
+ margin: 0px;
9
+ padding: 0px;
10
+ }
11
+ div.header{
12
+ background-color: #E2F4FE;
13
+ background-image: url('https://assets.digitalocean.com/public/waves.png');
14
+ background-repeat: repeat-x;
15
+ background-position: center bottom;
16
+ padding-top: 150px;
17
+ }
18
+ div.main{
19
+ overflow: auto;
20
+ width: 960px;
21
+ margin: auto;
22
+ margin-top: 50px;
23
+ }
24
+ div.text{
25
+ width: 450px;
26
+ float: left;
27
+ }
28
+ div.text p{
29
+ color: #fff;
30
+ font-size: 18pt;
31
+ font-family: helvetica;
32
+ }
33
+ div.mascot{
34
+ width: 450px;
35
+ float: right;
36
+ }
37
+ div.copyright{
38
+ clear: both;
39
+ }
40
+ div.copyright p{
41
+ color: #fff;
42
+ text-align: center;
43
+ font-size: 10pt;
44
+ font-family: helvetica;
45
+ padding-top: 60px;
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <div class="header">
52
+ </div>
53
+ <div class="main">
54
+ <div class="text">
55
+ <a href="https://www.digitalocean.com"><img src="https://assets.digitalocean.com/public/logo.png"></a>
56
+ <p>Oh no! It seems as though we've encountered a problem! Please try your request again.</p>
57
+ </div>
58
+ <div class="mascot">
59
+ <img src="https://assets.digitalocean.com/public/mascot.png" />
60
+ </div>
61
+ </div>
62
+ <div class="copyright">
63
+ <p>&copy;2011-2013 DigitalOcean<sup>TM</sup>, Inc. All Rights Reserved.</p>
64
+ </div>
65
+ <div style="display:none">
66
+ ::CLOUDFLARE_ERROR_500S_BOX::
67
+ </div>
68
+ </body>
@@ -6,6 +6,7 @@
6
6
  "image_id": 420,
7
7
  "name": "foo",
8
8
  "ip_address": "33.33.33.10",
9
+ "private_ip_address": "10.20.30.40",
9
10
  "region_id": 1,
10
11
  "size_id": 33,
11
12
  "status": "active"
@@ -0,0 +1,13 @@
1
+ {
2
+ "status": "OK",
3
+ "droplet": {
4
+ "backups_active": null,
5
+ "id": 100823,
6
+ "image_id": 420,
7
+ "name": "test222",
8
+ "ip_address": "33.33.33.10",
9
+ "region_id": 1,
10
+ "size_id": 33,
11
+ "status": "active"
12
+ }
13
+ }
@@ -6,6 +6,7 @@
6
6
  "image_id": 420,
7
7
  "name": "foo",
8
8
  "ip_address": "33.33.33.10",
9
+ "private_ip_address": "10.20.30.40",
9
10
  "region_id": 1,
10
11
  "size_id": 33,
11
12
  "status": "off"
@@ -3,6 +3,7 @@
3
3
  "droplets": [
4
4
  {
5
5
  "ip_address": "33.33.33.10",
6
+ "private_ip_address": "10.20.30.1",
6
7
  "backups_active": null,
7
8
  "id": 100823,
8
9
  "image_id": 420,
@@ -23,6 +24,7 @@
23
24
  },
24
25
  {
25
26
  "ip_address": "33.33.33.10",
27
+ "private_ip_address": "10.20.30.40",
26
28
  "backups_active": null,
27
29
  "id": 100823,
28
30
  "image_id": 420,
@@ -0,0 +1,35 @@
1
+ {
2
+ "status": "OK",
3
+ "droplets": [
4
+ {
5
+ "ip_address": "33.33.33.10",
6
+ "backups_active": null,
7
+ "id": 100823,
8
+ "image_id": 420,
9
+ "name": "test222",
10
+ "region_id": 1,
11
+ "size_id": 33,
12
+ "status": "active"
13
+ },
14
+ {
15
+ "ip_address": "33.33.33.10",
16
+ "backups_active": null,
17
+ "id": 100824,
18
+ "image_id": 420,
19
+ "name": "test223",
20
+ "region_id": 1,
21
+ "size_id": 33,
22
+ "status": "active"
23
+ },
24
+ {
25
+ "ip_address": "33.33.33.10",
26
+ "backups_active": null,
27
+ "id": 100825,
28
+ "image_id": 420,
29
+ "name": "foo",
30
+ "region_id": 1,
31
+ "size_id": 33,
32
+ "status": "active"
33
+ }
34
+ ]
35
+ }
@@ -3,6 +3,7 @@
3
3
  "droplets": [
4
4
  {
5
5
  "ip_address": "33.33.33.10",
6
+ "private_ip_address": "10.20.30.1",
6
7
  "backups_active": null,
7
8
  "id": 100823,
8
9
  "image_id": 420,
@@ -23,6 +24,7 @@
23
24
  },
24
25
  {
25
26
  "ip_address": "33.33.33.10",
27
+ "private_ip_address": "10.20.30.40",
26
28
  "backups_active": null,
27
29
  "id": 100823,
28
30
  "image_id": 420,
@@ -1,17 +1,20 @@
1
1
  {
2
2
  "status": "OK",
3
3
  "regions": [
4
- {
5
- "id": 1,
6
- "name": "Region 1"
7
- },
8
4
  {
9
5
  "id": 2,
10
- "name": "Region 2"
6
+ "name": "Region 2",
7
+ "slug": "reg2"
11
8
  },
12
9
  {
13
10
  "id": 3,
14
- "name": "Region 3"
11
+ "name": "Region 3",
12
+ "slug": "reg3"
13
+ },
14
+ {
15
+ "id": 1,
16
+ "name": "Region 1",
17
+ "slug": "reg1"
15
18
  }
16
19
  ]
17
20
  }
@@ -7,7 +7,7 @@ describe Tugboat::Middleware::Base do
7
7
 
8
8
  describe ".initialize" do
9
9
  it "prints a clear line" do
10
- $stdout.should_receive(:print).with("")
10
+ expect($stdout).to receive(:print).with("")
11
11
  klass.new({})
12
12
  end
13
13
  end
@@ -6,7 +6,7 @@ describe Tugboat::Middleware::CheckCredentials do
6
6
  describe ".call" do
7
7
  it "raises SystemExit with no configuration" do
8
8
  stub_request(:get, "https://api.digitalocean.com/droplets?api_key=#{api_key}&client_id=#{client_key}").
9
- to_return(:status => 200, :body => "<html>You are being redirected...</html>")
9
+ to_return(:headers => {'Content-Type' => 'application/json'}, :status => 200, :body => "<html>You are being redirected...</html>")
10
10
 
11
11
  # Inject the client.
12
12
  env["ocean"] = ocean
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tugboat::Middleware::InjectClient do
4
+ include_context "spec"
5
+
6
+ let(:tmp_path) { project_path + "/tmp/tugboat" }
7
+
8
+ before :each do
9
+ config = Tugboat::Configuration.instance
10
+ env["config"] = config
11
+ end
12
+
13
+ describe ".call" do
14
+
15
+ it "loads the client into the environment" do
16
+ described_class.new(app).call(env)
17
+
18
+ env["ocean"].should be_a DigitalOcean::API
19
+ end
20
+
21
+ it "creates a client with values from config file" do
22
+ DigitalOcean::API.should_receive(:new).with(hash_including(:client_id=>"foo", :api_key=>"bar"))
23
+
24
+ described_class.new(app).call(env)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -8,7 +8,7 @@ describe Tugboat::Middleware::InjectConfiguration do
8
8
  it "loads the configuration into the environment" do
9
9
  described_class.new(app).call(env)
10
10
 
11
- env["config"].should == config
11
+ expect(env["config"]).to eq(config)
12
12
  end
13
13
 
14
14
  end
@@ -4,44 +4,67 @@ describe Tugboat::Middleware::SSHDroplet do
4
4
  include_context "spec"
5
5
 
6
6
  before do
7
- Kernel.stub(:exec)
7
+ allow(Kernel).to receive(:exec)
8
8
  end
9
9
 
10
10
  describe ".call" do
11
11
 
12
12
  it "exec ssh with correct options" do
13
- Kernel.should_receive(:exec).with("ssh",
13
+ expect(Kernel).to receive(:exec).with("ssh",
14
14
  "-o", "IdentitiesOnly=yes",
15
15
  "-o", "LogLevel=ERROR",
16
16
  "-o", "StrictHostKeyChecking=no",
17
17
  "-o", "UserKnownHostsFile=/dev/null",
18
- "-i", ssh_key_path,
18
+ "-i", File.expand_path(ssh_key_path),
19
19
  "-p", ssh_port,
20
- "#{ssh_user}@#{droplet_ip}")
20
+ "#{ssh_user}@#{droplet_ip_private}")
21
21
 
22
22
  env["droplet_ip"] = droplet_ip
23
+ env["droplet_ip_private"] = droplet_ip_private
23
24
  env["config"] = config
24
25
 
25
26
  described_class.new(app).call(env)
26
27
  end
27
28
 
28
29
  it "executes ssh with custom options" do
29
- Kernel.should_receive(:exec).with("ssh",
30
+ expect(Kernel).to receive(:exec).with("ssh",
30
31
  "-o", "IdentitiesOnly=yes",
31
32
  "-o", "LogLevel=ERROR",
32
33
  "-o", "StrictHostKeyChecking=no",
33
34
  "-o", "UserKnownHostsFile=/dev/null",
34
- "-i", ssh_key_path,
35
+ "-i", File.expand_path(ssh_key_path),
35
36
  "-p", ssh_port,
37
+ "-e",
36
38
  "-q",
37
39
  "-X",
38
40
  "#{ssh_user}@#{droplet_ip}",
39
41
  "echo hello")
40
42
 
41
43
  env["droplet_ip"] = droplet_ip
44
+ env["droplet_ip_private"] = droplet_ip_private
42
45
  env["config"] = config
43
46
  env["user_droplet_ssh_command"] = "echo hello"
44
- env["user_droplet_ssh_opts"] = "-q -X"
47
+ env["user_droplet_use_public_ip"] = true
48
+ env["user_droplet_ssh_opts"] = "-e -q -X"
49
+
50
+ described_class.new(app).call(env)
51
+ end
52
+
53
+ it "executes ssh using public ip setting from config" do
54
+ config.data["use_public_ip"] = true
55
+
56
+ expect(Kernel).to receive(:exec).with("ssh",
57
+ "-o", "IdentitiesOnly=yes",
58
+ "-o", "LogLevel=ERROR",
59
+ "-o", "StrictHostKeyChecking=no",
60
+ "-o", "UserKnownHostsFile=/dev/null",
61
+ "-i", File.expand_path(ssh_key_path),
62
+ "-p", ssh_port,
63
+ "#{ssh_user}@#{droplet_ip}")
64
+
65
+ env["droplet_ip"] = droplet_ip
66
+ env["droplet_ip_private"] = droplet_ip_private
67
+ env["config"] = config
45
68
 
46
69
  described_class.new(app).call(env)
47
70
  end
@@ -10,6 +10,7 @@ shared_context "spec" do
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
+ let(:droplet_ip_private) { "10.20.30.40" }
13
14
  let(:droplet_id) { 1234 }
14
15
  let(:region) { '3' }
15
16
  let(:image) { '345791'}
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,12 @@
1
+ require 'simplecov'
1
2
  require 'coveralls'
2
- Coveralls.wear! { add_filter '/spec/' }
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ]
7
+ SimpleCov.start do
8
+ coverage_dir('coverage/')
9
+ end
3
10
 
4
11
  require 'tugboat'
5
12
  require 'webmock/rspec'
@@ -8,7 +15,7 @@ require "shared/environment"
8
15
 
9
16
  RSpec.configure do |config|
10
17
  # Pretty tests
11
- config.color_enabled = true
18
+ config.color = true
12
19
 
13
20
  config.order = :random
14
21
  end
@@ -17,8 +24,8 @@ def project_path
17
24
  File.expand_path("../..", __FILE__)
18
25
  end
19
26
 
20
- def fixture(fixture_name)
21
- File.new(project_path + "/spec/fixtures/#{fixture_name}.json")
27
+ def fixture(fixture_name, format='json')
28
+ File.new(project_path + "/spec/fixtures/#{fixture_name}.#{format}")
22
29
  end
23
30
 
24
31
  ENV["TUGBOAT_CONFIG_PATH"] = project_path + "/tmp/tugboat"
data/tugboat.gemspec CHANGED
@@ -12,20 +12,22 @@ Gem::Specification.new do |gem|
12
12
  gem.summary = %q{A command line tool for interacting with your DigitalOcean droplets.}
13
13
  gem.homepage = "https://github.com/pearkes/tugboat"
14
14
 
15
- gem.files = `git ls-files`.split($/)
16
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
- gem.require_paths = ["lib"]
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.required_ruby_version = ">= 1.9.2"
19
20
 
20
21
  gem.add_dependency "thor", "~> 0.18.1"
21
22
  gem.add_dependency "digital_ocean", "~> 1.0.1"
22
23
  gem.add_dependency "middleware" , "~> 0.1.0"
23
24
 
24
25
  gem.add_development_dependency "rake"
25
- gem.add_development_dependency "rspec-core", "~> 2.13.0"
26
- gem.add_development_dependency "rspec-expectations", "~> 2.13.0"
27
- gem.add_development_dependency "rspec-mocks", "~> 2.13.0"
26
+ gem.add_development_dependency "rspec-core", "~> 2.14.0"
27
+ gem.add_development_dependency "rspec-expectations", "~> 2.14.0"
28
+ gem.add_development_dependency "rspec-mocks", "~> 2.14.0"
28
29
  gem.add_development_dependency "webmock", "~> 1.11.0"
29
30
  gem.add_development_dependency "coveralls", "~> 0.6.7"
31
+ gem.add_development_dependency 'aruba', '~> 0.6.2'
30
32
 
31
33
  end