tugboat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/CONTRIBUTING.md +7 -0
- data/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/README.md +122 -0
- data/Rakefile +1 -0
- data/bin/tugboat +4 -0
- data/lib/tugboat/cli.rb +185 -0
- data/lib/tugboat/config.rb +76 -0
- data/lib/tugboat/middleware/ask_for_credentials.rb +21 -0
- data/lib/tugboat/middleware/base.rb +28 -0
- data/lib/tugboat/middleware/check_configuration.rb +18 -0
- data/lib/tugboat/middleware/check_credentials.rb +24 -0
- data/lib/tugboat/middleware/confirm_action.rb +18 -0
- data/lib/tugboat/middleware/create_droplet.rb +26 -0
- data/lib/tugboat/middleware/destroy_droplet.rb +23 -0
- data/lib/tugboat/middleware/find_droplet.rb +112 -0
- data/lib/tugboat/middleware/halt_droplet.rb +23 -0
- data/lib/tugboat/middleware/info_droplet.rb +37 -0
- data/lib/tugboat/middleware/inject_client.rb +19 -0
- data/lib/tugboat/middleware/inject_configuration.rb +16 -0
- data/lib/tugboat/middleware/list_droplets.rb +24 -0
- data/lib/tugboat/middleware/list_images.rb +29 -0
- data/lib/tugboat/middleware/restart_droplet.rb +23 -0
- data/lib/tugboat/middleware/snapshot_droplet.rb +25 -0
- data/lib/tugboat/middleware/ssh_droplet.rb +26 -0
- data/lib/tugboat/middleware.rb +132 -0
- data/lib/tugboat/version.rb +3 -0
- data/lib/tugboat.rb +6 -0
- data/spec/config_spec.rb +90 -0
- data/spec/spec_helper.rb +11 -0
- data/tugboat.gemspec +24 -0
- metadata +144 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the client has set-up configuration yet.
|
4
|
+
class FindDroplet < Base
|
5
|
+
def call(env)
|
6
|
+
ocean = env["ocean"]
|
7
|
+
user_fuzzy_name = env['user_droplet_fuzzy_name']
|
8
|
+
user_droplet_name = env['user_droplet_name']
|
9
|
+
user_droplet_id = env['user_droplet_id']
|
10
|
+
|
11
|
+
# First, if nothing is provided to us, we should quit and
|
12
|
+
# let the user know.
|
13
|
+
if !user_fuzzy_name && !user_droplet_name && !user_droplet_id
|
14
|
+
say "Tugboat attempted to find a droplet with no arguments.", :red
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
# If you were to `tugboat restart foo -n foo-server-001` then we'd use
|
19
|
+
# 'foo-server-001' without looking up the fuzzy name.
|
20
|
+
#
|
21
|
+
# This is why we check in this order.
|
22
|
+
|
23
|
+
# Easy for us if they provide an id. Just set it to the droplet_id
|
24
|
+
if user_droplet_id
|
25
|
+
say "Droplet id provided. Finding Droplet...", nil, false
|
26
|
+
droplet = ocean.droplets.show user_droplet_id
|
27
|
+
|
28
|
+
env["droplet_id"] = droplet.id
|
29
|
+
env["droplet_name"] = "(#{droplet.name})"
|
30
|
+
env["droplet_ip"] = droplet.ip_address
|
31
|
+
end
|
32
|
+
|
33
|
+
# If they provide a name, we need to get the ID for it.
|
34
|
+
# This requires a lookup.
|
35
|
+
if user_droplet_name && !env["droplet_id"]
|
36
|
+
say "Droplet name provided. Finding droplet ID...", nil, false
|
37
|
+
|
38
|
+
# Look for the droplet by an exact name match.
|
39
|
+
ocean.droplets.list.droplets.each do |d|
|
40
|
+
if droplet.name == user_droplet_name
|
41
|
+
env["droplet_id"] = d.id
|
42
|
+
env["droplet_name"] = "(#{d.name})"
|
43
|
+
env["droplet_ip"] = d.ip_address
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# If we coulnd't find it, tell the user and drop out of the
|
48
|
+
# sequence.
|
49
|
+
if !env["droplet_id"]
|
50
|
+
say "Unable to find a droplet named '#{user_droplet_name}'.", :red
|
51
|
+
return
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# We only need to "fuzzy find" a droplet if a fuzzy name is provided,
|
56
|
+
# and we don't want to fuzzy search if an id or name is provided
|
57
|
+
# with a flag.
|
58
|
+
#
|
59
|
+
# This requires a lookup.
|
60
|
+
if user_fuzzy_name && !env["droplet_id"]
|
61
|
+
say "Droplet fuzzy name provided. Finding droplet ID...", nil, false
|
62
|
+
|
63
|
+
found_droplets = []
|
64
|
+
choices = []
|
65
|
+
|
66
|
+
ocean.droplets.list.droplets.each_with_index do |d, i|
|
67
|
+
# Check to see if one of the droplet names have the fuzzy string.
|
68
|
+
if d.name.include? user_fuzzy_name
|
69
|
+
found_droplets << d
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check to see if we have more then one droplet, and prompt
|
74
|
+
# a user to choose otherwise.
|
75
|
+
if found_droplets.length == 1
|
76
|
+
env["droplet_id"] = found_droplets.first.id
|
77
|
+
env["droplet_name"] = "(#{found_droplets.first.name})"
|
78
|
+
env["droplet_ip"] = found_droplets.first.ip_address
|
79
|
+
elsif found_droplets.length > 1
|
80
|
+
# Did we run the multiple questionairre?
|
81
|
+
did_run_multiple = true
|
82
|
+
|
83
|
+
say "Multiple droplets found."
|
84
|
+
say
|
85
|
+
found_droplets.each_with_index do |d, i|
|
86
|
+
say "#{i}) #{d.name} (#{d.id})"
|
87
|
+
choices << i.to_s
|
88
|
+
end
|
89
|
+
say
|
90
|
+
choice = ask "Please choose a droplet:", :limited_to => choices
|
91
|
+
env["droplet_id"] = found_droplets[choice.to_i].id
|
92
|
+
env["droplet_name"] = found_droplets[choice.to_i].name
|
93
|
+
env["droplet_ip"] = found_droplets[choice.to_i].ip_address
|
94
|
+
end
|
95
|
+
|
96
|
+
# If we coulnd't find it, tell the user and drop out of the
|
97
|
+
# sequence.
|
98
|
+
if !env["droplet_id"]
|
99
|
+
say "error\nUnable to find a droplet named '#{user_fuzzy_name}'.", :red
|
100
|
+
return
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
if !did_run_multiple
|
105
|
+
say "done#{CLEAR}, #{env["droplet_id"]} #{env["droplet_name"]}", :green
|
106
|
+
end
|
107
|
+
@app.call(env)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class HaltDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
say "Queuing shutdown for #{env["droplet_id"]} #{env["droplet_name"]}...", nil, false
|
8
|
+
|
9
|
+
req = ocean.droplets.shutdown env["droplet_id"]
|
10
|
+
|
11
|
+
if req.status == "ERROR"
|
12
|
+
say req.error_message, :red
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
say "done", :green
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class InfoDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
req = ocean.droplets.show env["droplet_id"]
|
8
|
+
|
9
|
+
if req.status == "ERROR"
|
10
|
+
say "#{req.status}: #{req.error_message}", :red
|
11
|
+
return
|
12
|
+
end
|
13
|
+
|
14
|
+
droplet = req.droplet
|
15
|
+
|
16
|
+
if droplet.status == "active"
|
17
|
+
status_color = GREEN
|
18
|
+
else
|
19
|
+
status_color = RED
|
20
|
+
end
|
21
|
+
|
22
|
+
say
|
23
|
+
say "Name: #{droplet.name}"
|
24
|
+
say "ID: #{droplet.id}"
|
25
|
+
say "Status: #{status_color}#{droplet.status}#{CLEAR}"
|
26
|
+
say "IP: #{droplet.ip_address}"
|
27
|
+
say "Region ID: #{droplet.region_id}"
|
28
|
+
say "Image ID: #{droplet.image_id}"
|
29
|
+
say "Size ID: #{droplet.size_id}"
|
30
|
+
say "Backups Active: #{droplet.backups_active || false}"
|
31
|
+
|
32
|
+
@app.call(env)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'digital_ocean'
|
2
|
+
module Tugboat
|
3
|
+
module Middleware
|
4
|
+
# Inject the digital ocean client into the environment
|
5
|
+
class InjectClient < Base
|
6
|
+
def call(env)
|
7
|
+
# Sets the digital ocean client into the environment for use
|
8
|
+
# later.
|
9
|
+
env["ocean"] = DigitalOcean::API.new \
|
10
|
+
:client_id => env["config"].client_key,
|
11
|
+
:api_key => env["config"].api_key,
|
12
|
+
:debug => ENV['DEBUG'] || false
|
13
|
+
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the client has set-up configuration yet.
|
4
|
+
class InjectConfiguration < Base
|
5
|
+
def call(env)
|
6
|
+
config = Tugboat::Configuration.instance
|
7
|
+
config.reset!
|
8
|
+
|
9
|
+
env["config"] = config
|
10
|
+
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the client has set-up configuration yet.
|
4
|
+
class ListDroplets < Base
|
5
|
+
def call(env)
|
6
|
+
ocean = env["ocean"]
|
7
|
+
|
8
|
+
ocean.droplets.list.droplets.each do |droplet|
|
9
|
+
|
10
|
+
if droplet.status == "active"
|
11
|
+
status_color = GREEN
|
12
|
+
else
|
13
|
+
status_color = RED
|
14
|
+
end
|
15
|
+
|
16
|
+
say "#{droplet.name} (ip: #{droplet.ip_address}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region_id}, id: #{droplet.id})"
|
17
|
+
end
|
18
|
+
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class ListImages < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
my_images = ocean.images.list :filter => "my_images"
|
7
|
+
if env["user_show_global_images"]
|
8
|
+
global = ocean.images.list :filter => "global"
|
9
|
+
end
|
10
|
+
|
11
|
+
say "My Images:"
|
12
|
+
my_images.images.each do |image|
|
13
|
+
say "#{image.name} (id: #{image.id}, distro: #{image.distribution})"
|
14
|
+
end
|
15
|
+
|
16
|
+
if env["user_show_global_images"]
|
17
|
+
say
|
18
|
+
say "Global Images:"
|
19
|
+
global.images.each do |image|
|
20
|
+
say "#{image.name} (id: #{image.id}, distro: #{image.distribution})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@app.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class RestartDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
say "Queuing restart for #{env["droplet_id"]} #{env["droplet_name"]}...", nil, false
|
8
|
+
|
9
|
+
req = ocean.droplets.reboot env["droplet_id"]
|
10
|
+
|
11
|
+
if req.status == "ERROR"
|
12
|
+
say "#{req.status}: #{req.error_message}", :red
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
say "done", :green
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class SnapshotDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
say "Queuing snapshot '#{env["user_snapshot_name"]}' for #{env["droplet_id"]} #{env["droplet_name"]}...", nil, false
|
8
|
+
|
9
|
+
# Temporary
|
10
|
+
req = ocean.droplets.snapshot env["droplet_id"],
|
11
|
+
:name => env["user_snapshot_name"]
|
12
|
+
|
13
|
+
if req.status == "ERROR"
|
14
|
+
say "#{req.status}: #{req.error_message}", :red
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
say "done", :green
|
19
|
+
|
20
|
+
@app.call(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class SSHDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
say "Executing SSH #{env["droplet_name"]}..."
|
6
|
+
|
7
|
+
options = [
|
8
|
+
"-o", "IdentitiesOnly=yes",
|
9
|
+
"-o", "LogLevel=ERROR",
|
10
|
+
"-o", "StrictHostKeyChecking=no",
|
11
|
+
"-o", "UserKnownHostsFile=/dev/null",
|
12
|
+
"-i", env["config"].ssh_key_path.to_s]
|
13
|
+
|
14
|
+
host_string = "#{env["config"].ssh_user}@#{env["droplet_ip"]}"
|
15
|
+
|
16
|
+
options << host_string
|
17
|
+
|
18
|
+
|
19
|
+
Kernel.exec("ssh", *options)
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require "middleware"
|
2
|
+
|
3
|
+
module Tugboat
|
4
|
+
module Middleware
|
5
|
+
autoload :Base, "tugboat/middleware/base"
|
6
|
+
autoload :InjectConfiguration, "tugboat/middleware/inject_configuration"
|
7
|
+
autoload :CheckConfiguration, "tugboat/middleware/check_configuration"
|
8
|
+
autoload :AskForCredentials, "tugboat/middleware/ask_for_credentials"
|
9
|
+
autoload :InjectClient, "tugboat/middleware/inject_client"
|
10
|
+
autoload :CheckCredentials, "tugboat/middleware/check_credentials"
|
11
|
+
autoload :ListDroplets, "tugboat/middleware/list_droplets"
|
12
|
+
autoload :FindDroplet, "tugboat/middleware/find_droplet"
|
13
|
+
autoload :RestartDroplet, "tugboat/middleware/restart_droplet"
|
14
|
+
autoload :HaltDroplet, "tugboat/middleware/halt_droplet"
|
15
|
+
autoload :InfoDroplet, "tugboat/middleware/info_droplet"
|
16
|
+
autoload :SSHDroplet, "tugboat/middleware/ssh_droplet"
|
17
|
+
autoload :CreateDroplet, "tugboat/middleware/create_droplet"
|
18
|
+
autoload :DestroyDroplet, "tugboat/middleware/destroy_droplet"
|
19
|
+
autoload :ConfirmAction, "tugboat/middleware/confirm_action"
|
20
|
+
autoload :SnapshotDroplet, "tugboat/middleware/snapshot_droplet"
|
21
|
+
autoload :ListImages, "tugboat/middleware/list_images"
|
22
|
+
|
23
|
+
# This takes the user through the authorization flow
|
24
|
+
def self.sequence_authorize
|
25
|
+
::Middleware::Builder.new do
|
26
|
+
use InjectConfiguration
|
27
|
+
use AskForCredentials
|
28
|
+
use InjectConfiguration
|
29
|
+
use CheckConfiguration
|
30
|
+
use InjectClient
|
31
|
+
use CheckCredentials
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# This provides a list of droplets
|
36
|
+
def self.sequence_list_droplets
|
37
|
+
::Middleware::Builder.new do
|
38
|
+
use InjectConfiguration
|
39
|
+
use CheckConfiguration
|
40
|
+
use InjectClient
|
41
|
+
use ListDroplets
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# This provides a list of droplets
|
46
|
+
def self.sequence_list_images
|
47
|
+
::Middleware::Builder.new do
|
48
|
+
use InjectConfiguration
|
49
|
+
use CheckConfiguration
|
50
|
+
use InjectClient
|
51
|
+
use ListImages
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# This restarts a droplet
|
56
|
+
def self.sequence_restart_droplet
|
57
|
+
::Middleware::Builder.new do
|
58
|
+
use InjectConfiguration
|
59
|
+
use CheckConfiguration
|
60
|
+
use InjectClient
|
61
|
+
use FindDroplet
|
62
|
+
use RestartDroplet
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# This halts a droplet
|
67
|
+
def self.sequence_halt_droplet
|
68
|
+
::Middleware::Builder.new do
|
69
|
+
use InjectConfiguration
|
70
|
+
use CheckConfiguration
|
71
|
+
use InjectClient
|
72
|
+
use FindDroplet
|
73
|
+
use HaltDroplet
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# This shows a droplet
|
78
|
+
def self.sequence_info_droplet
|
79
|
+
::Middleware::Builder.new do
|
80
|
+
use InjectConfiguration
|
81
|
+
use CheckConfiguration
|
82
|
+
use InjectClient
|
83
|
+
use FindDroplet
|
84
|
+
use InfoDroplet
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# SSH Into a droplet
|
89
|
+
def self.sequence_ssh_droplet
|
90
|
+
::Middleware::Builder.new do
|
91
|
+
use InjectConfiguration
|
92
|
+
use CheckConfiguration
|
93
|
+
use InjectClient
|
94
|
+
use FindDroplet
|
95
|
+
use SSHDroplet
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# SSH Into a droplet
|
100
|
+
def self.sequence_create_droplet
|
101
|
+
::Middleware::Builder.new do
|
102
|
+
use InjectConfiguration
|
103
|
+
use CheckConfiguration
|
104
|
+
use InjectClient
|
105
|
+
use CreateDroplet
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# SSH Into a droplet
|
110
|
+
def self.sequence_destroy_droplet
|
111
|
+
::Middleware::Builder.new do
|
112
|
+
use InjectConfiguration
|
113
|
+
use CheckConfiguration
|
114
|
+
use InjectClient
|
115
|
+
use FindDroplet
|
116
|
+
use ConfirmAction
|
117
|
+
use DestroyDroplet
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# SSH Into a droplet
|
122
|
+
def self.sequence_snapshot_droplet
|
123
|
+
::Middleware::Builder.new do
|
124
|
+
use InjectConfiguration
|
125
|
+
use CheckConfiguration
|
126
|
+
use InjectClient
|
127
|
+
use FindDroplet
|
128
|
+
use SnapshotDroplet
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/tugboat.rb
ADDED
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tugboat::Configuration do
|
4
|
+
let(:tmp_path) { project_path + "/tmp/tugboat" }
|
5
|
+
|
6
|
+
after :each do
|
7
|
+
# Clean up the temp file.
|
8
|
+
File.delete(project_path + "/tmp/tugboat") if File.exist?(project_path + "/tmp/tugboat")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "is a singleton" do
|
12
|
+
expect(Tugboat::Configuration).to be_a Class
|
13
|
+
expect do
|
14
|
+
Tugboat::Configuration.new
|
15
|
+
end.to raise_error(NoMethodError, /private method `new' called/)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "has a data attribute" do
|
19
|
+
config = Tugboat::Configuration.instance
|
20
|
+
expect(config.data).to be
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can set a path" do
|
24
|
+
config = Tugboat::Configuration.instance
|
25
|
+
config.path = tmp_path
|
26
|
+
expect(config.path).to equal(tmp_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "properly resets path" do
|
30
|
+
config = Tugboat::Configuration.instance
|
31
|
+
config.path = tmp_path
|
32
|
+
config.reset!
|
33
|
+
expect(config.path).to eq(File.join(File.expand_path("~"), '.tugboat'))
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "the file" do
|
37
|
+
let(:client_key) { "foo" }
|
38
|
+
let(:api_key) { "bar" }
|
39
|
+
let(:ssh_user) { "baz" }
|
40
|
+
let(:ssh_key_path) { "~/.ssh/id_rsa2.pub" }
|
41
|
+
|
42
|
+
let(:config) { config = Tugboat::Configuration.instance }
|
43
|
+
|
44
|
+
before :each do
|
45
|
+
# Create a temporary file
|
46
|
+
config.path = tmp_path
|
47
|
+
config.create_config_file(client_key, api_key, ssh_key_path, ssh_user)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can be created" do
|
51
|
+
expect(File.exist?(tmp_path)).to be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can be loaded" do
|
55
|
+
data = config.load_config_file
|
56
|
+
expect(data).to_not be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "the file format"
|
60
|
+
let(:data) { YAML.load_file(tmp_path) }
|
61
|
+
|
62
|
+
it "should have authentication at the top level" do
|
63
|
+
expect(data).to have_key("authentication")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should have ssh at the top level" do
|
67
|
+
expect(data).to have_key("ssh")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should have a client key" do
|
71
|
+
auth = data["authentication"]
|
72
|
+
expect(auth).to have_key("client_key")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should have an api key" do
|
76
|
+
auth = data["authentication"]
|
77
|
+
expect(auth).to have_key("api_key")
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should have an ssh key path" do
|
81
|
+
ssh = data["ssh"]
|
82
|
+
expect(ssh).to have_key("ssh_key_path")
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should have an ssh user" do
|
86
|
+
ssh = data["ssh"]
|
87
|
+
expect(ssh).to have_key("ssh_user")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/tugboat.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tugboat/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "tugboat"
|
8
|
+
gem.version = Tugboat::VERSION
|
9
|
+
gem.authors = ["Jack Pearkes"]
|
10
|
+
gem.email = ["jackpearkes@gmail.com"]
|
11
|
+
gem.description = %q{A command line tool for interacting with your DigitalOcean droplets.}
|
12
|
+
gem.summary = %q{A command line tool for interacting with your DigitalOcean droplets.}
|
13
|
+
gem.homepage = "https://github.com/pearkes/tugboat"
|
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"]
|
19
|
+
|
20
|
+
gem.add_dependency "thor", "~> 0.18.1"
|
21
|
+
gem.add_dependency "digital_ocean", "~> 1.0.1"
|
22
|
+
gem.add_dependency "middleware" , "~> 0.1.0"
|
23
|
+
gem.add_dependency "activesupport"
|
24
|
+
end
|