tugboat 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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
+