tugboat 0.0.1
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.
- 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
|