tugboat 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -4
- data/CHANGELOG.md +325 -102
- data/CHANGELOG_old.md +150 -0
- data/Gemfile +6 -0
- data/README.md +12 -6
- data/Rakefile +7 -3
- data/features/step_definitions/steps.rb +0 -0
- data/features/support/env.rb +9 -0
- data/features/vagrant-adam/config_current_directory.feature +27 -0
- data/lib/tugboat/cli.rb +31 -3
- data/lib/tugboat/config.rb +13 -5
- data/lib/tugboat/middleware.rb +8 -0
- data/lib/tugboat/middleware/ask_for_credentials.rb +5 -4
- data/lib/tugboat/middleware/authentication_middleware.rb +2 -2
- data/lib/tugboat/middleware/config.rb +29 -0
- data/lib/tugboat/middleware/custom_logger.rb +2 -2
- data/lib/tugboat/middleware/find_droplet.rb +4 -0
- data/lib/tugboat/middleware/info_droplet.rb +5 -0
- data/lib/tugboat/middleware/inject_client.rb +1 -1
- data/lib/tugboat/middleware/list_droplets.rb +5 -1
- data/lib/tugboat/middleware/list_regions.rb +2 -2
- data/lib/tugboat/middleware/ssh_droplet.rb +17 -2
- data/lib/tugboat/version.rb +1 -1
- data/spec/cli/add_key_spec.rb +9 -9
- data/spec/cli/authorize_cli_spec.rb +76 -52
- data/spec/cli/create_cli_spec.rb +39 -4
- data/spec/cli/debug_cli_spec.rb +44 -0
- data/spec/cli/destroy_cli_spec.rb +30 -11
- data/spec/cli/destroy_image_cli_spec.rb +11 -11
- data/spec/cli/droplets_cli_spec.rb +6 -6
- data/spec/cli/halt_cli_spec.rb +9 -9
- data/spec/cli/images_cli_spec.rb +6 -6
- data/spec/cli/info_cli_spec.rb +42 -7
- data/spec/cli/info_image_cli_spec.rb +6 -6
- data/spec/cli/keys_cli_spec.rb +1 -1
- data/spec/cli/password_reset_cli_spec.rb +8 -8
- data/spec/cli/rebuild_cli_spec.rb +49 -49
- data/spec/cli/regions_cli_spec.rb +4 -4
- data/spec/cli/resize_cli_spec.rb +8 -8
- data/spec/cli/restart_cli_spec.rb +8 -8
- data/spec/cli/sizes_cli_spec.rb +1 -1
- data/spec/cli/snapshot_cli_spec.rb +7 -7
- data/spec/cli/ssh_cli_spec.rb +4 -4
- data/spec/cli/start_cli_spec.rb +7 -7
- data/spec/cli/verify_cli_spec.rb +10 -2
- data/spec/cli/wait_cli_spec.rb +6 -6
- data/spec/fixtures/500.html +68 -0
- data/spec/fixtures/show_droplet.json +1 -0
- data/spec/fixtures/show_droplet_fuzzy.json +13 -0
- data/spec/fixtures/show_droplet_inactive.json +1 -0
- data/spec/fixtures/show_droplets.json +2 -0
- data/spec/fixtures/show_droplets_fuzzy.json +35 -0
- data/spec/fixtures/show_droplets_inactive.json +2 -0
- data/spec/fixtures/show_regions.json +9 -6
- data/spec/middleware/base_spec.rb +1 -1
- data/spec/middleware/check_credentials_spec.rb +1 -1
- data/spec/middleware/inject_client_spec.rb +29 -0
- data/spec/middleware/inject_configuration_spec.rb +1 -1
- data/spec/middleware/ssh_droplet_spec.rb +30 -7
- data/spec/shared/environment.rb +1 -0
- data/spec/spec_helper.rb +11 -4
- data/tugboat.gemspec +9 -7
- metadata +64 -33
data/spec/cli/wait_cli_spec.rb
CHANGED
@@ -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>©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>
|
@@ -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
|
}
|
@@ -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
|
@@ -4,44 +4,67 @@ describe Tugboat::Middleware::SSHDroplet do
|
|
4
4
|
include_context "spec"
|
5
5
|
|
6
6
|
before do
|
7
|
-
Kernel.
|
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.
|
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}@#{
|
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.
|
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["
|
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
|
data/spec/shared/environment.rb
CHANGED
@@ -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
|
-
|
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.
|
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}
|
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
|
16
|
-
gem.executables
|
17
|
-
gem.test_files
|
18
|
-
gem.require_paths
|
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.
|
26
|
-
gem.add_development_dependency "rspec-expectations", "~> 2.
|
27
|
-
gem.add_development_dependency "rspec-mocks", "~> 2.
|
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
|