tugboat 2.2.2 → 2.2.3
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 +4 -4
- data/.rubocop.yml +520 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +0 -6
- data/README.md +34 -2
- data/Rakefile +6 -1
- data/bin/tugboat +2 -2
- data/features/cassettes/config/Array_of_SSH_Keys_in_Config.yml +1 -1
- data/features/cassettes/config/Single_SSH_key_as_number_in_config.yml +1 -1
- data/features/step_definitions/steps.rb +1 -1
- data/features/support/env.rb +7 -3
- data/lib/tugboat.rb +2 -2
- data/lib/tugboat/cli.rb +394 -444
- data/lib/tugboat/config.rb +43 -61
- data/lib/tugboat/middleware.rb +33 -33
- data/lib/tugboat/middleware/add_key.rb +12 -13
- data/lib/tugboat/middleware/ask_for_credentials.rb +12 -13
- data/lib/tugboat/middleware/base.rb +25 -20
- data/lib/tugboat/middleware/check_configuration.rb +3 -6
- data/lib/tugboat/middleware/check_credentials.rb +0 -1
- data/lib/tugboat/middleware/check_droplet_active.rb +2 -4
- data/lib/tugboat/middleware/check_droplet_inactive.rb +2 -4
- data/lib/tugboat/middleware/config.rb +3 -5
- data/lib/tugboat/middleware/confirm_action.rb +4 -6
- data/lib/tugboat/middleware/create_droplet.rb +27 -44
- data/lib/tugboat/middleware/custom_logger.rb +52 -54
- data/lib/tugboat/middleware/destroy_droplet.rb +5 -6
- data/lib/tugboat/middleware/destroy_image.rb +5 -6
- data/lib/tugboat/middleware/find_droplet.rb +43 -47
- data/lib/tugboat/middleware/find_image.rb +23 -29
- data/lib/tugboat/middleware/halt_droplet.rb +9 -10
- data/lib/tugboat/middleware/info_droplet.rb +30 -33
- data/lib/tugboat/middleware/info_image.rb +1 -1
- data/lib/tugboat/middleware/inject_client.rb +8 -10
- data/lib/tugboat/middleware/inject_configuration.rb +1 -2
- data/lib/tugboat/middleware/list_droplets.rb +9 -10
- data/lib/tugboat/middleware/list_images.rb +9 -9
- data/lib/tugboat/middleware/list_regions.rb +1 -1
- data/lib/tugboat/middleware/list_sizes.rb +1 -1
- data/lib/tugboat/middleware/list_ssh_keys.rb +1 -3
- data/lib/tugboat/middleware/password_reset.rb +6 -7
- data/lib/tugboat/middleware/rebuild_droplet.rb +7 -7
- data/lib/tugboat/middleware/resize_droplet.rb +6 -7
- data/lib/tugboat/middleware/restart_droplet.rb +4 -11
- data/lib/tugboat/middleware/snapshot_droplet.rb +7 -8
- data/lib/tugboat/middleware/ssh_droplet.rb +30 -31
- data/lib/tugboat/middleware/start_droplet.rb +5 -5
- data/lib/tugboat/middleware/wait_for_state.rb +2 -3
- data/lib/tugboat/version.rb +1 -1
- data/spec/cli/add_key_spec.rb +25 -28
- data/spec/cli/authorize_cli_spec.rb +57 -60
- data/spec/cli/config_cli_spec.rb +8 -11
- data/spec/cli/create_cli_spec.rb +40 -46
- data/spec/cli/debug_cli_spec.rb +29 -29
- data/spec/cli/destroy_cli_spec.rb +58 -60
- data/spec/cli/destroy_image_cli_spec.rb +42 -45
- data/spec/cli/droplets_cli_spec.rb +62 -64
- data/spec/cli/env_variable_spec.rb +14 -15
- data/spec/cli/halt_cli_spec.rb +65 -69
- data/spec/cli/help_cli_spec.rb +8 -8
- data/spec/cli/images_cli_spec.rb +28 -30
- data/spec/cli/info_cli_spec.rb +144 -147
- data/spec/cli/info_image_cli_spec.rb +57 -60
- data/spec/cli/keys_cli_spec.rb +8 -10
- data/spec/cli/password_reset_cli_spec.rb +56 -56
- data/spec/cli/rebuild_cli_spec.rb +194 -198
- data/spec/cli/regions_cli_spec.rb +8 -8
- data/spec/cli/resize_cli_spec.rb +54 -56
- data/spec/cli/restart_cli_spec.rb +53 -57
- data/spec/cli/sizes_cli_spec.rb +7 -8
- data/spec/cli/snapshot_cli_spec.rb +50 -53
- data/spec/cli/ssh_cli_spec.rb +41 -42
- data/spec/cli/start_cli_spec.rb +48 -52
- data/spec/cli/verify_cli_spec.rb +22 -25
- data/spec/cli/version_cli_spec.rb +6 -8
- data/spec/cli/wait_cli_spec.rb +50 -52
- data/spec/config_spec.rb +56 -57
- data/spec/middleware/base_spec.rb +5 -6
- data/spec/middleware/check_configuration_spec.rb +5 -7
- data/spec/middleware/check_credentials_spec.rb +9 -10
- data/spec/middleware/check_droplet_active_spec.rb +5 -7
- data/spec/middleware/check_droplet_inactive_spec.rb +5 -7
- data/spec/middleware/find_droplet_spec.rb +4 -5
- data/spec/middleware/find_image_spec.rb +4 -5
- data/spec/middleware/inject_client_spec.rb +9 -12
- data/spec/middleware/inject_configuration_spec.rb +4 -7
- data/spec/middleware/ssh_droplet_spec.rb +70 -73
- data/spec/shared/environment.rb +18 -20
- data/spec/spec_helper.rb +4 -4
- data/tugboat.gemspec +10 -6
- metadata +88 -17
@@ -11,7 +11,7 @@ module Tugboat
|
|
11
11
|
# First, if nothing is provided to us, we should quit and
|
12
12
|
# let the user know.
|
13
13
|
if !user_fuzzy_name && !user_image_name && !user_image_id
|
14
|
-
say
|
14
|
+
say 'Tugboat attempted to find an image with no arguments.', :red
|
15
15
|
say "Try running `tugboat #{env['tugboat_action']} imagename`", :green
|
16
16
|
say "For more help run: `tugboat help #{env['tugboat_action']}`", :blue
|
17
17
|
exit 1
|
@@ -24,7 +24,7 @@ module Tugboat
|
|
24
24
|
|
25
25
|
# Easy for us if they provide an id. Just set it to the image_id
|
26
26
|
if user_image_id
|
27
|
-
say
|
27
|
+
say 'Image id provided. Finding Image...', nil, false
|
28
28
|
response = ocean.image.show user_image_id
|
29
29
|
|
30
30
|
unless response.success?
|
@@ -32,26 +32,26 @@ module Tugboat
|
|
32
32
|
exit 1
|
33
33
|
end
|
34
34
|
|
35
|
-
env[
|
36
|
-
env[
|
35
|
+
env['image_id'] = response.image.id
|
36
|
+
env['image_name'] = "(#{response.image.name})"
|
37
37
|
end
|
38
38
|
|
39
39
|
# If they provide a name, we need to get the ID for it.
|
40
40
|
# This requires a lookup.
|
41
|
-
if user_image_name && !env[
|
42
|
-
say
|
41
|
+
if user_image_name && !env['image_id']
|
42
|
+
say 'Image name provided. Finding Image...', nil, false
|
43
43
|
|
44
44
|
# Look for the image by an exact name match.
|
45
45
|
ocean.image.all['images'].each do |d|
|
46
46
|
if d.name == user_image_name
|
47
|
-
env[
|
48
|
-
env[
|
47
|
+
env['image_id'] = d.id
|
48
|
+
env['image_name'] = "(#{d.name})"
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
# If we coulnd't find it, tell the user and drop out of the
|
53
53
|
# sequence.
|
54
|
-
|
54
|
+
unless env['image_id']
|
55
55
|
say "error\nUnable to find an image named '#{user_image_name}'.", :red
|
56
56
|
exit 1
|
57
57
|
end
|
@@ -62,61 +62,55 @@ module Tugboat
|
|
62
62
|
# with a flag.
|
63
63
|
#
|
64
64
|
# This requires a lookup.
|
65
|
-
if user_fuzzy_name && !env[
|
66
|
-
say
|
65
|
+
if user_fuzzy_name && !env['image_id']
|
66
|
+
say 'Image fuzzy name provided. Finding image ID...', nil, false
|
67
67
|
|
68
68
|
found_images = []
|
69
69
|
choices = []
|
70
70
|
|
71
|
-
ocean.image.all['images'].each_with_index do |d,
|
72
|
-
|
71
|
+
ocean.image.all['images'].each_with_index do |d, _i|
|
73
72
|
# Check to see if one of the image names have the fuzzy string.
|
74
|
-
if d.name.upcase.include? user_fuzzy_name.upcase
|
75
|
-
found_images << d
|
76
|
-
end
|
73
|
+
found_images << d if d.name.upcase.include? user_fuzzy_name.upcase
|
77
74
|
|
78
75
|
unless d.slug.nil?
|
79
|
-
if d.slug.upcase.include? user_fuzzy_name.upcase
|
80
|
-
found_images << d
|
81
|
-
end
|
76
|
+
found_images << d if d.slug.upcase.include? user_fuzzy_name.upcase
|
82
77
|
end
|
83
78
|
end
|
84
79
|
|
85
80
|
# Check to see if we have more then one image, and prompt
|
86
81
|
# a user to choose otherwise.
|
87
82
|
if found_images.length == 1
|
88
|
-
env[
|
89
|
-
env[
|
83
|
+
env['image_id'] = found_images.first.id
|
84
|
+
env['image_name'] = "(#{found_images.first.name})"
|
90
85
|
elsif found_images.length > 1
|
91
86
|
# Did we run the multiple questionairre?
|
92
87
|
did_run_multiple = true
|
93
88
|
|
94
|
-
say
|
89
|
+
say 'Multiple images found.'
|
95
90
|
say
|
96
91
|
found_images.each_with_index do |d, i|
|
97
92
|
say "#{i}) #{d.name} (#{d.id})"
|
98
93
|
choices << i.to_s
|
99
94
|
end
|
100
95
|
say
|
101
|
-
choice = ask
|
102
|
-
env[
|
103
|
-
env[
|
96
|
+
choice = ask 'Please choose a image:', limited_to: choices
|
97
|
+
env['image_id'] = found_images[choice.to_i].id
|
98
|
+
env['image_name'] = found_images[choice.to_i].name
|
104
99
|
end
|
105
100
|
|
106
101
|
# If we coulnd't find it, tell the user and drop out of the
|
107
102
|
# sequence.
|
108
|
-
|
103
|
+
unless env['image_id']
|
109
104
|
say "error\nUnable to find an image named '#{user_fuzzy_name}'.", :red
|
110
105
|
exit 1
|
111
106
|
end
|
112
107
|
end
|
113
108
|
|
114
|
-
|
115
|
-
say "done#{CLEAR}, #{env[
|
109
|
+
unless did_run_multiple
|
110
|
+
say "done#{CLEAR}, #{env['image_id']} #{env['image_name']}", :green
|
116
111
|
end
|
117
112
|
@app.call(env)
|
118
113
|
end
|
119
114
|
end
|
120
115
|
end
|
121
116
|
end
|
122
|
-
|
@@ -4,19 +4,19 @@ module Tugboat
|
|
4
4
|
def call(env)
|
5
5
|
ocean = env['barge']
|
6
6
|
|
7
|
-
response = if env[
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
response = if env['user_droplet_hard']
|
8
|
+
say "Queuing hard shutdown for #{env['droplet_id']} #{env['droplet_name']}...", nil, false
|
9
|
+
ocean.droplet.power_off env['droplet_id']
|
10
|
+
else
|
11
|
+
say "Queuing shutdown for #{env['droplet_id']} #{env['droplet_name']}...", nil, false
|
12
|
+
ocean.droplet.shutdown env['droplet_id']
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
if response.success?
|
16
|
+
say 'Halt successful!', :green
|
17
|
+
else
|
16
18
|
say "Failed to halt on Droplet: #{response.message}", :red
|
17
19
|
exit 1
|
18
|
-
else
|
19
|
-
say "Halt successful!", :green
|
20
20
|
end
|
21
21
|
|
22
22
|
@app.call(env)
|
@@ -24,4 +24,3 @@ module Tugboat
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
@@ -4,7 +4,7 @@ module Tugboat
|
|
4
4
|
def call(env)
|
5
5
|
ocean = env['barge']
|
6
6
|
|
7
|
-
response = ocean.droplet.show env[
|
7
|
+
response = ocean.droplet.show env['droplet_id']
|
8
8
|
|
9
9
|
check_response_success('get info for Droplet', response)
|
10
10
|
|
@@ -15,45 +15,45 @@ module Tugboat
|
|
15
15
|
exit 1
|
16
16
|
end
|
17
17
|
|
18
|
-
if droplet.status ==
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
status_color = if droplet.status == 'active'
|
19
|
+
GREEN
|
20
|
+
else
|
21
|
+
RED
|
22
|
+
end
|
23
23
|
|
24
|
-
attribute = env[
|
24
|
+
attribute = env['user_attribute']
|
25
25
|
|
26
|
-
droplet_ip4_public = droplet.networks.v4.
|
27
|
-
droplet_ip6_public = droplet.networks.v6.
|
28
|
-
check_private_ip = droplet.networks.v4.
|
26
|
+
droplet_ip4_public = droplet.networks.v4.find { |address| address.type == 'public' }.ip_address
|
27
|
+
droplet_ip6_public = droplet.networks.v6.find { |address| address.type == 'public' }.ip_address unless droplet.networks.v6.empty?
|
28
|
+
check_private_ip = droplet.networks.v4.find { |address| address.type == 'private' }
|
29
29
|
droplet_private_ip = check_private_ip.ip_address if check_private_ip
|
30
30
|
|
31
31
|
attributes_list = [
|
32
|
-
[
|
33
|
-
[
|
34
|
-
[
|
35
|
-
[
|
36
|
-
[
|
37
|
-
[
|
38
|
-
[
|
39
|
-
[
|
40
|
-
[
|
41
|
-
[
|
32
|
+
['name', droplet.name],
|
33
|
+
['id', droplet.id],
|
34
|
+
['status', droplet.status],
|
35
|
+
['ip4', droplet_ip4_public],
|
36
|
+
['ip6', droplet_ip6_public],
|
37
|
+
['private_ip', droplet_private_ip],
|
38
|
+
['region', droplet.region.slug],
|
39
|
+
['image', droplet.image.id],
|
40
|
+
['size', droplet.size_slug],
|
41
|
+
['backups_active', !droplet.backup_ids.empty?]
|
42
42
|
]
|
43
43
|
attributes = Hash[*attributes_list.flatten(1)]
|
44
44
|
|
45
45
|
if attribute
|
46
|
-
if attributes.
|
46
|
+
if attributes.key? attribute
|
47
47
|
say attributes[attribute]
|
48
48
|
else
|
49
49
|
say "Invalid attribute \"#{attribute}\"", :red
|
50
|
-
say
|
50
|
+
say 'Provide one of the following:', :red
|
51
51
|
attributes_list.each { |a| say " #{a[0]}", :red }
|
52
52
|
exit 1
|
53
53
|
end
|
54
54
|
else
|
55
|
-
if env[
|
56
|
-
attributes_list.select{ |a| a[1]
|
55
|
+
if env['user_porcelain']
|
56
|
+
attributes_list.select { |a| !a[1].nil? }.each { |a| say "#{a[0]} #{a[1]}" }
|
57
57
|
else
|
58
58
|
say
|
59
59
|
say "Name: #{droplet.name}"
|
@@ -62,15 +62,13 @@ module Tugboat
|
|
62
62
|
say "IP4: #{droplet_ip4_public}"
|
63
63
|
say "IP6: #{droplet_ip6_public}" unless droplet.networks.v6.empty?
|
64
64
|
|
65
|
-
if droplet_private_ip
|
66
|
-
say "Private IP: #{droplet_private_ip}"
|
67
|
-
end
|
65
|
+
say "Private IP: #{droplet_private_ip}" if droplet_private_ip
|
68
66
|
|
69
|
-
if droplet.image.slug.nil?
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
image_description = if droplet.image.slug.nil?
|
68
|
+
droplet.image.name
|
69
|
+
else
|
70
|
+
droplet.image.slug
|
71
|
+
end
|
74
72
|
|
75
73
|
say "Region: #{droplet.region.name} - #{droplet.region.slug}"
|
76
74
|
say "Image: #{droplet.image.id} - #{image_description}"
|
@@ -84,4 +82,3 @@ module Tugboat
|
|
84
82
|
end
|
85
83
|
end
|
86
84
|
end
|
87
|
-
|
@@ -5,19 +5,17 @@ module Tugboat
|
|
5
5
|
module Middleware
|
6
6
|
# Inject the digital ocean client into the environment
|
7
7
|
class InjectClient < Base
|
8
|
+
def call(env)
|
9
|
+
# Sets the digital ocean client into the environment for use
|
10
|
+
# later.
|
11
|
+
@access_token = env['config'].access_token
|
8
12
|
|
9
|
-
|
10
|
-
# Sets the digital ocean client into the environment for use
|
11
|
-
# later.
|
12
|
-
@access_token = env["config"].access_token
|
13
|
+
env['barge'] = Barge::Client.new(access_token: @access_token)
|
13
14
|
|
14
|
-
|
15
|
+
env['barge'].faraday.use CustomLogger if ENV['DEBUG']
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
@app.call(env)
|
19
|
-
end
|
17
|
+
@app.call(env)
|
18
|
+
end
|
20
19
|
end
|
21
20
|
end
|
22
21
|
end
|
23
|
-
|
@@ -13,22 +13,22 @@ module Tugboat
|
|
13
13
|
droplet_list.each do |droplet|
|
14
14
|
has_one = true
|
15
15
|
|
16
|
-
private_addr = droplet.networks.v4.
|
16
|
+
private_addr = droplet.networks.v4.find { |address| address.type == 'private' }
|
17
17
|
if private_addr
|
18
18
|
private_ip = ", private_ip: #{private_addr.ip_address}"
|
19
19
|
end
|
20
20
|
|
21
|
-
if droplet.status ==
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
status_color = if droplet.status == 'active'
|
22
|
+
GREEN
|
23
|
+
else
|
24
|
+
RED
|
25
|
+
end
|
26
26
|
|
27
|
-
public_addr = droplet.networks.v4.
|
28
|
-
say "#{droplet.name} (ip: #{public_addr.ip_address}#{private_ip}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region.slug}, id: #{droplet.id}#{env[
|
27
|
+
public_addr = droplet.networks.v4.find { |address| address.type == 'public' }
|
28
|
+
say "#{droplet.name} (ip: #{public_addr.ip_address}#{private_ip}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region.slug}, id: #{droplet.id}#{env['include_urls'] ? droplet_id_to_url(droplet.id) : ''})"
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
unless has_one
|
32
32
|
say "You don't appear to have any droplets.", :red
|
33
33
|
say "Try creating one with #{GREEN}\`tugboat create\`#{CLEAR}"
|
34
34
|
end
|
@@ -44,4 +44,3 @@ module Tugboat
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
@@ -3,33 +3,33 @@ module Tugboat
|
|
3
3
|
class ListImages < Base
|
4
4
|
def call(env)
|
5
5
|
ocean = env['barge']
|
6
|
-
my_images = ocean.image.all(:
|
6
|
+
my_images = ocean.image.all(private: true)
|
7
7
|
public_images = ocean.image.all.images - my_images.images
|
8
8
|
|
9
9
|
if env['user_show_just_private_images']
|
10
|
-
say
|
11
|
-
say
|
10
|
+
say 'Showing just private images', :green
|
11
|
+
say 'Private Images:', :blue
|
12
12
|
my_images_list = my_images.images
|
13
13
|
if my_images_list.nil? || my_images_list.empty?
|
14
|
-
say
|
14
|
+
say 'No private images found'
|
15
15
|
else
|
16
16
|
my_images_list.each do |image|
|
17
17
|
say "#{image.name} (id: #{image.id}, distro: #{image.distribution})"
|
18
18
|
end
|
19
19
|
end
|
20
20
|
else
|
21
|
-
say
|
22
|
-
say
|
21
|
+
say 'Showing both private and public images'
|
22
|
+
say 'Private Images:', :blue
|
23
23
|
my_images_list = my_images.images
|
24
24
|
if my_images_list.nil? || my_images_list.empty?
|
25
|
-
say
|
25
|
+
say 'No private images found'
|
26
26
|
else
|
27
27
|
my_images_list.each do |image|
|
28
28
|
say "#{image.name} (id: #{image.id}, distro: #{image.distribution})"
|
29
29
|
end
|
30
30
|
end
|
31
31
|
say ''
|
32
|
-
say
|
32
|
+
say 'Public Images:', :blue
|
33
33
|
public_images.each do |image|
|
34
34
|
say "#{image.name} (slug: #{image.slug}, id: #{image.id}, distro: #{image.distribution})"
|
35
35
|
end
|
@@ -39,4 +39,4 @@ module Tugboat
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
end
|
42
|
+
end
|
@@ -2,11 +2,10 @@ module Tugboat
|
|
2
2
|
module Middleware
|
3
3
|
class ListSSHKeys < Base
|
4
4
|
def call(env)
|
5
|
-
|
6
5
|
ocean = env['barge']
|
7
6
|
ssh_keys = ocean.key.all.ssh_keys
|
8
7
|
|
9
|
-
say
|
8
|
+
say 'SSH Keys:'
|
10
9
|
ssh_keys.each do |key|
|
11
10
|
say "Name: #{key.name}, (id: #{key.id}), fingerprint: #{key.fingerprint}"
|
12
11
|
end
|
@@ -16,4 +15,3 @@ module Tugboat
|
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
19
|
-
|
@@ -4,15 +4,15 @@ module Tugboat
|
|
4
4
|
def call(env)
|
5
5
|
ocean = env['barge']
|
6
6
|
|
7
|
-
say "Queuing password reset for #{env[
|
8
|
-
response = ocean.droplet.password_reset env[
|
7
|
+
say "Queuing password reset for #{env['droplet_id']} #{env['droplet_name']}...", nil, false
|
8
|
+
response = ocean.droplet.password_reset env['droplet_id']
|
9
9
|
|
10
|
-
|
10
|
+
if response.success?
|
11
|
+
say 'Password reset successful!', :green
|
12
|
+
say 'Your new root password will be emailed to you', :green
|
13
|
+
else
|
11
14
|
say "Failed to reset password on Droplet: #{response.message}", :red
|
12
15
|
exit 1
|
13
|
-
else
|
14
|
-
say "Password reset successful!", :green
|
15
|
-
say "Your new root password will be emailed to you", :green
|
16
16
|
end
|
17
17
|
|
18
18
|
@app.call(env)
|
@@ -20,4 +20,3 @@ module Tugboat
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|