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.
Files changed (48) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.md +27 -2
  3. data/README.md +61 -0
  4. data/lib/tugboat/cli.rb +91 -4
  5. data/lib/tugboat/middleware.rb +54 -0
  6. data/lib/tugboat/middleware/check_droplet_active.rb +17 -0
  7. data/lib/tugboat/middleware/check_droplet_inactive.rb +17 -0
  8. data/lib/tugboat/middleware/find_droplet.rb +4 -0
  9. data/lib/tugboat/middleware/password_reset.rb +23 -0
  10. data/lib/tugboat/middleware/resize_droplet.rb +24 -0
  11. data/lib/tugboat/middleware/ssh_droplet.rb +4 -0
  12. data/lib/tugboat/middleware/start_droplet.rb +21 -0
  13. data/lib/tugboat/middleware/wait_for_state.rb +35 -0
  14. data/lib/tugboat/version.rb +1 -1
  15. data/spec/cli/authorize_cli_spec.rb +0 -4
  16. data/spec/cli/create_cli_spec.rb +0 -4
  17. data/spec/cli/destroy_cli_spec.rb +0 -4
  18. data/spec/cli/droplets_cli_spec.rb +1 -5
  19. data/spec/cli/halt_cli_spec.rb +16 -4
  20. data/spec/cli/help_cli_spec.rb +17 -0
  21. data/spec/cli/images_cli_spec.rb +0 -4
  22. data/spec/cli/info_cli_spec.rb +0 -4
  23. data/spec/cli/keys_cli_spec.rb +0 -4
  24. data/spec/cli/password_reset_cli_spec.rb +83 -0
  25. data/spec/cli/regions_cli_spec.rb +0 -4
  26. data/spec/cli/resize_cli_spec.rb +82 -0
  27. data/spec/cli/restart_cli_spec.rb +0 -4
  28. data/spec/cli/sizes_cli_spec.rb +0 -4
  29. data/spec/cli/snapshot_cli_spec.rb +18 -7
  30. data/spec/cli/ssh_cli_spec.rb +19 -4
  31. data/spec/cli/start_cli_spec.rb +76 -0
  32. data/spec/cli/version_cli_spec.rb +0 -4
  33. data/spec/cli/wait_cli_spec.rb +66 -0
  34. data/spec/config_spec.rb +2 -0
  35. data/spec/fixtures/show_droplet_inactive.json +13 -0
  36. data/spec/fixtures/show_droplets.json +1 -1
  37. data/spec/fixtures/show_droplets_inactive.json +35 -0
  38. data/spec/middleware/check_configuration_spec.rb +0 -3
  39. data/spec/middleware/check_credentials_spec.rb +0 -3
  40. data/spec/middleware/check_droplet_active_spec.rb +15 -0
  41. data/spec/middleware/check_droplet_inactive_spec.rb +15 -0
  42. data/spec/middleware/find_droplet_spec.rb +0 -3
  43. data/spec/middleware/inject_configuration_spec.rb +0 -3
  44. data/spec/middleware/ssh_droplet_spec.rb +19 -3
  45. data/spec/shared/environment.rb +3 -0
  46. data/spec/spec_helper.rb +5 -0
  47. data/tugboat.gemspec +2 -0
  48. metadata +42 -2
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  .bundle
7
7
  .rvmrc
8
8
  Gemfile.lock
9
+ coverage
9
10
  doc/*
10
11
  log/*
11
12
  pkg/*
@@ -1,4 +1,29 @@
1
- ## 0.0.5 (unreleased)
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
@@ -9,7 +9,9 @@ module Tugboat
9
9
 
10
10
  !check_unknown_options
11
11
 
12
- map "--version" => :version, "-v" => :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 => 64,
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 => 2676,
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
 
@@ -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
+