tugboat 0.0.1
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 +12 -0
- data/CONTRIBUTING.md +7 -0
- data/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/README.md +122 -0
- data/Rakefile +1 -0
- data/bin/tugboat +4 -0
- data/lib/tugboat/cli.rb +185 -0
- data/lib/tugboat/config.rb +76 -0
- data/lib/tugboat/middleware/ask_for_credentials.rb +21 -0
- data/lib/tugboat/middleware/base.rb +28 -0
- data/lib/tugboat/middleware/check_configuration.rb +18 -0
- data/lib/tugboat/middleware/check_credentials.rb +24 -0
- data/lib/tugboat/middleware/confirm_action.rb +18 -0
- data/lib/tugboat/middleware/create_droplet.rb +26 -0
- data/lib/tugboat/middleware/destroy_droplet.rb +23 -0
- data/lib/tugboat/middleware/find_droplet.rb +112 -0
- data/lib/tugboat/middleware/halt_droplet.rb +23 -0
- data/lib/tugboat/middleware/info_droplet.rb +37 -0
- data/lib/tugboat/middleware/inject_client.rb +19 -0
- data/lib/tugboat/middleware/inject_configuration.rb +16 -0
- data/lib/tugboat/middleware/list_droplets.rb +24 -0
- data/lib/tugboat/middleware/list_images.rb +29 -0
- data/lib/tugboat/middleware/restart_droplet.rb +23 -0
- data/lib/tugboat/middleware/snapshot_droplet.rb +25 -0
- data/lib/tugboat/middleware/ssh_droplet.rb +26 -0
- data/lib/tugboat/middleware.rb +132 -0
- data/lib/tugboat/version.rb +3 -0
- data/lib/tugboat.rb +6 -0
- data/spec/config_spec.rb +90 -0
- data/spec/spec_helper.rb +11 -0
- data/tugboat.gemspec +24 -0
- metadata +144 -0
data/.gitignore
ADDED
data/CONTRIBUTING.md
ADDED
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jack Pearkes
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# Tugboat
|
2
|
+
|
3
|
+
A command line tool for interacting with your [DigitalOcean](https://www.digitalocean.com/) droplets.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
gem install tugboat
|
8
|
+
|
9
|
+
## Configuration
|
10
|
+
|
11
|
+
Run the configuration utility, `tugboat authorize`. You can grab your keys
|
12
|
+
[here](https://www.digitalocean.com/api_access).
|
13
|
+
|
14
|
+
$ tugboat authorize
|
15
|
+
Enter your client key: foo
|
16
|
+
Enter your API key: bar
|
17
|
+
Enter your SSH key path (optional, defaults to ~/.ssh/id_rsa):
|
18
|
+
Enter your SSH user (optional, defaults to jack):
|
19
|
+
Authentication with DigitalOcean was successful!
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Retrieve a list of your droplets
|
24
|
+
|
25
|
+
$ tugboat droplets
|
26
|
+
pearkes-web-001 (ip: 30.30.30.1, status: active, region: 1, id: 13231511)
|
27
|
+
pearkes-admin-001 (ip: 30.30.30.3, status: active, region: 1, id: 13231512)
|
28
|
+
pearkes-api-001 (ip: 30.30.30.5, status: active, region: 1, id: 13231513)
|
29
|
+
|
30
|
+
### Fuzzy name matching
|
31
|
+
|
32
|
+
You can pass a unique fragment of a droplets name for interactions
|
33
|
+
throughout `tugboat`.
|
34
|
+
|
35
|
+
$ tugboat restart admin
|
36
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231512 (pearkes-admin-001)
|
37
|
+
Queuing restart for 13231512 (pearkes-admin-001)...done
|
38
|
+
|
39
|
+
tugboat handles multiple matches as well:
|
40
|
+
|
41
|
+
$ tugboat restart pearkes
|
42
|
+
Droplet fuzzy name provided. Finding droplet ID...Multiple droplets found.
|
43
|
+
|
44
|
+
0) pearkes-web-001 (13231511)
|
45
|
+
1) pearkes-admin-001 (13231512)
|
46
|
+
2) pearkes-api-001 (13231513)
|
47
|
+
|
48
|
+
Please choose a droplet: ["0", "1", "2"] 0
|
49
|
+
Queuing restart for 13231511 (pearkes-web-001)...done
|
50
|
+
|
51
|
+
### SSH into a droplet
|
52
|
+
|
53
|
+
You can configure a SSH username and key path in `tugboat authorize`.
|
54
|
+
|
55
|
+
This lets you ssh into a droplet by providing it's name, or a partial
|
56
|
+
match.
|
57
|
+
|
58
|
+
$ tugboat ssh admin
|
59
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231512 (pearkes-admin-001)
|
60
|
+
Executing SSH (pearkes-admin-001)...
|
61
|
+
Welcome to Ubuntu 12.10 (GNU/Linux 3.5.0-17-generic x86_64)
|
62
|
+
pearkes@pearkes-admin-001:~#
|
63
|
+
|
64
|
+
### Create a droplet
|
65
|
+
|
66
|
+
$ tugboat create pearkes-www-002 -s 64 -i 2676 -r 2
|
67
|
+
Queueing creation of droplet 'pearkes-www-002'...done
|
68
|
+
|
69
|
+
### Info about a droplet
|
70
|
+
|
71
|
+
$ tugboat info admin
|
72
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231512 (pearkes-admin-001)
|
73
|
+
|
74
|
+
Name: pearkes-admin-001
|
75
|
+
ID: 13231512
|
76
|
+
Status: active
|
77
|
+
IP: 30.30.30.3
|
78
|
+
Region ID: 1
|
79
|
+
Image ID: 25489
|
80
|
+
Size ID: 66
|
81
|
+
Backups Active: false
|
82
|
+
|
83
|
+
### Destroy a droplet
|
84
|
+
|
85
|
+
$ tugboat destroy pearkes-www-002
|
86
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231515 (pearkes-www-002)
|
87
|
+
Warning! Potentially destructive action. Please confirm [y/n]: y
|
88
|
+
Queuing destroy for 13231515 (pearkes-www-002)...done
|
89
|
+
|
90
|
+
### Restart a droplet
|
91
|
+
|
92
|
+
$ tugboat restart admin
|
93
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231512 (pearkes-admin-001)
|
94
|
+
Queuing restart for 13231512 (pearkes-admin-001)...done
|
95
|
+
|
96
|
+
### Shutdown a droplet
|
97
|
+
|
98
|
+
$ tugboat halt admin
|
99
|
+
Droplet fuzzy name provided. Finding droplet ID...done, 13231512 (pearkes-admin-001)
|
100
|
+
Queuing shutdown for 13231512 (pearkes-admin-001)...done
|
101
|
+
|
102
|
+
### Snapshot a droplet
|
103
|
+
|
104
|
+
$ tugboat snapshot admin test-admin-snaphot
|
105
|
+
Queuing snapshot 'test' for 13231512 (pearkes-admin-001)...done
|
106
|
+
|
107
|
+
## Help
|
108
|
+
|
109
|
+
If you're curious about command flags for a specific command, you can
|
110
|
+
ask tugboat about it.
|
111
|
+
|
112
|
+
$ tugboat help restart
|
113
|
+
|
114
|
+
|
115
|
+
For a complete overview of all of the available commands, run:
|
116
|
+
|
117
|
+
$ tugboat help
|
118
|
+
|
119
|
+
## Contributing
|
120
|
+
|
121
|
+
See the [contributing guide](CONTRIBUTING.md).
|
122
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/tugboat
ADDED
data/lib/tugboat/cli.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Tugboat
|
4
|
+
autoload :Middleware, "tugboat/middleware"
|
5
|
+
|
6
|
+
class CLI < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
ENV['THOR_COLUMNS'] = '120'
|
9
|
+
|
10
|
+
!check_unknown_options
|
11
|
+
|
12
|
+
desc "help [COMMAND]", "Describe commands or a specific command"
|
13
|
+
def help(meth=nil)
|
14
|
+
super
|
15
|
+
if !meth
|
16
|
+
say "To learn more or to contribute, please see github.com/pearkes/tugboat"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "authorize", "Authorize a DigitalOcean account with tugboat"
|
21
|
+
long_desc "This takes you through a workflow for adding configuration
|
22
|
+
details to tugboat. First, you are asked for your API and Client keys,
|
23
|
+
which are stored in ~/.tugboat.
|
24
|
+
|
25
|
+
You can retrieve your credentials from digitalocean.com/api_access.
|
26
|
+
|
27
|
+
Optionally, you can configure the default SSH key path and username
|
28
|
+
used for `tugboat ssh`. These default to '~/.ssh/id_rsa' and the
|
29
|
+
$USER environment variable.
|
30
|
+
"
|
31
|
+
def authorize
|
32
|
+
Middleware.sequence_authorize.call({})
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "droplets", "Retrieve a list of your droplets"
|
36
|
+
def droplets
|
37
|
+
Middleware.sequence_list_droplets.call({})
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "images", "Retrieve a list of your images"
|
41
|
+
method_option "global",
|
42
|
+
:type => :boolean,
|
43
|
+
:default => false,
|
44
|
+
:aliases => "-g",
|
45
|
+
:desc => "Show global images"
|
46
|
+
def images
|
47
|
+
Middleware.sequence_list_images.call({
|
48
|
+
"user_show_global_images" => options[:global],
|
49
|
+
})
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "ssh FUZZY_NAME", "SSH into a droplet"
|
53
|
+
method_option "id",
|
54
|
+
:type => :string,
|
55
|
+
:aliases => "-i",
|
56
|
+
:desc => "The ID of the droplet."
|
57
|
+
method_option "name",
|
58
|
+
:type => :string,
|
59
|
+
:aliases => "-n",
|
60
|
+
:desc => "The exact name of the droplet"
|
61
|
+
def ssh(name=nil)
|
62
|
+
Middleware.sequence_ssh_droplet.call({
|
63
|
+
"user_droplet_id" => options[:id],
|
64
|
+
"user_droplet_name" => options[:name],
|
65
|
+
"user_droplet_fuzzy_name" => name
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "create NAME", "Create a droplet."
|
70
|
+
method_option "size",
|
71
|
+
:type => :numeric,
|
72
|
+
:aliases => "-s",
|
73
|
+
:default => 64,
|
74
|
+
:desc => "The size_id of the droplet"
|
75
|
+
method_option "image",
|
76
|
+
:type => :numeric,
|
77
|
+
:aliases => "-i",
|
78
|
+
:default => 2676,
|
79
|
+
:desc => "The image_id of the droplet"
|
80
|
+
method_option "region",
|
81
|
+
:type => :numeric,
|
82
|
+
:aliases => "-r",
|
83
|
+
:default => 1,
|
84
|
+
:desc => "The region_id of the droplet"
|
85
|
+
def create(name)
|
86
|
+
Middleware.sequence_create_droplet.call({
|
87
|
+
"create_droplet_size_id" => options[:size],
|
88
|
+
"create_droplet_image_id" => options[:image],
|
89
|
+
"create_droplet_region_id" => options[:region],
|
90
|
+
"create_droplet_name" => name
|
91
|
+
})
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "destroy FUZZY_NAME", "Destroy a droplet"
|
95
|
+
method_option "id",
|
96
|
+
:type => :string,
|
97
|
+
:aliases => "-i",
|
98
|
+
:desc => "The ID of the droplet."
|
99
|
+
method_option "name",
|
100
|
+
:type => :string,
|
101
|
+
:aliases => "-n",
|
102
|
+
:desc => "The exact name of the droplet"
|
103
|
+
def destroy(name=nil)
|
104
|
+
Middleware.sequence_destroy_droplet.call({
|
105
|
+
"user_droplet_id" => options[:id],
|
106
|
+
"user_droplet_name" => options[:name],
|
107
|
+
"user_droplet_fuzzy_name" => name
|
108
|
+
})
|
109
|
+
end
|
110
|
+
|
111
|
+
desc "restart FUZZY_NAME", "Restart a droplet"
|
112
|
+
method_option "id",
|
113
|
+
:type => :string,
|
114
|
+
:aliases => "-i",
|
115
|
+
:desc => "The ID of the droplet."
|
116
|
+
method_option "name",
|
117
|
+
:type => :string,
|
118
|
+
:aliases => "-n",
|
119
|
+
:desc => "The exact name of the droplet"
|
120
|
+
def restart(name=nil)
|
121
|
+
Middleware.sequence_restart_droplet.call({
|
122
|
+
"user_droplet_id" => options[:id],
|
123
|
+
"user_droplet_name" => options[:name],
|
124
|
+
"user_droplet_fuzzy_name" => name
|
125
|
+
})
|
126
|
+
end
|
127
|
+
|
128
|
+
desc "halt FUZZY_NAME", "Shutdown a droplet"
|
129
|
+
method_option "id",
|
130
|
+
:type => :string,
|
131
|
+
:aliases => "-i",
|
132
|
+
:desc => "The ID of the droplet."
|
133
|
+
method_option "name",
|
134
|
+
:type => :string,
|
135
|
+
:aliases => "-n",
|
136
|
+
:desc => "The exact name of the droplet"
|
137
|
+
def halt(name=nil)
|
138
|
+
Middleware.sequence_halt_droplet.call({
|
139
|
+
"user_droplet_id" => options[:id],
|
140
|
+
"user_droplet_name" => options[:name],
|
141
|
+
"user_droplet_fuzzy_name" => name
|
142
|
+
})
|
143
|
+
end
|
144
|
+
|
145
|
+
desc "info FUZZY_NAME [OPTIONS]", "Show a droplet's information"
|
146
|
+
method_option "id",
|
147
|
+
:type => :string,
|
148
|
+
:aliases => "-i",
|
149
|
+
:desc => "The ID of the droplet."
|
150
|
+
method_option "name",
|
151
|
+
:type => :string,
|
152
|
+
:aliases => "-n",
|
153
|
+
:desc => "The exact name of the droplet"
|
154
|
+
def info(name=nil)
|
155
|
+
Middleware.sequence_info_droplet.call({
|
156
|
+
"user_droplet_id" => options[:id],
|
157
|
+
"user_droplet_name" => options[:name],
|
158
|
+
"user_droplet_fuzzy_name" => name
|
159
|
+
})
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "snapshot FUZZY_NAME [OPTIONS]", "Queue a snapshot of the droplet."
|
163
|
+
method_option "id",
|
164
|
+
:type => :string,
|
165
|
+
:aliases => "-i",
|
166
|
+
:desc => "The ID of the droplet."
|
167
|
+
method_option "name",
|
168
|
+
:type => :string,
|
169
|
+
:aliases => "-n",
|
170
|
+
:desc => "The exact name of the droplet"
|
171
|
+
method_option "snapshot",
|
172
|
+
:type => :string,
|
173
|
+
:aliases => "-s",
|
174
|
+
:desc => "The name of the snapshot"
|
175
|
+
def snapshot(name=nil, snapshot_name)
|
176
|
+
Middleware.sequence_snapshot_droplet.call({
|
177
|
+
"user_droplet_id" => options[:id],
|
178
|
+
"user_droplet_name" => options[:name],
|
179
|
+
"user_droplet_fuzzy_name" => name,
|
180
|
+
"user_snapshot_name" => snapshot_name
|
181
|
+
})
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Tugboat
|
4
|
+
# This is the configuration object. It reads in configuration
|
5
|
+
# from a .tugboat file located in the user's home directory
|
6
|
+
|
7
|
+
class Configuration
|
8
|
+
include Singleton
|
9
|
+
attr_reader :data
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
FILE_NAME = '.tugboat'
|
13
|
+
DEFAULT_SSH_KEY_PATH = '.ssh/id_rsa'
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@path = File.join(File.expand_path("~"), FILE_NAME)
|
17
|
+
@data = self.load_config_file
|
18
|
+
end
|
19
|
+
|
20
|
+
# If we can't load the config file, self.data is nil, which we can
|
21
|
+
# check for in CheckConfiguration
|
22
|
+
def load_config_file
|
23
|
+
require 'yaml'
|
24
|
+
YAML.load_file(@path)
|
25
|
+
rescue Errno::ENOENT
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
def client_key
|
30
|
+
@data['authentication']['client_key']
|
31
|
+
end
|
32
|
+
|
33
|
+
def api_key
|
34
|
+
@data['authentication']['api_key']
|
35
|
+
end
|
36
|
+
|
37
|
+
def ssh_key_path
|
38
|
+
@data['ssh']['ssh_key_path']
|
39
|
+
end
|
40
|
+
|
41
|
+
def ssh_user
|
42
|
+
@data['ssh']['ssh_user']
|
43
|
+
end
|
44
|
+
|
45
|
+
# Allow the path to be set.
|
46
|
+
def path=(path)
|
47
|
+
@path = path
|
48
|
+
path
|
49
|
+
end
|
50
|
+
|
51
|
+
# Re-runs initialize
|
52
|
+
def reset!
|
53
|
+
self.send(:initialize)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Writes a config file
|
57
|
+
def create_config_file(client, api, ssh_key_path, ssh_user)
|
58
|
+
# Default SSH Key path
|
59
|
+
if ssh_key_path.empty?
|
60
|
+
ssh_key_path = File.join(File.expand_path("~"), DEFAULT_SSH_KEY_PATH)
|
61
|
+
end
|
62
|
+
|
63
|
+
if ssh_user.empty?
|
64
|
+
ssh_user = ENV['USER']
|
65
|
+
end
|
66
|
+
|
67
|
+
require 'yaml'
|
68
|
+
File.open(@path, File::RDWR|File::TRUNC|File::CREAT, 0600) do |file|
|
69
|
+
data = {"authentication" => { "client_key" => client, "api_key" => api },
|
70
|
+
"ssh" => { "ssh_user" => ssh_user, "ssh_key_path" => ssh_key_path }}
|
71
|
+
file.write data.to_yaml
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Ask for user credentials from the command line, then write them out.
|
4
|
+
class AskForCredentials < Base
|
5
|
+
def call(env)
|
6
|
+
say "Note: You can get this information from digitalocean.com/api_access", :yellow
|
7
|
+
say
|
8
|
+
client_key = ask "Enter your client key:"
|
9
|
+
api_key = ask "Enter your API key:"
|
10
|
+
ssh_key_path = ask "Enter your SSH key path (optional, defaults to ~/.ssh/id_rsa):"
|
11
|
+
ssh_user = ask "Enter your SSH user (optional, defaults to #{ENV['USER']}):"
|
12
|
+
|
13
|
+
# Write the config file.
|
14
|
+
env['config'].create_config_file(client_key, api_key, ssh_key_path, ssh_user)
|
15
|
+
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# A base middleware class to initalize.
|
4
|
+
class Base
|
5
|
+
# Some colors for making things pretty.
|
6
|
+
CLEAR = "\e[0m"
|
7
|
+
RED = "\e[31m"
|
8
|
+
GREEN = "\e[32m"
|
9
|
+
YELLOW = "\e[33m"
|
10
|
+
|
11
|
+
# We want access to all of the fun thor cli helper methods,
|
12
|
+
# like say, yes?, ask, etc.
|
13
|
+
include Thor::Shell
|
14
|
+
|
15
|
+
def initialize(app)
|
16
|
+
@app = app
|
17
|
+
# This resets the color to "clear" on the user's terminal.
|
18
|
+
say "", :clear, false
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(env)
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
# Check if the client has set-up configuration yet.
|
4
|
+
class CheckConfiguration < Base
|
5
|
+
def call(env)
|
6
|
+
config = env["config"]
|
7
|
+
|
8
|
+
if !config || !config.data || !config.api_key || !config.client_key
|
9
|
+
say "You must run `tugboat authorize` in order to connect to DigitalOcean", :red
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Tugboat
|
4
|
+
module Middleware
|
5
|
+
# Check if the client can connect to the ocean
|
6
|
+
class CheckCredentials < Base
|
7
|
+
def call(env)
|
8
|
+
# We use a harmless API call to check if the authentication will
|
9
|
+
# work.
|
10
|
+
begin
|
11
|
+
env["ocean"].droplets.list
|
12
|
+
rescue
|
13
|
+
say "Authentication with DigitalOcean failed. Run `tugboat authorize`", :red
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
say "Authentication with DigitalOcean was successful.", :green
|
18
|
+
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class ConfirmAction < Base
|
4
|
+
def call(env)
|
5
|
+
response = yes? "Warning! Potentially destructive action. Please confirm [y/n]:"
|
6
|
+
|
7
|
+
if !response
|
8
|
+
say "Aborted due to user request.", :red
|
9
|
+
# Quit
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class CreateDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
say "Queueing creation of droplet '#{env["create_droplet_name"]}'...", nil, false
|
8
|
+
|
9
|
+
req = ocean.droplets.create :name => env["create_droplet_name"],
|
10
|
+
:size_id => env["create_droplet_size_id"],
|
11
|
+
:image_id => env["create_droplet_image_id"],
|
12
|
+
:region_id => env["create_droplet_region_id"]
|
13
|
+
|
14
|
+
if req.status == "ERROR"
|
15
|
+
say req.error_message, :red
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
say "done", :green
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tugboat
|
2
|
+
module Middleware
|
3
|
+
class DestroyDroplet < Base
|
4
|
+
def call(env)
|
5
|
+
ocean = env["ocean"]
|
6
|
+
|
7
|
+
say "Queuing destroy for #{env["droplet_id"]} #{env["droplet_name"]}...", nil, false
|
8
|
+
|
9
|
+
req = ocean.droplets.delete env["droplet_id"]
|
10
|
+
|
11
|
+
if req.status == "ERROR"
|
12
|
+
say "#{req.status}: #{req.error_message}", :red
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
say "done", :green
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|