tugboat 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +78 -1
- data/lib/tugboat/cli.rb +98 -1
- data/lib/tugboat/middleware.rb +35 -0
- data/lib/tugboat/middleware/backup_setting.rb +30 -0
- data/lib/tugboat/middleware/base.rb +64 -0
- data/lib/tugboat/middleware/list_droplets.rb +1 -14
- data/lib/tugboat/middleware/list_snapshots.rb +28 -0
- data/lib/tugboat/middleware/scp_droplet.rb +34 -0
- data/lib/tugboat/version.rb +1 -1
- data/spec/cli/backup_setting_spec.rb +69 -0
- data/spec/cli/droplets_cli_spec.rb +148 -0
- data/spec/cli/scp_cli_spec.rb +75 -0
- data/spec/cli/snapshots_cli_spec.rb +46 -0
- data/spec/fixtures/disable_backups_response.json +27 -0
- data/spec/fixtures/enable_backups_response.json +27 -0
- data/spec/fixtures/show_snapshots.json +34 -0
- data/spec/fixtures/show_snapshots_empty.json +7 -0
- metadata +21 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a56c6cb7473968939a5e1a56012640156bc284a
|
4
|
+
data.tar.gz: 6283168b5d267c420c74e57c416ac3be8839b8d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 507273ace57c2dc9f5a9f948d1da9cdd5cb06adb3476ad945437fcf8311c0bbcf17deed1d7b7fe3141f520163c329cfeaa63174631ba58250dcb05136ecd97bc
|
7
|
+
data.tar.gz: cb40271dba29c11d8e3bcec36de00450bb4bf866edfb79b9ca0b0a1809ff52949737e4e4e37b1688a95ad2c22f427b16364f2745a6c81761d71752ca042751ee
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,23 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
+
## [v4.1.0](https://github.com/petems/tugboat/tree/v4.1.0) (2018-03-11)
|
6
|
+
[Full Changelog](https://github.com/petems/tugboat/compare/v4.0.0...v4.1.0)
|
7
|
+
|
8
|
+
**Implemented enhancements:**
|
9
|
+
|
10
|
+
- Commands like "droplets" need formatting switches [\#206](https://github.com/petems/tugboat/issues/206)
|
11
|
+
- Add a scp command [\#120](https://github.com/petems/tugboat/issues/120)
|
12
|
+
- Turn Backups on/off [\#51](https://github.com/petems/tugboat/issues/51)
|
13
|
+
|
14
|
+
**Merged pull requests:**
|
15
|
+
|
16
|
+
- Update README typo. [\#298](https://github.com/petems/tugboat/pull/298) ([simi](https://github.com/simi))
|
17
|
+
- Add ability to list snapshots [\#296](https://github.com/petems/tugboat/pull/296) ([petems](https://github.com/petems))
|
18
|
+
- Add backup\_config setting [\#295](https://github.com/petems/tugboat/pull/295) ([petems](https://github.com/petems))
|
19
|
+
- Adds porcelain option to droplets command [\#294](https://github.com/petems/tugboat/pull/294) ([petems](https://github.com/petems))
|
20
|
+
- Adds new SCP feature [\#291](https://github.com/petems/tugboat/pull/291) ([petems](https://github.com/petems))
|
21
|
+
|
5
22
|
## [v4.0.0](https://github.com/petems/tugboat/tree/v4.0.0) (2017-12-03)
|
6
23
|
[Full Changelog](https://github.com/petems/tugboat/compare/v3.1.0...v4.0.0)
|
7
24
|
|
@@ -13,6 +30,7 @@ All notable changes to this project will be documented in this file.
|
|
13
30
|
|
14
31
|
**Merged pull requests:**
|
15
32
|
|
33
|
+
- Bump Version to 4.0.0 [\#293](https://github.com/petems/tugboat/pull/293) ([petems](https://github.com/petems))
|
16
34
|
- Removing `Assigned but unused variable` warnings [\#292](https://github.com/petems/tugboat/pull/292) ([petems](https://github.com/petems))
|
17
35
|
- Adds helper for showing snapshot parameter [\#290](https://github.com/petems/tugboat/pull/290) ([petems](https://github.com/petems))
|
18
36
|
- Updates Faraday middleware gem [\#289](https://github.com/petems/tugboat/pull/289) ([petems](https://github.com/petems))
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ A command line tool for interacting with your [DigitalOcean](https://www.digital
|
|
6
6
|
|
7
7
|
## History
|
8
8
|
|
9
|
-
When Tugboat was created, DigitalOcean was an extremely new cloud provider. They'd only released their public beta back in [2012](https://whoapi.com/blog/1497/fast-growing-digitalocean-is-fueled-by-customer-love/), and their new SSD backed machines only
|
9
|
+
When Tugboat was created, DigitalOcean was an extremely new cloud provider. They'd only released their public beta back in [2012](https://whoapi.com/blog/1497/fast-growing-digitalocean-is-fueled-by-customer-love/), and their new SSD backed machines only premiered in early [2013](https://techcrunch.com/2013/01/15/techstars-graduate-digitalocean-switches-to-ssd-for-its-5-per-month-vps-to-take-on-linode-and-rackspace/).
|
10
10
|
|
11
11
|
Tugboat started out life around that time, [back in April 2013](https://github.com/pearkes/tugboat/commit/f0fbc1f438cce81c286f0e60014dc4393ac95cb6). Back then, there were no official libraries for DigitalOcean, and the 1.0 API was a bit unstable and occasionally flakey.
|
12
12
|
|
@@ -81,6 +81,44 @@ defaults:
|
|
81
81
|
pearkes-admin-001 (ip: 30.30.30.3, status: active, region: nyc2, id: 13231512)
|
82
82
|
pearkes-api-001 (ip: 30.30.30.5, status: active, region: nyc2, id: 13231513)
|
83
83
|
|
84
|
+
If you wish to use the droplet listing as part of scripting or munging output, you can use the `--porcelain`:
|
85
|
+
|
86
|
+
$ tugboat droplets --attribute=ip4
|
87
|
+
pearkes-web-001,30.30.30.1
|
88
|
+
pearkes-admin-001,30.30.30.3
|
89
|
+
pearkes-api-001,30.30.30.5
|
90
|
+
|
91
|
+
Or `--attribute` parameter:
|
92
|
+
|
93
|
+
$ tugboat droplets --porcelain
|
94
|
+
name pearkes-web-001
|
95
|
+
id 13231515
|
96
|
+
status active
|
97
|
+
ip4 330.30.30.1
|
98
|
+
region lon1
|
99
|
+
image 6918990
|
100
|
+
size 1gb
|
101
|
+
backups_active false
|
102
|
+
|
103
|
+
name pearkes-admin-001
|
104
|
+
id 13231513
|
105
|
+
status active
|
106
|
+
ip4 30.30.30.3
|
107
|
+
region lon1
|
108
|
+
image 6918990
|
109
|
+
size 1gb
|
110
|
+
backups_active false
|
111
|
+
|
112
|
+
name pearkes-web-001
|
113
|
+
id 13231514
|
114
|
+
status active
|
115
|
+
ip4 30.30.30.5
|
116
|
+
region lon1
|
117
|
+
image 6918990
|
118
|
+
size 1gb
|
119
|
+
backups_active true
|
120
|
+
|
121
|
+
|
84
122
|
### Fuzzy name matching
|
85
123
|
|
86
124
|
You can pass a unique fragment of a droplets name for interactions
|
@@ -116,6 +154,21 @@ match.
|
|
116
154
|
Welcome to Ubuntu 12.10 (GNU/Linux 3.5.0-17-generic x86_64)
|
117
155
|
pearkes@pearkes-admin-001:~#
|
118
156
|
|
157
|
+
### SCP files to droplet
|
158
|
+
|
159
|
+
*You can configure an SSH username and key path in `tugboat authorize`,
|
160
|
+
or by changing your `~/.tugboat`.*
|
161
|
+
|
162
|
+
This lets you scp a file into a droplet by providing it's name, or a partial
|
163
|
+
match.
|
164
|
+
|
165
|
+
$ tugboat scp test-scp /tmp/foo /tmp/bar
|
166
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 72025053 (test-scp)
|
167
|
+
Executing SCP on Droplet (test-scp)...
|
168
|
+
Attempting SCP with `scp -i /Users/petems/.ssh/digital_ocean /tmp/foo root@132.61.164.113:/tmp/bar`
|
169
|
+
foo
|
170
|
+
100% 0 0.0KB/s 00:00
|
171
|
+
|
119
172
|
### Create a droplet
|
120
173
|
|
121
174
|
$ tugboat create pearkes-www-002 -s 512mb -i ubuntu-12-04-x64 -r nyc2 -k 11251
|
@@ -183,6 +236,18 @@ Print a single attribute.
|
|
183
236
|
$ tugboat resize admin -s 66
|
184
237
|
Queuing resize for 13231512 (pearkes-admin-001)...done
|
185
238
|
|
239
|
+
### Enabling backups on a droplet
|
240
|
+
|
241
|
+
$ tugboat backup_config admin --on
|
242
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
243
|
+
Backup action enable backups is complete
|
244
|
+
|
245
|
+
### Disabling backups on a droplet
|
246
|
+
|
247
|
+
$ tugboat backup_config admin --off
|
248
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
249
|
+
Backup action disable backups is complete
|
250
|
+
|
186
251
|
### List Available Images
|
187
252
|
|
188
253
|
You can list all images
|
@@ -206,6 +271,18 @@ Or just list images that you have created.
|
|
206
271
|
My application image (id: 6376601, distro: Ubuntu)
|
207
272
|
....
|
208
273
|
|
274
|
+
### List Current Snapshots
|
275
|
+
|
276
|
+
$ tugboat snapshots
|
277
|
+
code-freeze-backup-october (id: 2013184, resource_type: droplet, created_at: 2016-10-06T11:43:06Z)
|
278
|
+
test-admin 2017-05-31 (id: 20234485, resource_type: droplet, created_at: 2017-05-31T02:07:07Z)
|
279
|
+
test-admin 2017-11-08 (id: 21133567, resource_type: droplet, created_at: 2017-11-08T02:49:09Z)
|
280
|
+
test-admin 2017-11-15 (id: 22355454, resource_type: droplet, created_at: 2017-11-15T03:11:08Z)
|
281
|
+
test-admin 2017-11-22 (id: 24523423, resource_type: droplet, created_at: 2017-11-22T03:10:09Z)
|
282
|
+
test-admin 2017-11-29 (id: 26212345, resource_type: droplet, created_at: 2017-11-29T03:15:25Z)
|
283
|
+
....
|
284
|
+
|
285
|
+
|
209
286
|
### List Available Sizes
|
210
287
|
|
211
288
|
$ tugboat sizes
|
data/lib/tugboat/cli.rb
CHANGED
@@ -37,6 +37,33 @@ module Tugboat
|
|
37
37
|
'user_quiet' => options[:quiet])
|
38
38
|
end
|
39
39
|
|
40
|
+
desc 'backup_config FUZZY_NAME', 'Manage backups on a droplet.'
|
41
|
+
method_option 'id',
|
42
|
+
type: :string,
|
43
|
+
aliases: '-i',
|
44
|
+
desc: 'The ID of the droplet.'
|
45
|
+
method_option 'name',
|
46
|
+
type: :string,
|
47
|
+
aliases: '-n',
|
48
|
+
desc: 'The exact name of the droplet'
|
49
|
+
method_option 'enable',
|
50
|
+
type: :boolean,
|
51
|
+
aliases: '--on',
|
52
|
+
desc: 'Enable backups on the droplet.'
|
53
|
+
method_option 'disable',
|
54
|
+
type: :boolean,
|
55
|
+
aliases: '--off',
|
56
|
+
desc: 'Disable backups on the droplet.'
|
57
|
+
def backup_config(name = nil, status = nil)
|
58
|
+
Middleware.sequence_backup_config.call('tugboat_action' => __method__,
|
59
|
+
'user_droplet_id' => options[:id],
|
60
|
+
'user_droplet_name' => options[:name],
|
61
|
+
'user_droplet_fuzzy_name' => name,
|
62
|
+
'enable' => options[:enable],
|
63
|
+
'disable' => options[:disable],
|
64
|
+
'user_quiet' => options[:quiet])
|
65
|
+
end
|
66
|
+
|
40
67
|
desc 'config', 'Show your current config information'
|
41
68
|
long_desc "This shows the current information in the .tugboat config
|
42
69
|
being used by the app
|
@@ -71,12 +98,26 @@ module Tugboat
|
|
71
98
|
default: 20,
|
72
99
|
aliases: '-p',
|
73
100
|
desc: 'Chose how many results to fetch from the DigitalOcean API (larger is slower)'
|
101
|
+
method_option 'attribute',
|
102
|
+
type: :string,
|
103
|
+
aliases: '-a',
|
104
|
+
desc: 'The name of the attribute to print.'
|
105
|
+
method_option 'porcelain',
|
106
|
+
type: :boolean,
|
107
|
+
desc: 'Give the output in an easy-to-parse format for scripts.'
|
108
|
+
method_option 'include_name',
|
109
|
+
type: :boolean,
|
110
|
+
default: true,
|
111
|
+
desc: 'Include the name when listing attributes from droplets.'
|
74
112
|
desc 'droplets [OPTIONS]', 'Retrieve a list of your droplets'
|
75
113
|
def droplets
|
76
114
|
Middleware.sequence_list_droplets.call('tugboat_action' => __method__,
|
77
115
|
'user_quiet' => options[:quiet],
|
78
116
|
'include_urls' => options['include_urls'],
|
79
|
-
'per_page' => options['per_page'],
|
117
|
+
'per_page' => options['per_page'],
|
118
|
+
'attribute' => options[:attribute],
|
119
|
+
'porcelain' => options[:porcelain],
|
120
|
+
'include_name' => options[:include_name])
|
80
121
|
end
|
81
122
|
|
82
123
|
desc 'images [OPTIONS]', 'Retrieve a list of images'
|
@@ -139,6 +180,48 @@ module Tugboat
|
|
139
180
|
'user_quiet' => options[:quiet])
|
140
181
|
end
|
141
182
|
|
183
|
+
desc 'scp FUZZY_NAME FROM_PATH TO_PATH', 'scp files to a droplet'
|
184
|
+
method_option 'id',
|
185
|
+
type: :string,
|
186
|
+
aliases: '-i',
|
187
|
+
desc: 'The ID of the droplet.'
|
188
|
+
method_option 'from_path',
|
189
|
+
type: :string,
|
190
|
+
aliases: '-from',
|
191
|
+
desc: 'The path of the local file'
|
192
|
+
method_option 'to_path',
|
193
|
+
type: :string,
|
194
|
+
aliases: '-to',
|
195
|
+
desc: 'The path to copy to on the remote droplet'
|
196
|
+
method_option 'name',
|
197
|
+
type: :string,
|
198
|
+
aliases: '-n',
|
199
|
+
desc: 'The exact name of the droplet'
|
200
|
+
method_option 'ssh_user',
|
201
|
+
type: :string,
|
202
|
+
aliases: '-u',
|
203
|
+
desc: 'Specifies which user to log in as'
|
204
|
+
method_option 'scp_command',
|
205
|
+
type: :string,
|
206
|
+
aliases: ['-c'],
|
207
|
+
desc: 'Command to run to copy the file, eg scp, rsync (defaults to scp)'
|
208
|
+
method_option 'wait',
|
209
|
+
type: :boolean,
|
210
|
+
aliases: '-w',
|
211
|
+
desc: 'Wait for droplet to become active before trying to SSH'
|
212
|
+
def scp(name = nil, from_path, to_path)
|
213
|
+
Middleware.sequence_scp_droplet.call('tugboat_action' => __method__,
|
214
|
+
'user_droplet_id' => options[:id],
|
215
|
+
'user_droplet_name' => options[:name],
|
216
|
+
'user_droplet_fuzzy_name' => name,
|
217
|
+
'user_from_file' => from_path,
|
218
|
+
'user_to_file' => to_path,
|
219
|
+
'user_droplet_ssh_user' => options[:ssh_user],
|
220
|
+
'user_scp_command' => options[:scp_command],
|
221
|
+
'user_droplet_ssh_wait' => options[:wait],
|
222
|
+
'user_quiet' => options[:quiet])
|
223
|
+
end
|
224
|
+
|
142
225
|
desc 'create NAME', 'Create a droplet.'
|
143
226
|
method_option 'size',
|
144
227
|
type: :string,
|
@@ -456,6 +539,20 @@ module Tugboat
|
|
456
539
|
'user_quiet' => options[:quiet])
|
457
540
|
end
|
458
541
|
|
542
|
+
method_option 'per_page',
|
543
|
+
type: :boolean,
|
544
|
+
default: 20,
|
545
|
+
aliases: '-p',
|
546
|
+
desc: 'Chose how many results to fetch from the DigitalOcean API (larger is slower)'
|
547
|
+
desc 'snapshots [OPTIONS]', 'Retrieve a list of your snapshots'
|
548
|
+
def snapshots
|
549
|
+
Middleware.sequence_list_snapshots.call(
|
550
|
+
'tugboat_action' => __method__,
|
551
|
+
'user_quiet' => options[:quiet],
|
552
|
+
'per_page' => options['per_page'],
|
553
|
+
)
|
554
|
+
end
|
555
|
+
|
459
556
|
desc 'password-reset FUZZY_NAME', 'Reset root password'
|
460
557
|
method_option 'id',
|
461
558
|
type: :numeric,
|
data/lib/tugboat/middleware.rb
CHANGED
@@ -5,6 +5,7 @@ module Tugboat
|
|
5
5
|
autoload :AddKey, 'tugboat/middleware/add_key'
|
6
6
|
autoload :AskForCredentials, 'tugboat/middleware/ask_for_credentials'
|
7
7
|
autoload :Base, 'tugboat/middleware/base'
|
8
|
+
autoload :BackupConfig, 'tugboat/middleware/backup_setting'
|
8
9
|
autoload :CheckConfiguration, 'tugboat/middleware/check_configuration'
|
9
10
|
autoload :CheckCredentials, 'tugboat/middleware/check_credentials'
|
10
11
|
autoload :CheckDropletActive, 'tugboat/middleware/check_droplet_active'
|
@@ -27,12 +28,14 @@ module Tugboat
|
|
27
28
|
autoload :ListImages, 'tugboat/middleware/list_images'
|
28
29
|
autoload :ListRegions, 'tugboat/middleware/list_regions'
|
29
30
|
autoload :ListSizes, 'tugboat/middleware/list_sizes'
|
31
|
+
autoload :ListSnapshots, 'tugboat/middleware/list_snapshots'
|
30
32
|
autoload :ListSSHKeys, 'tugboat/middleware/list_ssh_keys'
|
31
33
|
autoload :PasswordReset, 'tugboat/middleware/password_reset'
|
32
34
|
autoload :ResizeDroplet, 'tugboat/middleware/resize_droplet'
|
33
35
|
autoload :RestartDroplet, 'tugboat/middleware/restart_droplet'
|
34
36
|
autoload :SnapshotDroplet, 'tugboat/middleware/snapshot_droplet'
|
35
37
|
autoload :SSHDroplet, 'tugboat/middleware/ssh_droplet'
|
38
|
+
autoload :SCPDroplet, 'tugboat/middleware/scp_droplet'
|
36
39
|
autoload :StartDroplet, 'tugboat/middleware/start_droplet'
|
37
40
|
autoload :WaitForState, 'tugboat/middleware/wait_for_state'
|
38
41
|
|
@@ -49,6 +52,16 @@ module Tugboat
|
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
55
|
+
def self.sequence_backup_config
|
56
|
+
::Middleware::Builder.new do
|
57
|
+
use InjectConfiguration
|
58
|
+
use CheckConfiguration
|
59
|
+
use InjectClient
|
60
|
+
use FindDroplet
|
61
|
+
use BackupConfig
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
52
65
|
# This checks that the credentials in ~/.tugboat are valid
|
53
66
|
def self.sequence_verify
|
54
67
|
::Middleware::Builder.new do
|
@@ -148,6 +161,18 @@ module Tugboat
|
|
148
161
|
end
|
149
162
|
end
|
150
163
|
|
164
|
+
# SSH into a droplet
|
165
|
+
def self.sequence_scp_droplet
|
166
|
+
::Middleware::Builder.new do
|
167
|
+
use InjectConfiguration
|
168
|
+
use CheckConfiguration
|
169
|
+
use InjectClient
|
170
|
+
use FindDroplet
|
171
|
+
use CheckDropletActive
|
172
|
+
use SCPDroplet
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
151
176
|
# Create a droplet
|
152
177
|
def self.sequence_create_droplet
|
153
178
|
::Middleware::Builder.new do
|
@@ -218,6 +243,16 @@ module Tugboat
|
|
218
243
|
end
|
219
244
|
end
|
220
245
|
|
246
|
+
# Display a list of available SSH keys
|
247
|
+
def self.sequence_list_snapshots
|
248
|
+
::Middleware::Builder.new do
|
249
|
+
use InjectConfiguration
|
250
|
+
use CheckConfiguration
|
251
|
+
use InjectClient
|
252
|
+
use ListSnapshots
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
221
256
|
# Create a droplet
|
222
257
|
def self.sequence_add_key
|
223
258
|
::Middleware::Builder.new do
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class BackupConfig < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env['droplet_kit']
|
6
|
+
|
7
|
+
if env['disable'] && env['enable']
|
8
|
+
say 'You cannot use both --disable and --enable for backup_config', :red
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
if env['disable']
|
14
|
+
response = ocean.droplet_actions.disable_backups(droplet_id: env['droplet_id'])
|
15
|
+
end
|
16
|
+
if env['enable']
|
17
|
+
response = ocean.droplet_actions.enable_backups(droplet_id: env['droplet_id'])
|
18
|
+
end
|
19
|
+
rescue DropletKit::Error => e
|
20
|
+
say "Failed to configure backups on droplet. Reason given from API: #{e}", :red
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
say "Backup action #{response_stringify(response)} is #{response.status}"
|
25
|
+
|
26
|
+
@app.call(env)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -25,6 +25,10 @@ module Tugboat
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
def response_stringify(response)
|
29
|
+
response.type.gsub(/_/,' ')
|
30
|
+
end
|
31
|
+
|
28
32
|
def call(env)
|
29
33
|
@app.call(env)
|
30
34
|
end
|
@@ -90,6 +94,66 @@ module Tugboat
|
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
97
|
+
def print_droplet_info(droplet, attribute, porcelain, include_urls, include_name)
|
98
|
+
droplet_ip4_public = droplet.networks.v4.find { |address| address.type == 'public' }.ip_address unless droplet.networks.v4.empty?
|
99
|
+
droplet_ip6_public = droplet.networks.v6.find { |address| address.type == 'public' }.ip_address unless droplet.networks.v6.empty?
|
100
|
+
check_private_ip = droplet.networks.v4.find { |address| address.type == 'private' }
|
101
|
+
droplet_private_ip = check_private_ip.ip_address if check_private_ip
|
102
|
+
|
103
|
+
attributes_list = [
|
104
|
+
['name', droplet.name],
|
105
|
+
['id', droplet.id],
|
106
|
+
['status', droplet.status],
|
107
|
+
['ip4', droplet_ip4_public],
|
108
|
+
['ip6', droplet_ip6_public],
|
109
|
+
['private_ip', droplet_private_ip],
|
110
|
+
['region', droplet.region.slug],
|
111
|
+
['image', droplet.image.id],
|
112
|
+
['size', droplet.size_slug],
|
113
|
+
['backups_active', !droplet.backup_ids.empty?]
|
114
|
+
]
|
115
|
+
attributes = Hash[*attributes_list.flatten(1)]
|
116
|
+
|
117
|
+
if attribute
|
118
|
+
if attributes.key? attribute
|
119
|
+
if include_name
|
120
|
+
say "#{attributes['name']},#{attributes[attribute]}"
|
121
|
+
else
|
122
|
+
say attributes[attribute]
|
123
|
+
end
|
124
|
+
else
|
125
|
+
say "Invalid attribute \"#{attribute}\"", :red
|
126
|
+
say 'Provide one of the following:', :red
|
127
|
+
attributes_list.each { |a| say " #{a[0]}", :red }
|
128
|
+
exit 1
|
129
|
+
end
|
130
|
+
else
|
131
|
+
if porcelain
|
132
|
+
attributes_list.select { |a| !a[1].nil? }.each { |a| say "#{a[0]} #{a[1]}" }
|
133
|
+
say ""
|
134
|
+
else
|
135
|
+
print_droplet_info_full(droplet, include_urls)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def print_droplet_info_full(droplet, include_urls)
|
141
|
+
private_addr = droplet.networks.v4.find { |address| address.type == 'private' }
|
142
|
+
if private_addr
|
143
|
+
private_ip = ", private_ip: #{private_addr.ip_address}"
|
144
|
+
end
|
145
|
+
|
146
|
+
status_color = if droplet.status == 'active'
|
147
|
+
GREEN
|
148
|
+
else
|
149
|
+
RED
|
150
|
+
end
|
151
|
+
|
152
|
+
public_addr = droplet.networks.v4.find { |address| address.type == 'public' }
|
153
|
+
|
154
|
+
say "#{droplet.name} (ip: #{public_addr.ip_address}#{private_ip}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region.slug}, size: #{droplet.size_slug}, id: #{droplet.id}#{include_urls ? droplet_id_to_url(droplet.id) : ''})"
|
155
|
+
end
|
156
|
+
|
93
157
|
# Get all pages of droplets
|
94
158
|
def get_droplet_list(ocean, per_page = 20)
|
95
159
|
verify_credentials(ocean)
|
@@ -14,20 +14,7 @@ module Tugboat
|
|
14
14
|
droplet_list.each do |droplet|
|
15
15
|
has_one = true
|
16
16
|
|
17
|
-
|
18
|
-
if private_addr
|
19
|
-
private_ip = ", private_ip: #{private_addr.ip_address}"
|
20
|
-
end
|
21
|
-
|
22
|
-
status_color = if droplet.status == 'active'
|
23
|
-
GREEN
|
24
|
-
else
|
25
|
-
RED
|
26
|
-
end
|
27
|
-
|
28
|
-
public_addr = droplet.networks.v4.find { |address| address.type == 'public' }
|
29
|
-
|
30
|
-
say "#{droplet.name} (ip: #{public_addr.ip_address}#{private_ip}, status: #{status_color}#{droplet.status}#{CLEAR}, region: #{droplet.region.slug}, size: #{droplet.size_slug}, id: #{droplet.id}#{env['include_urls'] ? droplet_id_to_url(droplet.id) : ''})"
|
17
|
+
print_droplet_info(droplet, env['attribute'], env['porcelain'], env['include_urls'], env['include_name'])
|
31
18
|
end
|
32
19
|
|
33
20
|
unless has_one
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the client has set-up configuration yet.
|
4
|
+
class ListSnapshots < Base
|
5
|
+
def call(env)
|
6
|
+
ocean = env['droplet_kit']
|
7
|
+
|
8
|
+
verify_credentials(ocean)
|
9
|
+
|
10
|
+
response = ocean.snapshots.all(per_page: env['per_page'])
|
11
|
+
|
12
|
+
has_one = false
|
13
|
+
|
14
|
+
response.each do |snapshot|
|
15
|
+
has_one = true
|
16
|
+
|
17
|
+
say "#{snapshot.name} (id: #{snapshot.id}, resource_type: #{snapshot.resource_type}, created_at: #{snapshot.created_at})"
|
18
|
+
end
|
19
|
+
|
20
|
+
unless has_one
|
21
|
+
say "You don't appear to have any snapshots.", :red
|
22
|
+
end
|
23
|
+
|
24
|
+
@app.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class SCPDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
say "Executing SCP on Droplet #{env['droplet_name']}..."
|
6
|
+
|
7
|
+
identity = File.expand_path(env['config'].ssh_key_path.to_s).strip
|
8
|
+
|
9
|
+
ssh_user = env['user_droplet_ssh_user'] || env['config'].ssh_user
|
10
|
+
|
11
|
+
scp_command = env['user_scp_command'] || 'scp'
|
12
|
+
|
13
|
+
host_ip = env['droplet_ip']
|
14
|
+
|
15
|
+
host_string = "#{ssh_user}@#{host_ip}"
|
16
|
+
|
17
|
+
if env['user_droplet_ssh_wait']
|
18
|
+
say 'Wait flag given, waiting for droplet to become active'
|
19
|
+
wait_for_state(env['droplet_id'], 'active', env['barge'])
|
20
|
+
end
|
21
|
+
|
22
|
+
identity_string = "-i #{identity}"
|
23
|
+
|
24
|
+
scp_command_string = [scp_command, identity_string, env['user_from_file'], "#{host_string}:#{env['user_to_file']}"].join(' ')
|
25
|
+
|
26
|
+
say "Attempting SCP with `#{scp_command_string}`"
|
27
|
+
|
28
|
+
Kernel.exec(scp_command_string)
|
29
|
+
|
30
|
+
@app.call(env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/tugboat/version.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tugboat::CLI do
|
4
|
+
include_context 'spec'
|
5
|
+
|
6
|
+
describe 'backup_config' do
|
7
|
+
it 'enables backups on a droplet with the enable flag' do
|
8
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
9
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
10
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
11
|
+
|
12
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=200').
|
13
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
14
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
15
|
+
|
16
|
+
stub_request(:post, 'https://api.digitalocean.com/v2/droplets/6918990/actions').
|
17
|
+
with(body: '{"type":"enable_backups"}',
|
18
|
+
headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
19
|
+
to_return(status: 200, body: fixture('enable_backups_response'), headers: {})
|
20
|
+
|
21
|
+
cli.options = cli.options.merge(enable: true)
|
22
|
+
|
23
|
+
expected_string = <<-eos
|
24
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
25
|
+
Backup action enable backups is in-progress
|
26
|
+
eos
|
27
|
+
|
28
|
+
expect { cli.backup_config('example.com') }.to output(expected_string).to_stdout
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'enables backups on a droplet with the disable flag' do
|
32
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
33
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
34
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
35
|
+
|
36
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=200').
|
37
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
38
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
39
|
+
|
40
|
+
stub_request(:post, 'https://api.digitalocean.com/v2/droplets/6918990/actions').
|
41
|
+
with(body: '{"type":"disable_backups"}',
|
42
|
+
headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
43
|
+
to_return(status: 200, body: fixture('disable_backups_response'), headers: {})
|
44
|
+
|
45
|
+
cli.options = cli.options.merge(disable: true)
|
46
|
+
|
47
|
+
expected_string = <<-eos
|
48
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
49
|
+
Backup action disable backups is in-progress
|
50
|
+
eos
|
51
|
+
|
52
|
+
expect { cli.backup_config('example.com') }.to output(expected_string).to_stdout
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'shows error if both enable and disable given' do
|
56
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
57
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
58
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
59
|
+
|
60
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=200').
|
61
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
62
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
63
|
+
|
64
|
+
cli.options = cli.options.merge(disable: true, enable: true)
|
65
|
+
|
66
|
+
expect { cli.backup_config('example.com') }.to raise_error(SystemExit).and output(%r{You cannot use both --disable and --enable for backup_config}).to_stdout
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -137,6 +137,154 @@ page2example3.com (ip: 104.236.32.173, status: \e[31moff\e[0m, region: nyc3, siz
|
|
137
137
|
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=2&per_page=3')).to have_been_made
|
138
138
|
end
|
139
139
|
|
140
|
+
it 'gives porcelain output when set' do
|
141
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
142
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
143
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
144
|
+
|
145
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
|
146
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
147
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })
|
148
|
+
|
149
|
+
cli.options = cli.options.merge('porcelain' => true)
|
150
|
+
|
151
|
+
expected_string = <<-eos
|
152
|
+
name example.com
|
153
|
+
id 6918990
|
154
|
+
status active
|
155
|
+
ip4 104.236.32.182
|
156
|
+
ip6 2604:A880:0800:0010:0000:0000:02DD:4001
|
157
|
+
region nyc3
|
158
|
+
image 6918990
|
159
|
+
size 512mb
|
160
|
+
backups_active true
|
161
|
+
|
162
|
+
name example2.com
|
163
|
+
id 3164956
|
164
|
+
status active
|
165
|
+
ip4 104.236.32.172
|
166
|
+
ip6 2604:A880:0800:0010:0000:0000:02DD:4001
|
167
|
+
region nyc3
|
168
|
+
image 6918990
|
169
|
+
size 512mb
|
170
|
+
backups_active true
|
171
|
+
|
172
|
+
name example3.com
|
173
|
+
id 3164444
|
174
|
+
status off
|
175
|
+
ip4 104.236.32.173
|
176
|
+
ip6 2604:A880:0800:0010:0000:0000:02DD:4001
|
177
|
+
region nyc3
|
178
|
+
image 6918990
|
179
|
+
size 512mb
|
180
|
+
backups_active true
|
181
|
+
|
182
|
+
eos
|
183
|
+
|
184
|
+
expect { cli.droplets }.to output(expected_string).to_stdout
|
185
|
+
|
186
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
|
187
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'gives ipv4 attribute output when set' do
|
191
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
192
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
193
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
194
|
+
|
195
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
|
196
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
197
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })
|
198
|
+
|
199
|
+
cli.options = cli.options.merge('attribute' => 'ip4', 'include_name' => true)
|
200
|
+
|
201
|
+
expected_string = <<-eos
|
202
|
+
example.com,104.236.32.182
|
203
|
+
example2.com,104.236.32.172
|
204
|
+
example3.com,104.236.32.173
|
205
|
+
eos
|
206
|
+
|
207
|
+
expect { cli.droplets }.to output(expected_string).to_stdout
|
208
|
+
|
209
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
|
210
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'gives status attribute output when set' do
|
214
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
215
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
216
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
217
|
+
|
218
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
|
219
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
220
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })
|
221
|
+
|
222
|
+
cli.options = cli.options.merge('attribute' => 'status', 'include_name' => true)
|
223
|
+
|
224
|
+
expected_string = <<-eos
|
225
|
+
example.com,active
|
226
|
+
example2.com,active
|
227
|
+
example3.com,off
|
228
|
+
eos
|
229
|
+
|
230
|
+
expect { cli.droplets }.to output(expected_string).to_stdout
|
231
|
+
|
232
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
|
233
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'gives only ip4 attribute output when set and include_name set to false' do
|
237
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
238
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
239
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
240
|
+
|
241
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
|
242
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
243
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })
|
244
|
+
|
245
|
+
cli.options = cli.options.merge('attribute' => 'ip4', 'include_name' => false)
|
246
|
+
|
247
|
+
expected_string = <<-eos
|
248
|
+
104.236.32.182
|
249
|
+
104.236.32.172
|
250
|
+
104.236.32.173
|
251
|
+
eos
|
252
|
+
|
253
|
+
expect { cli.droplets }.to output(expected_string).to_stdout
|
254
|
+
|
255
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.twice
|
256
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20')).to have_been_made
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'shows error if attribute asked for does not exist' do
|
260
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
261
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
262
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
263
|
+
|
264
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=20').
|
265
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
266
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: { 'Content-Type' => 'application/json' })
|
267
|
+
|
268
|
+
cli.options = cli.options.merge('attribute' => 'foo')
|
269
|
+
|
270
|
+
expected_string = <<-eos
|
271
|
+
Invalid attribute \"foo\"
|
272
|
+
Provide one of the following:
|
273
|
+
name
|
274
|
+
id
|
275
|
+
status
|
276
|
+
ip4
|
277
|
+
ip6
|
278
|
+
private_ip
|
279
|
+
region
|
280
|
+
image
|
281
|
+
size
|
282
|
+
backups_active
|
283
|
+
eos
|
284
|
+
|
285
|
+
expect { cli.droplets }.to raise_error(SystemExit).and output(expected_string).to_stdout
|
286
|
+
end
|
287
|
+
|
140
288
|
it 'shows error on failure in initial stage' do
|
141
289
|
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
142
290
|
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tugboat::CLI do
|
4
|
+
include_context 'spec'
|
5
|
+
|
6
|
+
describe 'scp' do
|
7
|
+
it "tries to fetch the droplet's IP from the API" do
|
8
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
9
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
10
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
11
|
+
|
12
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=200').
|
13
|
+
to_return(headers: { 'Content-Type' => 'application/json' }, status: 200, body: fixture('show_droplets'))
|
14
|
+
allow(Kernel).to receive(:exec).with("scp -i #{Dir.home}/.ssh/id_rsa2 /tmp/foo baz@104.236.32.182:/tmp/bar")
|
15
|
+
|
16
|
+
expected_string = <<-eos
|
17
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
18
|
+
Executing SCP on Droplet (example.com)...
|
19
|
+
Attempting SCP with `scp -i #{Dir.home}/.ssh/id_rsa2 /tmp/foo baz@104.236.32.182:/tmp/bar`
|
20
|
+
eos
|
21
|
+
|
22
|
+
expect { cli.scp('example.com', '/tmp/foo', '/tmp/bar') }.to output(expected_string).to_stdout
|
23
|
+
end
|
24
|
+
|
25
|
+
it "runs with rsync if given at the command line" do
|
26
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
27
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
28
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
29
|
+
|
30
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=200').
|
31
|
+
to_return(headers: { 'Content-Type' => 'application/json' }, status: 200, body: fixture('show_droplets'))
|
32
|
+
allow(Kernel).to receive(:exec).with("rsync -i #{Dir.home}/.ssh/id_rsa2 /tmp/foo baz@104.236.32.182:/tmp/bar")
|
33
|
+
|
34
|
+
expected_string = <<-eos
|
35
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
36
|
+
Executing SCP on Droplet (example.com)...
|
37
|
+
Attempting SCP with `rsync -i #{Dir.home}/.ssh/id_rsa2 /tmp/foo baz@104.236.32.182:/tmp/bar`
|
38
|
+
eos
|
39
|
+
|
40
|
+
cli.options = cli.options.merge(scp_command: 'rsync')
|
41
|
+
|
42
|
+
expect { cli.scp('example.com', '/tmp/foo', '/tmp/bar') }.to output(expected_string).to_stdout
|
43
|
+
end
|
44
|
+
|
45
|
+
it "wait's until droplet active if -w command is given and droplet eventually active" do
|
46
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
47
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
48
|
+
to_return(status: 200, body: '', headers: {})
|
49
|
+
|
50
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets/6918990?per_page=200').
|
51
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
52
|
+
to_return(
|
53
|
+
{ status: 200, body: fixture('show_droplet_inactive'), headers: {} },
|
54
|
+
status: 200, body: fixture('show_droplet'), headers: {}
|
55
|
+
)
|
56
|
+
|
57
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=200').
|
58
|
+
to_return(headers: { 'Content-Type' => 'application/json' }, status: 200, body: fixture('show_droplets'))
|
59
|
+
allow(Kernel).to receive(:exec).with("scp -i #{Dir.home}/.ssh/id_rsa2 /tmp/foo baz@104.236.32.182:/tmp/bar")
|
60
|
+
|
61
|
+
cli.options = cli.options.merge(wait: true)
|
62
|
+
|
63
|
+
expected_string = <<-eos
|
64
|
+
Droplet fuzzy name provided. Finding droplet ID...done\e[0m, 6918990 (example.com)
|
65
|
+
Executing SCP on Droplet (example.com)...
|
66
|
+
Wait flag given, waiting for droplet to become active
|
67
|
+
..done\e[0m (2s)
|
68
|
+
Attempting SCP with `scp -i #{Dir.home}/.ssh/id_rsa2 /tmp/foo baz@104.236.32.182:/tmp/bar`
|
69
|
+
eos
|
70
|
+
|
71
|
+
expect { cli.scp('example.com', '/tmp/foo', '/tmp/bar') }.to output(expected_string).to_stdout
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tugboat::CLI do
|
4
|
+
include_context 'spec'
|
5
|
+
|
6
|
+
describe 'snapshots' do
|
7
|
+
it 'shows a list when snapshots exist' do
|
8
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
9
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
10
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
11
|
+
|
12
|
+
stub_request(:get, "https://api.digitalocean.com/v2/snapshots?page=1&per_page=20").
|
13
|
+
with(headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Bearer foo', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}).
|
14
|
+
to_return(status: 200, body: fixture('show_snapshots'), headers: {})
|
15
|
+
|
16
|
+
expected_string = <<-eos
|
17
|
+
5.10 x64 (id: 6372321, resource_type: droplet, created_at: 2014-09-26T16:40:18Z)
|
18
|
+
eos
|
19
|
+
|
20
|
+
expect { cli.snapshots }.to output(expected_string).to_stdout
|
21
|
+
|
22
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.once
|
23
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/snapshots?page=1&per_page=20')).to have_been_made.once
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'shows a message when no snapshots exist' do
|
27
|
+
stub_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1').
|
28
|
+
with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer foo', 'Content-Type' => 'application/json', 'User-Agent' => 'Faraday v0.9.2' }).
|
29
|
+
to_return(status: 200, body: fixture('show_droplets'), headers: {})
|
30
|
+
|
31
|
+
stub_request(:get, "https://api.digitalocean.com/v2/snapshots?page=1&per_page=20").
|
32
|
+
with(headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Bearer foo', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}).
|
33
|
+
to_return(status: 200, body: fixture('show_snapshots_empty'), headers: {})
|
34
|
+
|
35
|
+
expected_string = <<-eos
|
36
|
+
You don't appear to have any snapshots.
|
37
|
+
eos
|
38
|
+
|
39
|
+
expect { cli.snapshots }.to output(expected_string).to_stdout
|
40
|
+
|
41
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/droplets?page=1&per_page=1')).to have_been_made.once
|
42
|
+
expect(a_request(:get, 'https://api.digitalocean.com/v2/snapshots?page=1&per_page=20')).to have_been_made.once
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"action": {
|
3
|
+
"id": 2,
|
4
|
+
"status": "in-progress",
|
5
|
+
"type": "disable_backups",
|
6
|
+
"started_at": "2014-07-29T14:35:27Z",
|
7
|
+
"completed_at": null,
|
8
|
+
"resource_id": 12,
|
9
|
+
"resource_type": "droplet",
|
10
|
+
"region_slug": "nyc1",
|
11
|
+
"region": {
|
12
|
+
"name": "New York",
|
13
|
+
"slug": "nyc1",
|
14
|
+
"available": true,
|
15
|
+
"sizes": [
|
16
|
+
"512mb"
|
17
|
+
],
|
18
|
+
"features": [
|
19
|
+
"virtio",
|
20
|
+
"private_networking",
|
21
|
+
"backups",
|
22
|
+
"ipv6",
|
23
|
+
"metadata"
|
24
|
+
]
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"action": {
|
3
|
+
"id": 2,
|
4
|
+
"status": "in-progress",
|
5
|
+
"type": "enable_backups",
|
6
|
+
"started_at": "2014-07-29T14:35:27Z",
|
7
|
+
"completed_at": null,
|
8
|
+
"resource_id": 12,
|
9
|
+
"resource_type": "droplet",
|
10
|
+
"region_slug": "nyc1",
|
11
|
+
"region": {
|
12
|
+
"name": "New York",
|
13
|
+
"slug": "nyc1",
|
14
|
+
"available": true,
|
15
|
+
"sizes": [
|
16
|
+
"512mb"
|
17
|
+
],
|
18
|
+
"features": [
|
19
|
+
"virtio",
|
20
|
+
"private_networking",
|
21
|
+
"backups",
|
22
|
+
"ipv6",
|
23
|
+
"metadata"
|
24
|
+
]
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
{
|
2
|
+
"snapshots": [
|
3
|
+
{
|
4
|
+
"id": 6372321,
|
5
|
+
"name": "5.10 x64",
|
6
|
+
"regions": [
|
7
|
+
"nyc1",
|
8
|
+
"ams1",
|
9
|
+
"sfo1",
|
10
|
+
"nyc2",
|
11
|
+
"ams2",
|
12
|
+
"sgp1",
|
13
|
+
"lon1",
|
14
|
+
"nyc3",
|
15
|
+
"ams3",
|
16
|
+
"fra1",
|
17
|
+
"tor1"
|
18
|
+
],
|
19
|
+
"created_at": "2014-09-26T16:40:18Z",
|
20
|
+
"resource_id": 2713828,
|
21
|
+
"resource_type": "droplet",
|
22
|
+
"min_disk_size": 20,
|
23
|
+
"size_gigabytes": 1.42
|
24
|
+
}
|
25
|
+
],
|
26
|
+
"links": {
|
27
|
+
"pages": {
|
28
|
+
"last": "https://api.digitalocean.com/v2/snapshots?page=1&per_page=1"
|
29
|
+
}
|
30
|
+
},
|
31
|
+
"meta": {
|
32
|
+
"total": 1
|
33
|
+
}
|
34
|
+
}
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tugboat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Pearkes
|
8
8
|
- Peter Souter
|
9
|
-
- Ørjan Blom
|
9
|
+
- "Ørjan Blom"
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2018-03-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: thor
|
@@ -396,6 +396,7 @@ files:
|
|
396
396
|
- lib/tugboat/middleware.rb
|
397
397
|
- lib/tugboat/middleware/add_key.rb
|
398
398
|
- lib/tugboat/middleware/ask_for_credentials.rb
|
399
|
+
- lib/tugboat/middleware/backup_setting.rb
|
399
400
|
- lib/tugboat/middleware/base.rb
|
400
401
|
- lib/tugboat/middleware/check_configuration.rb
|
401
402
|
- lib/tugboat/middleware/check_credentials.rb
|
@@ -419,11 +420,13 @@ files:
|
|
419
420
|
- lib/tugboat/middleware/list_images.rb
|
420
421
|
- lib/tugboat/middleware/list_regions.rb
|
421
422
|
- lib/tugboat/middleware/list_sizes.rb
|
423
|
+
- lib/tugboat/middleware/list_snapshots.rb
|
422
424
|
- lib/tugboat/middleware/list_ssh_keys.rb
|
423
425
|
- lib/tugboat/middleware/password_reset.rb
|
424
426
|
- lib/tugboat/middleware/rebuild_droplet.rb
|
425
427
|
- lib/tugboat/middleware/resize_droplet.rb
|
426
428
|
- lib/tugboat/middleware/restart_droplet.rb
|
429
|
+
- lib/tugboat/middleware/scp_droplet.rb
|
427
430
|
- lib/tugboat/middleware/snapshot_droplet.rb
|
428
431
|
- lib/tugboat/middleware/ssh_droplet.rb
|
429
432
|
- lib/tugboat/middleware/start_droplet.rb
|
@@ -432,6 +435,7 @@ files:
|
|
432
435
|
- license/dependency_decisions.yml
|
433
436
|
- spec/cli/add_key_spec.rb
|
434
437
|
- spec/cli/authorize_cli_spec.rb
|
438
|
+
- spec/cli/backup_setting_spec.rb
|
435
439
|
- spec/cli/config_cli_spec.rb
|
436
440
|
- spec/cli/create_cli_spec.rb
|
437
441
|
- spec/cli/debug_cli_spec.rb
|
@@ -450,8 +454,10 @@ files:
|
|
450
454
|
- spec/cli/regions_cli_spec.rb
|
451
455
|
- spec/cli/resize_cli_spec.rb
|
452
456
|
- spec/cli/restart_cli_spec.rb
|
457
|
+
- spec/cli/scp_cli_spec.rb
|
453
458
|
- spec/cli/sizes_cli_spec.rb
|
454
459
|
- spec/cli/snapshot_cli_spec.rb
|
460
|
+
- spec/cli/snapshots_cli_spec.rb
|
455
461
|
- spec/cli/ssh_cli_spec.rb
|
456
462
|
- spec/cli/start_cli_spec.rb
|
457
463
|
- spec/cli/verify_cli_spec.rb
|
@@ -463,8 +469,10 @@ files:
|
|
463
469
|
- spec/fixtures/create_droplet.json
|
464
470
|
- spec/fixtures/create_ssh_key.json
|
465
471
|
- spec/fixtures/create_ssh_key_from_file.json
|
472
|
+
- spec/fixtures/disable_backups_response.json
|
466
473
|
- spec/fixtures/droplet_no_network.json
|
467
474
|
- spec/fixtures/droplet_start_response.json
|
475
|
+
- spec/fixtures/enable_backups_response.json
|
468
476
|
- spec/fixtures/not_found.json
|
469
477
|
- spec/fixtures/password_reset_response.json
|
470
478
|
- spec/fixtures/power_cycle_response.json
|
@@ -487,6 +495,8 @@ files:
|
|
487
495
|
- spec/fixtures/show_redmine_image.json
|
488
496
|
- spec/fixtures/show_regions.json
|
489
497
|
- spec/fixtures/show_sizes.json
|
498
|
+
- spec/fixtures/show_snapshots.json
|
499
|
+
- spec/fixtures/show_snapshots_empty.json
|
490
500
|
- spec/fixtures/shutdown_response.json
|
491
501
|
- spec/fixtures/snapshot_response.json
|
492
502
|
- spec/fixtures/ubuntu_image_9801951.json
|
@@ -524,7 +534,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
524
534
|
version: '0'
|
525
535
|
requirements: []
|
526
536
|
rubyforge_project:
|
527
|
-
rubygems_version: 2.5
|
537
|
+
rubygems_version: 2.4.5
|
528
538
|
signing_key:
|
529
539
|
specification_version: 4
|
530
540
|
summary: A command line tool for interacting with your DigitalOcean droplets.
|
@@ -538,6 +548,7 @@ test_files:
|
|
538
548
|
- features/tugboat/config_number_key.feature
|
539
549
|
- spec/cli/add_key_spec.rb
|
540
550
|
- spec/cli/authorize_cli_spec.rb
|
551
|
+
- spec/cli/backup_setting_spec.rb
|
541
552
|
- spec/cli/config_cli_spec.rb
|
542
553
|
- spec/cli/create_cli_spec.rb
|
543
554
|
- spec/cli/debug_cli_spec.rb
|
@@ -556,8 +567,10 @@ test_files:
|
|
556
567
|
- spec/cli/regions_cli_spec.rb
|
557
568
|
- spec/cli/resize_cli_spec.rb
|
558
569
|
- spec/cli/restart_cli_spec.rb
|
570
|
+
- spec/cli/scp_cli_spec.rb
|
559
571
|
- spec/cli/sizes_cli_spec.rb
|
560
572
|
- spec/cli/snapshot_cli_spec.rb
|
573
|
+
- spec/cli/snapshots_cli_spec.rb
|
561
574
|
- spec/cli/ssh_cli_spec.rb
|
562
575
|
- spec/cli/start_cli_spec.rb
|
563
576
|
- spec/cli/verify_cli_spec.rb
|
@@ -569,8 +582,10 @@ test_files:
|
|
569
582
|
- spec/fixtures/create_droplet.json
|
570
583
|
- spec/fixtures/create_ssh_key.json
|
571
584
|
- spec/fixtures/create_ssh_key_from_file.json
|
585
|
+
- spec/fixtures/disable_backups_response.json
|
572
586
|
- spec/fixtures/droplet_no_network.json
|
573
587
|
- spec/fixtures/droplet_start_response.json
|
588
|
+
- spec/fixtures/enable_backups_response.json
|
574
589
|
- spec/fixtures/not_found.json
|
575
590
|
- spec/fixtures/password_reset_response.json
|
576
591
|
- spec/fixtures/power_cycle_response.json
|
@@ -593,6 +608,8 @@ test_files:
|
|
593
608
|
- spec/fixtures/show_redmine_image.json
|
594
609
|
- spec/fixtures/show_regions.json
|
595
610
|
- spec/fixtures/show_sizes.json
|
611
|
+
- spec/fixtures/show_snapshots.json
|
612
|
+
- spec/fixtures/show_snapshots_empty.json
|
596
613
|
- spec/fixtures/shutdown_response.json
|
597
614
|
- spec/fixtures/snapshot_response.json
|
598
615
|
- spec/fixtures/ubuntu_image_9801951.json
|