tugboat 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG.md +27 -2
- data/README.md +61 -0
- data/lib/tugboat/cli.rb +91 -4
- data/lib/tugboat/middleware.rb +54 -0
- data/lib/tugboat/middleware/check_droplet_active.rb +17 -0
- data/lib/tugboat/middleware/check_droplet_inactive.rb +17 -0
- data/lib/tugboat/middleware/find_droplet.rb +4 -0
- data/lib/tugboat/middleware/password_reset.rb +23 -0
- data/lib/tugboat/middleware/resize_droplet.rb +24 -0
- data/lib/tugboat/middleware/ssh_droplet.rb +4 -0
- data/lib/tugboat/middleware/start_droplet.rb +21 -0
- data/lib/tugboat/middleware/wait_for_state.rb +35 -0
- data/lib/tugboat/version.rb +1 -1
- data/spec/cli/authorize_cli_spec.rb +0 -4
- data/spec/cli/create_cli_spec.rb +0 -4
- data/spec/cli/destroy_cli_spec.rb +0 -4
- data/spec/cli/droplets_cli_spec.rb +1 -5
- data/spec/cli/halt_cli_spec.rb +16 -4
- data/spec/cli/help_cli_spec.rb +17 -0
- data/spec/cli/images_cli_spec.rb +0 -4
- data/spec/cli/info_cli_spec.rb +0 -4
- data/spec/cli/keys_cli_spec.rb +0 -4
- data/spec/cli/password_reset_cli_spec.rb +83 -0
- data/spec/cli/regions_cli_spec.rb +0 -4
- data/spec/cli/resize_cli_spec.rb +82 -0
- data/spec/cli/restart_cli_spec.rb +0 -4
- data/spec/cli/sizes_cli_spec.rb +0 -4
- data/spec/cli/snapshot_cli_spec.rb +18 -7
- data/spec/cli/ssh_cli_spec.rb +19 -4
- data/spec/cli/start_cli_spec.rb +76 -0
- data/spec/cli/version_cli_spec.rb +0 -4
- data/spec/cli/wait_cli_spec.rb +66 -0
- data/spec/config_spec.rb +2 -0
- data/spec/fixtures/show_droplet_inactive.json +13 -0
- data/spec/fixtures/show_droplets.json +1 -1
- data/spec/fixtures/show_droplets_inactive.json +35 -0
- data/spec/middleware/check_configuration_spec.rb +0 -3
- data/spec/middleware/check_credentials_spec.rb +0 -3
- data/spec/middleware/check_droplet_active_spec.rb +15 -0
- data/spec/middleware/check_droplet_inactive_spec.rb +15 -0
- data/spec/middleware/find_droplet_spec.rb +0 -3
- data/spec/middleware/inject_configuration_spec.rb +0 -3
- data/spec/middleware/ssh_droplet_spec.rb +19 -3
- data/spec/shared/environment.rb +3 -0
- data/spec/spec_helper.rb +5 -0
- data/tugboat.gemspec +2 -0
- metadata +42 -2
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,29 @@
|
|
1
|
-
## 0.0.
|
1
|
+
## 0.0.6 (June 25, 2013)
|
2
|
+
|
3
|
+
FEATURES:
|
4
|
+
|
5
|
+
- [Ørjan](https://github.com/blom) added a `start` command, which
|
6
|
+
let's you start a droplet. [GH-30]
|
7
|
+
- [Ørjan](https://github.com/blom) added a `resize` command, which
|
8
|
+
let's you resize a droplet. [GH-40]
|
9
|
+
- [Ørjan](https://github.com/blom) added a `password-reset` command
|
10
|
+
[GH-45]
|
11
|
+
- Added a the `wait` command, allowing you to "wait" for a droplet
|
12
|
+
to enter a state. [GH-46]
|
13
|
+
|
14
|
+
IMPROVEMENTS:
|
15
|
+
|
16
|
+
- [Ørjan](https://github.com/blom) added an `--ssh-opts` flag, for the
|
17
|
+
`ssh` command. [GH-38]
|
18
|
+
- Droplet state is checked for some commands. For example, a droplet
|
19
|
+
can't be started if it's active. [GH-31]
|
20
|
+
|
21
|
+
BUG FIXES:
|
22
|
+
|
23
|
+
- DigitalOcean changed their `image_id`'s, so the defaults for `create`
|
24
|
+
were updated. [GH-39]
|
25
|
+
|
26
|
+
## 0.0.5 (May 4, 2013)
|
2
27
|
|
3
28
|
FEATURES:
|
4
29
|
|
@@ -11,7 +36,7 @@ FEATURES:
|
|
11
36
|
- [Ørjan](https://github.com/blom) added a `sizes` command, which
|
12
37
|
returns a list of available sizes. You can specify which size to
|
13
38
|
use while creating: `tugboat create foobar -s 66` [GH-19]
|
14
|
-
- [Ørjan](https://github.com/blom) added a `hard` flag to
|
39
|
+
- [Ørjan](https://github.com/blom) added a `hard` flag to
|
15
40
|
`halt` and `restart`. This cycles the Droplet's power. `tugboat restart --hard` [GH-27]
|
16
41
|
|
17
42
|
IMPROVEMENTS:
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Tugboat
|
2
2
|
[![Build Status](https://travis-ci.org/pearkes/tugboat.png?branch=master)](https://travis-ci.org/pearkes/tugboat)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/pearkes/tugboat/badge.png?branch=master)](https://coveralls.io/r/pearkes/tugboat)
|
3
4
|
|
4
5
|
A command line tool for interacting with your [DigitalOcean](https://www.digitalocean.com/) droplets.
|
5
6
|
|
@@ -107,6 +108,66 @@ match.
|
|
107
108
|
$ tugboat snapshot test-admin-snaphot admin
|
108
109
|
Queuing snapshot 'test-admin-snapshot' for 13231512 (pearkes-admin-001)...done
|
109
110
|
|
111
|
+
### Resize a droplet
|
112
|
+
|
113
|
+
$ tugboat resize admin -s 66
|
114
|
+
Queuing resize for 13231512 (pearkes-admin-001)...done
|
115
|
+
|
116
|
+
### List Available Images
|
117
|
+
|
118
|
+
You can list images that you have created.
|
119
|
+
|
120
|
+
$ tugboat images
|
121
|
+
My Images:
|
122
|
+
pearkes-admin-001 2013-05-19 (id: 13231512, distro: Ubuntu)
|
123
|
+
....
|
124
|
+
|
125
|
+
Optionally, list images provided by DigitalOcean as well.
|
126
|
+
|
127
|
+
$ tugboat images --global
|
128
|
+
My Images:
|
129
|
+
pearkes-admin-001 2013-05-19 (id: 13231512, distro: Ubuntu)
|
130
|
+
....
|
131
|
+
Global Images:
|
132
|
+
CentOS 5.8 x64 (id: 1601, distro: CentOS)
|
133
|
+
...
|
134
|
+
|
135
|
+
### List Available Sizes
|
136
|
+
|
137
|
+
$ tugboat sizes
|
138
|
+
Sizes:
|
139
|
+
512MB (id: 66)
|
140
|
+
1GB (id: 63)
|
141
|
+
...
|
142
|
+
|
143
|
+
### List Available Regions
|
144
|
+
|
145
|
+
$ tugboat regions
|
146
|
+
Regions:
|
147
|
+
New York 1 (id: 1)
|
148
|
+
Amsterdam 1 (id: 2)
|
149
|
+
San Francisco 1 (id: 3)
|
150
|
+
|
151
|
+
### List SSH Keys
|
152
|
+
|
153
|
+
$ tugboat keys
|
154
|
+
Keys:
|
155
|
+
pearkes (id: 10501)
|
156
|
+
...
|
157
|
+
|
158
|
+
### Wait for Droplet State
|
159
|
+
|
160
|
+
Sometimes you want to wait for a droplet to enter some state, for
|
161
|
+
example "off".
|
162
|
+
|
163
|
+
$ tugboat wait admin --state off
|
164
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231512 (pearkes-admin-001)
|
165
|
+
Waiting for droplet to become off....
|
166
|
+
...
|
167
|
+
|
168
|
+
This will simply block until the droplet returns a state of "off".
|
169
|
+
A period will be printed after each request.
|
170
|
+
|
110
171
|
## Help
|
111
172
|
|
112
173
|
If you're curious about command flags for a specific command, you can
|
data/lib/tugboat/cli.rb
CHANGED
@@ -9,7 +9,9 @@ module Tugboat
|
|
9
9
|
|
10
10
|
!check_unknown_options
|
11
11
|
|
12
|
-
map "--version"
|
12
|
+
map "--version" => :version,
|
13
|
+
"-v" => :version,
|
14
|
+
"password-reset" => :password_reset
|
13
15
|
|
14
16
|
desc "help [COMMAND]", "Describe commands or a specific command"
|
15
17
|
def help(meth=nil)
|
@@ -68,13 +70,18 @@ module Tugboat
|
|
68
70
|
:type => :string,
|
69
71
|
:aliases => "-u",
|
70
72
|
:desc => "Specifies which user to log in as"
|
73
|
+
method_option "ssh_opts",
|
74
|
+
:type => :string,
|
75
|
+
:aliases => "-o",
|
76
|
+
:desc => "Custom SSH options"
|
71
77
|
def ssh(name=nil)
|
72
78
|
Middleware.sequence_ssh_droplet.call({
|
73
79
|
"user_droplet_id" => options[:id],
|
74
80
|
"user_droplet_name" => options[:name],
|
75
81
|
"user_droplet_fuzzy_name" => name,
|
76
82
|
"user_droplet_ssh_port" => options[:ssh_port],
|
77
|
-
"user_droplet_ssh_user" => options[:ssh_user]
|
83
|
+
"user_droplet_ssh_user" => options[:ssh_user],
|
84
|
+
"user_droplet_ssh_opts" => options[:ssh_opts]
|
78
85
|
})
|
79
86
|
end
|
80
87
|
|
@@ -82,12 +89,12 @@ module Tugboat
|
|
82
89
|
method_option "size",
|
83
90
|
:type => :numeric,
|
84
91
|
:aliases => "-s",
|
85
|
-
:default =>
|
92
|
+
:default => 66,
|
86
93
|
:desc => "The size_id of the droplet"
|
87
94
|
method_option "image",
|
88
95
|
:type => :numeric,
|
89
96
|
:aliases => "-i",
|
90
|
-
:default =>
|
97
|
+
:default => 284203,
|
91
98
|
:desc => "The image_id of the droplet"
|
92
99
|
method_option "region",
|
93
100
|
:type => :numeric,
|
@@ -228,6 +235,86 @@ module Tugboat
|
|
228
235
|
def sizes
|
229
236
|
Middleware.sequence_sizes.call({})
|
230
237
|
end
|
238
|
+
|
239
|
+
desc "start FUZZY_NAME", "Start a droplet"
|
240
|
+
method_option "id",
|
241
|
+
:type => :string,
|
242
|
+
:aliases => "-i",
|
243
|
+
:desc => "The ID of the droplet."
|
244
|
+
method_option "name",
|
245
|
+
:type => :string,
|
246
|
+
:aliases => "-n",
|
247
|
+
:desc => "The exact name of the droplet"
|
248
|
+
def start(name=nil)
|
249
|
+
Middleware.sequence_start_droplet.call({
|
250
|
+
"user_droplet_id" => options[:id],
|
251
|
+
"user_droplet_name" => options[:name],
|
252
|
+
"user_droplet_fuzzy_name" => name
|
253
|
+
})
|
254
|
+
end
|
255
|
+
|
256
|
+
desc "resize FUZZY_NAME", "Resize a droplet"
|
257
|
+
method_option "id",
|
258
|
+
:type => :numeric,
|
259
|
+
:aliases => "-i",
|
260
|
+
:desc => "The ID of the droplet."
|
261
|
+
method_option "name",
|
262
|
+
:type => :string,
|
263
|
+
:aliases => "-n",
|
264
|
+
:desc => "The exact name of the droplet"
|
265
|
+
method_option "size",
|
266
|
+
:type => :numeric,
|
267
|
+
:aliases => "-s",
|
268
|
+
:required => true,
|
269
|
+
:desc => "The size_id to resize the droplet to"
|
270
|
+
def resize(name=nil)
|
271
|
+
Middleware.sequence_resize_droplet.call({
|
272
|
+
"user_droplet_id" => options[:id],
|
273
|
+
"user_droplet_name" => options[:name],
|
274
|
+
"user_droplet_size" => options[:size],
|
275
|
+
"user_droplet_fuzzy_name" => name
|
276
|
+
})
|
277
|
+
end
|
278
|
+
|
279
|
+
desc "password-reset FUZZY_NAME", "Reset root password"
|
280
|
+
method_option "id",
|
281
|
+
:type => :numeric,
|
282
|
+
:aliases => "-i",
|
283
|
+
:desc => "The ID of the droplet."
|
284
|
+
method_option "name",
|
285
|
+
:type => :string,
|
286
|
+
:aliases => "-n",
|
287
|
+
:desc => "The exact name of the droplet"
|
288
|
+
def password_reset(name=nil)
|
289
|
+
Middleware.sequence_password_reset.call({
|
290
|
+
"user_droplet_id" => options[:id],
|
291
|
+
"user_droplet_name" => options[:name],
|
292
|
+
"user_droplet_fuzzy_name" => name
|
293
|
+
})
|
294
|
+
end
|
295
|
+
|
296
|
+
desc "wait FUZZY_NAME", "Wait for a droplet to reach a state"
|
297
|
+
method_option "id",
|
298
|
+
:type => :numeric,
|
299
|
+
:aliases => "-i",
|
300
|
+
:desc => "The ID of the droplet."
|
301
|
+
method_option "name",
|
302
|
+
:type => :string,
|
303
|
+
:aliases => "-n",
|
304
|
+
:desc => "The exact name of the droplet"
|
305
|
+
method_option "state",
|
306
|
+
:type => :string,
|
307
|
+
:aliases => "-s",
|
308
|
+
:default => "active",
|
309
|
+
:desc => "The state of the droplet to wait for"
|
310
|
+
def wait(name=nil)
|
311
|
+
Middleware.sequence_wait.call({
|
312
|
+
"user_droplet_id" => options[:id],
|
313
|
+
"user_droplet_name" => options[:name],
|
314
|
+
"user_droplet_desired_state" => options[:state],
|
315
|
+
"user_droplet_fuzzy_name" => name
|
316
|
+
})
|
317
|
+
end
|
231
318
|
end
|
232
319
|
end
|
233
320
|
|
data/lib/tugboat/middleware.rb
CHANGED
@@ -11,6 +11,7 @@ module Tugboat
|
|
11
11
|
autoload :ListDroplets, "tugboat/middleware/list_droplets"
|
12
12
|
autoload :FindDroplet, "tugboat/middleware/find_droplet"
|
13
13
|
autoload :RestartDroplet, "tugboat/middleware/restart_droplet"
|
14
|
+
autoload :StartDroplet, "tugboat/middleware/start_droplet"
|
14
15
|
autoload :HaltDroplet, "tugboat/middleware/halt_droplet"
|
15
16
|
autoload :InfoDroplet, "tugboat/middleware/info_droplet"
|
16
17
|
autoload :SSHDroplet, "tugboat/middleware/ssh_droplet"
|
@@ -18,10 +19,15 @@ module Tugboat
|
|
18
19
|
autoload :DestroyDroplet, "tugboat/middleware/destroy_droplet"
|
19
20
|
autoload :ConfirmAction, "tugboat/middleware/confirm_action"
|
20
21
|
autoload :SnapshotDroplet, "tugboat/middleware/snapshot_droplet"
|
22
|
+
autoload :ResizeDroplet, "tugboat/middleware/resize_droplet"
|
21
23
|
autoload :ListImages, "tugboat/middleware/list_images"
|
22
24
|
autoload :ListSSHKeys, "tugboat/middleware/list_ssh_keys"
|
23
25
|
autoload :ListRegions, "tugboat/middleware/list_regions"
|
24
26
|
autoload :ListSizes, "tugboat/middleware/list_sizes"
|
27
|
+
autoload :CheckDropletActive, "tugboat/middleware/check_droplet_active"
|
28
|
+
autoload :CheckDropletInactive, "tugboat/middleware/check_droplet_inactive"
|
29
|
+
autoload :PasswordReset, "tugboat/middleware/password_reset"
|
30
|
+
autoload :WaitForState, "tugboat/middleware/wait_for_state"
|
25
31
|
|
26
32
|
# Start the authorization flow.
|
27
33
|
# This writes a ~/.tugboat file, which can be edited manually.
|
@@ -67,6 +73,18 @@ module Tugboat
|
|
67
73
|
end
|
68
74
|
end
|
69
75
|
|
76
|
+
# Start a droplet
|
77
|
+
def self.sequence_start_droplet
|
78
|
+
::Middleware::Builder.new do
|
79
|
+
use InjectConfiguration
|
80
|
+
use CheckConfiguration
|
81
|
+
use InjectClient
|
82
|
+
use FindDroplet
|
83
|
+
use CheckDropletInactive
|
84
|
+
use StartDroplet
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
70
88
|
# Shutdown a droplet
|
71
89
|
def self.sequence_halt_droplet
|
72
90
|
::Middleware::Builder.new do
|
@@ -74,6 +92,7 @@ module Tugboat
|
|
74
92
|
use CheckConfiguration
|
75
93
|
use InjectClient
|
76
94
|
use FindDroplet
|
95
|
+
use CheckDropletActive
|
77
96
|
use HaltDroplet
|
78
97
|
end
|
79
98
|
end
|
@@ -96,6 +115,7 @@ module Tugboat
|
|
96
115
|
use CheckConfiguration
|
97
116
|
use InjectClient
|
98
117
|
use FindDroplet
|
118
|
+
use CheckDropletActive
|
99
119
|
use SSHDroplet
|
100
120
|
end
|
101
121
|
end
|
@@ -129,6 +149,7 @@ module Tugboat
|
|
129
149
|
use CheckConfiguration
|
130
150
|
use InjectClient
|
131
151
|
use FindDroplet
|
152
|
+
use CheckDropletInactive
|
132
153
|
use SnapshotDroplet
|
133
154
|
end
|
134
155
|
end
|
@@ -162,5 +183,38 @@ module Tugboat
|
|
162
183
|
use ListSizes
|
163
184
|
end
|
164
185
|
end
|
186
|
+
|
187
|
+
# Resize a droplet
|
188
|
+
def self.sequence_resize_droplet
|
189
|
+
::Middleware::Builder.new do
|
190
|
+
use InjectConfiguration
|
191
|
+
use CheckConfiguration
|
192
|
+
use InjectClient
|
193
|
+
use FindDroplet
|
194
|
+
use ResizeDroplet
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Reset root password
|
199
|
+
def self.sequence_password_reset
|
200
|
+
::Middleware::Builder.new do
|
201
|
+
use InjectConfiguration
|
202
|
+
use CheckConfiguration
|
203
|
+
use InjectClient
|
204
|
+
use FindDroplet
|
205
|
+
use PasswordReset
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Wait for a droplet to enter a desired state
|
210
|
+
def self.sequence_wait
|
211
|
+
::Middleware::Builder.new do
|
212
|
+
use InjectConfiguration
|
213
|
+
use CheckConfiguration
|
214
|
+
use InjectClient
|
215
|
+
use FindDroplet
|
216
|
+
use WaitForState
|
217
|
+
end
|
218
|
+
end
|
165
219
|
end
|
166
220
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the droplet in the environment is active
|
4
|
+
class CheckDropletActive < Base
|
5
|
+
def call(env)
|
6
|
+
|
7
|
+
if env["droplet_status"] != "active"
|
8
|
+
say "Droplet must be on for this operation to be successful.", :red
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the droplet in the environment is inactive, or "off"
|
4
|
+
class CheckDropletInactive < Base
|
5
|
+
def call(env)
|
6
|
+
|
7
|
+
if env["droplet_status"] != "off"
|
8
|
+
say "Droplet must be off for this operation to be successful.", :red
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -33,6 +33,7 @@ module Tugboat
|
|
33
33
|
env["droplet_id"] = req.droplet.id
|
34
34
|
env["droplet_name"] = "(#{req.droplet.name})"
|
35
35
|
env["droplet_ip"] = req.droplet.ip_address
|
36
|
+
env["droplet_status"] = req.droplet.status
|
36
37
|
end
|
37
38
|
|
38
39
|
# If they provide a name, we need to get the ID for it.
|
@@ -46,6 +47,7 @@ module Tugboat
|
|
46
47
|
env["droplet_id"] = d.id
|
47
48
|
env["droplet_name"] = "(#{d.name})"
|
48
49
|
env["droplet_ip"] = d.ip_address
|
50
|
+
env["droplet_status"] = d.status
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
@@ -81,6 +83,7 @@ module Tugboat
|
|
81
83
|
env["droplet_id"] = found_droplets.first.id
|
82
84
|
env["droplet_name"] = "(#{found_droplets.first.name})"
|
83
85
|
env["droplet_ip"] = found_droplets.first.ip_address
|
86
|
+
env["droplet_status"] = found_droplets.first.status
|
84
87
|
elsif found_droplets.length > 1
|
85
88
|
# Did we run the multiple questionairre?
|
86
89
|
did_run_multiple = true
|
@@ -96,6 +99,7 @@ module Tugboat
|
|
96
99
|
env["droplet_id"] = found_droplets[choice.to_i].id
|
97
100
|
env["droplet_name"] = found_droplets[choice.to_i].name
|
98
101
|
env["droplet_ip"] = found_droplets[choice.to_i].ip_address
|
102
|
+
env["droplet_status"] = found_droplets[choice.to_i].status
|
99
103
|
end
|
100
104
|
|
101
105
|
# If we coulnd't find it, tell the user and drop out of the
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class PasswordReset < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
say "Queuing password reset for #{env["droplet_id"]} #{env["droplet_name"]}...", nil, false
|
8
|
+
res = ocean.droplets.password_reset env["droplet_id"]
|
9
|
+
|
10
|
+
if res.status == "ERROR"
|
11
|
+
say res.error_message, :red
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
say "done", :green
|
16
|
+
say "Your new root password will be emailed to you"
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|