tugboat 0.0.5 → 0.0.6
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 +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
|
[](https://travis-ci.org/pearkes/tugboat)
|
3
|
+
[](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
|
+
|