c66 0.1.0 → 0.1.97

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of c66 might be problematic. Click here for more details.

checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OGMyZGVhZTc0NzBhNjFhM2NiNjNiMTA1OGJiNjMyNmQzZmIyZTMyNw==
5
+ data.tar.gz: !binary |-
6
+ YTc2Y2I3MWY1MzJlMjdhZmI1OGY5Y2JjMWIwOWQ4Nzk5YTU5ZDUyYQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZDdiZDJhODY3MzM4OTU5MWY3YWMxY2NiYWY4YjM1NWIwYjZhNGZkZTkzNTkw
10
+ NDAyYTMyZTZiMTlkNTlkOGJhZWU4NGFkNTU4MTA1NmE1MzkwMzViMzllZGQz
11
+ YWE4ZjVmNDcxN2ZiZjc1MWZhODkyMTk3NjRlZTU0MTk3MjVmMDQ=
12
+ data.tar.gz: !binary |-
13
+ ZWQ3NzAxOGMwOTM2ZTM3NTExMzNmNmFkN2ViOWEwY2E4OGZkYjQ0NTYzNmZl
14
+ ZjM5NjA1ZmQyMzZmNjY3YzM0ZjRkMDcxMDY5MzZhODUzZWNhNjNiY2U4M2U0
15
+ ZGFiYzI0NWEwNDdlMWIwZmE3MGI3NDVmN2EyMDBhZTA2N2U0YmQ=
data/.gitignore CHANGED
@@ -2,12 +2,8 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
5
  coverage
10
- doc/
6
+ InstalledFiles
11
7
  lib/bundler/man
12
8
  pkg
13
9
  rdoc
@@ -15,3 +11,16 @@ spec/reports
15
11
  test/tmp
16
12
  test/version_tmp
17
13
  tmp
14
+ .cloud66
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
21
+ .ruby-gemset
22
+ .ruby-version
23
+ /dev.sh
24
+ /.DS_Store
25
+
26
+ /source.sh
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ c66 (0.1.96)
5
+ bundler (~> 1.3)
6
+ httparty (~> 0.11.0)
7
+ json (~> 1.7.7)
8
+ oauth2 (~> 0.9.2)
9
+ rake (~> 10.1.0)
10
+ thor (~> 0.18.1)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ faraday (0.9.0)
16
+ multipart-post (>= 1.2, < 3)
17
+ httparty (0.11.0)
18
+ multi_json (~> 1.0)
19
+ multi_xml (>= 0.5.2)
20
+ json (1.7.7)
21
+ jwt (0.1.10)
22
+ multi_json (>= 1.5)
23
+ multi_json (1.8.4)
24
+ multi_xml (0.5.5)
25
+ multipart-post (2.0.0)
26
+ oauth2 (0.9.3)
27
+ faraday (>= 0.8, < 0.10)
28
+ jwt (~> 0.1.8)
29
+ multi_json (~> 1.3)
30
+ multi_xml (~> 0.5)
31
+ rack (~> 1.2)
32
+ rack (1.5.2)
33
+ rake (10.1.1)
34
+ thor (0.18.1)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ c66!
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Cloud66 Limited.
1
+ Copyright (c) 2013 - 2014 Cloud66 Limited.
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,111 +1,210 @@
1
- # Cloud 66 Toolbelt: c66
2
-
3
- Cloud 66 Toolbelt is a simple command-line tool for the awesome Cloud 66 customers. It allows you to deploy, modify settings and retrieve the current status of your Cloud 66 stacks, and much more!
1
+ <h1 class="doc-title">Cloud 66 Toolbelt</h1>
2
+ <p class="lead">Cloud 66 Toolbelt is a simple command-line tool for the awesome Cloud 66 customers. It allows you to deploy, modify settings and retrieve the current status of your Cloud 66 stacks, and much more!</p>
4
3
 
5
4
  ## Installation
6
5
 
7
6
  You can install the Cloud 66 Toolbelt using [RubyGems](http://rubygems.org/):
7
+ <p>
8
+ <kbd>$ gem install c66</kbd>
9
+ </p>
8
10
 
9
- $ gem install c66
10
-
11
- ## Usage
11
+ ## Help
12
12
 
13
- ### Help
14
13
  With c66 installed, you can display the help with one of the following instructions:
14
+ <p>
15
+ <kbd>$ c66 help</kbd>
16
+ </p>
17
+
18
+ or
15
19
 
16
- $ c66 help
20
+ <p>
21
+ <kbd>$ c66</kbd>
22
+ </p>
17
23
 
18
- or
24
+ or for a specific command:
19
25
 
20
- $ c66
26
+ <p>
27
+ <kbd>$ c66 help &lt;command&gt;</kbd>
28
+ </p>
21
29
 
22
- ### Initialize the Toolbelt
30
+ ## Initialize the Toolbelt
23
31
 
24
32
  Firstly, to use the Toolbelt, you will need to initiate it using:
25
33
 
26
- $ c66 init
27
-
34
+ <p>
35
+ <kbd>$ c66 init</kbd>
36
+ </p>
37
+
28
38
  Then visit the URL given once authorized, copy and paste the `authorization code` into the command-line interface.
29
-
30
39
  You need to sign in and allow the Cloud 66 Toolbelt application to use your account to access to the authorization code.
31
40
 
32
- Note: This is a one-off task.
41
+ **Note**: This is a one-off task.
33
42
 
34
- ### List the Stacks
43
+ ## List the Stacks
35
44
 
36
45
  You can list all your stacks using:
37
46
 
38
- $ c66 list
47
+ <p>
48
+ <kbd>$ c66 list</kbd>
49
+ </p>
39
50
 
40
- ### Deploy a Stack
51
+ ## Deploy a Stack
41
52
 
42
53
  Deploy a stack using the command `deploy` with a stack UID (Unique Identifer):
43
54
 
44
- $ c66 deploy --stack <stack_UID>
45
-
55
+ <p>
56
+ <kbd>$ c66 deploy --stack &lt;stack_UID&gt;</kbd>
57
+ </p>
58
+
46
59
  or
47
60
 
48
- $ c66 deploy -s <stack_UID>
49
-
61
+ <p>
62
+ <kbd>$ c66 deploy -s &lt;stack_UID&gt;</kbd>
63
+ </p>
64
+
50
65
  You can retrieve the UID of a stack using the `list` command.
51
-
52
66
  Through the Cloud 66 interface, click on your stack, then click on the cog and select the stack information view to retrieve the UID:
53
-
54
67
  ![stack_uid](http://cdn.cloud66.com.s3.amazonaws.com/images/Toolbelt/exemple_stack_uid.PNG)
55
68
 
56
- The stack UID is saved when you deploy through the Cloud 66 Toolbelt. It allows you to deploy a stack without putting the stack UID every time:
69
+ There is a command to save a default stack UID:
70
+
71
+ <p>
72
+ <kbd>$ c66 save --stack &lt;stack_UID&gt;</kbd>
73
+ </p>
74
+
75
+ or
76
+
77
+ <p>
78
+ <kbd>$ c66 save -s &lt;stack_UID&gt;</kbd>
79
+ </p>
80
+
81
+ **Note:** The stack is saved in your current folder (.cloud66/stack.json) and only one default stack will be saved per folder.
57
82
 
58
- $ c66 deploy
83
+ When your stack UID is saved, you are able to use other commands without specify the stack UID.
84
+ For instance, it allows you to deploy a stack without putting the stack UID every time:
59
85
 
86
+ <p>
87
+ <kbd>$ c66 deploy</kbd>
88
+ </p>
89
+
60
90
  you can use a short-cut for this command:
61
91
 
62
- $ c66 d
92
+ <p>
93
+ <kbd>$ c66 d</kbd>
94
+ </p>
95
+
96
+ You can save multiple stack UID by giving an alias to a specific stack:
97
+
98
+ <p>
99
+ <kbd>$ c66 save --stack &lt;stack_UID&gt; --alias &lt;stack_alias&gt;</kbd>
100
+ </p>
63
101
 
64
- ### Settings of a Stack
102
+ Then you can use commands and specific a stack's alias, like so:
103
+
104
+ <p>
105
+ <kbd>$ c66 deploy -s &lt;stack_alias&gt;</kbd>
106
+ </p>
107
+
108
+ ## Settings of a Stack
65
109
 
66
110
  It is possible to retrieve the settings of a specified stack and to easily modify them:
67
111
 
68
112
  To display the settings:
69
113
 
70
- $ c66 settings --stack <stack_UID>
114
+ <p>
115
+ <kbd>$ c66 settings --stack &lt;stack_UID&gt;</kbd>
116
+ </p>
117
+
118
+ or
119
+
120
+ <p>
121
+ <kbd>$ c66 settings -s &lt;stack_UID&gt;</kbd>
122
+ </p>
123
+
124
+ If a default stack UID is saved:
71
125
 
126
+ <p>
127
+ <kbd>$ c66 settings</kbd>
128
+ </p>
129
+
130
+ To modify a setting:
131
+
132
+ <p>
133
+ <kbd>$ c66 set --stack &lt;stack_UID&gt; --setting_name &lt;setting_name&gt; --value &lt;value&gt;</kbd>
134
+ </p>
135
+
72
136
  or
73
137
 
74
- $ c66 settings -s <stack_UID>
138
+ <p>
139
+ <kbd>$ c66 set -s &lt;stack_UID&gt; -n &lt;setting_name&gt; -v &lt;value&gt;</kbd>
140
+ </p>
141
+
142
+ If a default stack UID is saved:
75
143
 
76
- If your stack UID is saved:
144
+ <p>
145
+ <kbd>$ c66 set --setting_name &lt;setting_name&gt; --value &lt;value&gt;</kbd>
146
+ </p>
147
+
148
+ or
77
149
 
78
- $ c66 settings
150
+ <p>
151
+ <kbd>$ c66 set -n &lt;setting_name&gt; -v &lt;value&gt;</kbd>
152
+ </p>
79
153
 
80
- To modify a setting:
154
+ ## Lease an IP address (version &ge; 0.1.91)
81
155
 
82
- $ c66 set --stack <stack_UID> --setting_name <setting_name> --value <value>
156
+ You can allow an IP address to connect temporarily to the specific stack through ssh (22):
157
+
158
+ <p>
159
+ <kbd>$ c66 lease --stack &lt;stack_UID&gt; --ip-address &lt;ip_address&gt; --time-to-open &lt;time_to_open&gt;</kbd>
160
+ </p>
83
161
 
84
162
  or
85
163
 
86
- $ c66 set -s <stack_UID> -n <setting_name> -v <value>
164
+ <p>
165
+ <kbd>$ c66 lease -s &lt;stack_UID&gt; -i &lt;ip_address&gt; -t &lt;time_to_open&gt;</kbd>
166
+ </p>
87
167
 
88
- If the stack UID is saved:
168
+ Options *ip-address* and *time-to-open* are optional.
169
+ By default:
89
170
 
90
- $ c66 set --setting_name <setting_name> --value <value>
171
+ - *ip-address* : your IP address
172
+ - *time-to-open* : 20 minutes
91
173
 
92
- or
174
+ To allow your own IP address to connect temporarily to the specific stack:
93
175
 
94
- $ c66 set -n <setting_name> -v <value>
176
+ <p>
177
+ <kbd>$ c66 lease --stack &lt;stack_UID&gt;</kbd>
178
+ </p>
95
179
 
180
+ If a default stack UID is saved:
96
181
 
97
- ## Contributing
182
+ <p>
183
+ <kbd>$ c66 lease</kbd>
184
+ </p>
98
185
 
99
- 1. Fork it
186
+ ## Download your backup (version &ge; 0.1.91)
100
187
 
101
- 2. Create your feature branch (`git checkout -b my-new-feature`)
188
+ You can download a backup if you installed <a href="http://help.cloud66.com/stack-features/db-backup.html" target="_blank">managed backups</a> on your stack. This feature will concatenate separate files into one automatically if your backup consists of numerous files.
102
189
 
103
- 3. Commit your changes (`git commit -am 'Add some feature'`)
190
+ <p>
191
+ <kbd>$ c66 download_backup -b &lt;backup_id&gt;
192
+ </p>
104
193
 
105
- 4. Push to the branch (`git push origin my-new-feature`)
194
+ You can retrieve the backup ID by accessing the "Managed backups" page through the Cloud 66 interface. Click on your stack and then on the managed backup icon in front of your database group, and your backup IDs should be visible.
106
195
 
107
- 5. Create new Pull Request
196
+ ## Information of your toolbelt settings
108
197
 
109
- ## Copyright
198
+ At any time, you can see your toolbelt settings, it includes the version of the toolbelt but also some information about your saved stacks:
199
+
200
+ <p>
201
+ <kbd>$ c66 info</kbd>
202
+ </p>
110
203
 
111
- Copyright (c) 2013 Cloud66 Limited.. See LICENSE for details.
204
+ ## Contributing
205
+
206
+ 1. Fork it
207
+ 2. Create your feature branch `git checkout -b my-new-feature`
208
+ 3. Commit your changes `git commit -am 'Add some feature'`
209
+ 4. Push to the branch `git push origin my-new-feature`
210
+ 5. Create new Pull Request
@@ -8,6 +8,7 @@ module C66
8
8
  module Commands
9
9
 
10
10
  CLIENT_NAME = 'c66'
11
+ CLIENT_FULLNAME = 'Cloud 66 Toolbelt'
11
12
 
12
13
  STK_QUEUED = 0
13
14
  STK_SUCCESS = 1
@@ -18,6 +19,8 @@ module C66
18
19
  STK_DEPLOYING = 6
19
20
  STK_TERMINAL_FAILURE = 7
20
21
 
22
+ FORBIDDEN_STACKS_ALIAS = ['params', 'toolbelt']
23
+
21
24
  STATUS = {
22
25
  STK_QUEUED => 'Pending analysis',
23
26
  STK_SUCCESS => 'Deployed successfully',
@@ -30,16 +33,18 @@ module C66
30
33
  }
31
34
 
32
35
  VERSION_FILE = 'http://cdn.cloud66.com/config/cloud66_toolbelt.json'
33
- BASE_URL = 'https://www.cloud66.com'
36
+ BASE_URL = ENV['C66_API_ENDPOINT'] || 'https://www.cloud66.com'
37
+ CLIENT_ID = ENV['C66_CLIENT_ID'] || '638412995ee3da6f67e24564ac297f9554ee253a8fe1502348c4d6e845bd9d0d'
38
+ CLIENT_SECRET = ENV['C66_CLIENT_SECRET'] || '961398353aa6e7f0f36dfcd83e447d748c54481b7a3b143e0119441516e8b91f'
34
39
 
35
40
  class C66Toolbelt < Thor
36
41
  no_commands {
37
42
  def values
38
43
  @values ||=
39
44
  { :base_url => "#{BASE_URL}/api/2",
40
- :client_id => "638412995ee3da6f67e24564ac297f9554ee253a8fe1502348c4d6e845bd9d0d",
41
- :client_secret => "961398353aa6e7f0f36dfcd83e447d748c54481b7a3b143e0119441516e8b91f",
42
- :scope => "public redeploy",
45
+ :client_id => CLIENT_ID,
46
+ :client_secret => CLIENT_SECRET,
47
+ :scope => "public redeploy admin users jobs",
43
48
  :redirect_url => "urn:ietf:wg:oauth:2.0:oob",
44
49
  :auth_url => "#{BASE_URL}/oauth/authorize",
45
50
  :token_url => "#{BASE_URL}/oauth/token"
@@ -47,6 +52,7 @@ module C66
47
52
  end
48
53
 
49
54
  def base_url
55
+ load_params
50
56
  values[:base_url]
51
57
  end
52
58
 
@@ -74,8 +80,16 @@ module C66
74
80
  File.join(c66_path, "params.json")
75
81
  end
76
82
 
77
- def stack_file
78
- File.join(stack_path, "stack.json")
83
+ def stack_file(alias_name = nil)
84
+ if alias_name
85
+ if alias_name.match(/\w/)
86
+ File.join(stack_path, "#{alias_name}.json")
87
+ else
88
+ abort "#{alias_name} is an invalid alias."
89
+ end
90
+ else
91
+ File.join(stack_path, "stack.json")
92
+ end
79
93
  end
80
94
 
81
95
  def load_config
@@ -88,7 +102,7 @@ module C66
88
102
 
89
103
  def save_config
90
104
  if !File.directory?(c66_path)
91
- Dir.mkdir_p(c66_path)
105
+ Dir.mkdir(c66_path)
92
106
  end
93
107
 
94
108
  File.open(config_file,"w") do |f|
@@ -96,28 +110,27 @@ module C66
96
110
  end
97
111
  end
98
112
 
99
- def save_stack(stack_id)
100
- if !File.directory?(stack_path)
101
- Dir.mkdir(stack_path)
102
- end
103
- @stack_json = { :stack_id => stack_id}
104
- File.open(stack_file,"w") do |f|
105
- f.write(@stack_json.to_json)
113
+ def load_stack(alias_name)
114
+ if File.exists?(stack_file(alias_name))
115
+ if file = JSON.load(IO.read(stack_file(alias_name)))
116
+ if file.has_key? 'stack_id'
117
+ @stack = file['stack_id']
118
+ end
119
+ if file.has_key? 'stack_name' and !@stack.nil?
120
+ @stack_name = file['stack_name']
121
+ say "Stack #{@stack_name} loaded."
122
+ end
123
+ end
106
124
  end
107
- @stack = stack_id
108
- say "Stack #{stack_id} saved to #{stack_file}"
109
125
  end
110
126
 
111
- def load_stack
112
- if File.exists?(stack_file)
113
- if @stack = JSON.load(IO.read(stack_file))['stack_id']
114
- say "Stack #{@stack} loaded."
115
- else
116
- abort "No stack id found at #{stack_file}"
117
- end
118
- else
119
- say("No stack saved at #{stack_file}.")
120
- end
127
+ def abort_no_stack
128
+ abort "No stack provided or saved, please use '--stack' or '-s' option. "\
129
+ "You can also use the 'save' method with '--stack' or '-s' option."
130
+ end
131
+
132
+ def abort_no_server
133
+ abort "Cannot find the given server name in the stack."
121
134
  end
122
135
 
123
136
  def load_params
@@ -130,25 +143,40 @@ module C66
130
143
  if @params.has_key? 'base_url'
131
144
  values[:base_url] = @params['base_url']
132
145
  else
133
- abort "Missing 'base_url' parameter in #{params_file}"
146
+ abort "Missing 'base_url' parameter in #{params_file}"
134
147
  end
135
148
  if @params.has_key? 'client_id'
136
149
  values[:client_id] = @params['client_id']
137
150
  else
138
- abort "Missing 'client_id' parameter in #{params_file}"
151
+ abort "Missing 'client_id' parameter in #{params_file}"
139
152
  end
140
153
  if @params.has_key? 'client_secret'
141
154
  values[:client_secret] = @params['client_secret']
142
155
  else
143
- abort "Missing 'client_secret' parameter in #{params_file}"
156
+ abort "Missing 'client_secret' parameter in #{params_file}"
144
157
  end
145
- say "Parameters loaded."
158
+ #say "Parameters loaded."
146
159
  end
147
160
  end
148
161
 
149
- def get_stack(stack)
150
- save_stack(stack) if stack
151
- load_stack if stack.nil?
162
+ def get_stack(stack_id_or_alias_name)
163
+ if stack_id_or_alias_name && !File.exist?(stack_file(stack_id_or_alias_name))
164
+ @stack=stack_id_or_alias_name
165
+ else
166
+ load_stack(stack_id_or_alias_name)
167
+ end
168
+ end
169
+
170
+ def get_server_by_name(stack_id_or_alias_name, server_name)
171
+ get_stack(stack_id_or_alias_name)
172
+
173
+ # get stack servers
174
+ response = token.get("#{base_url}/stacks/#{@stack}/servers.json")
175
+ servers = parse_response(response)['response']
176
+ server = servers.select { |s| s['name'].downcase == server_name.downcase }
177
+
178
+ return nil if server.empty?
179
+ return server.first
152
180
  end
153
181
 
154
182
  def client
@@ -173,33 +201,60 @@ module C66
173
201
  end
174
202
  end
175
203
 
176
- def self.get_version
204
+ def error_message(error)
205
+ begin
206
+ if !error.response.parsed.nil?
207
+ if (error.response.parsed.has_key? 'details')
208
+ say error.response.parsed['details']
209
+ else
210
+ say error.response.parsed['error_description']
211
+ end
212
+ end
213
+ rescue => e
214
+ abort e.message
215
+ end
216
+ end
217
+
218
+ def get_version
177
219
  begin
178
- JSON.load(HTTParty.get(VERSION_FILE)).fetch("version")
220
+ JSON.load(HTTParty.get(VERSION_FILE).response.body).fetch("version")
179
221
  rescue => e
180
- puts "Failed to retrieve the latest version of Cloud 66 Toolbelt, please contact us"
222
+ say "Failed to retrieve the latest version of Cloud 66 Toolbelt, please contact us"
181
223
  end
182
224
  end
183
225
 
184
-
185
- def self.compare_versions
186
- result = C66::Utils::VERSION<=>get_version
226
+ def display_info
227
+ say "#{CLIENT_FULLNAME} version #{C66::Utils::VERSION}\n\n"
228
+ end
229
+
230
+ def compare_versions
231
+ result = C66::Utils::VERSION <=> Gem::Version.new(get_version)
187
232
  case result
188
233
  when 0..1
189
- #say "Version is up-to-date."
190
- when -1
191
- puts "There is a new version of Cloud66 Toolbelt. Pease run \"gem update #{CLIENT_NAME}\"."
234
+ #say "Version is up-to-date."
235
+ when -1
236
+ say "There is a new version of Cloud66 Toolbelt. Pease run \"gem update #{CLIENT_NAME}\".",:red
192
237
  end
193
- end
238
+ end
239
+
240
+ def before_each_action
241
+ compare_versions
242
+ # pending_intercom_messages
243
+ end
194
244
  }
195
245
 
196
- package_name "Cloud 66 Toolbelt"
197
- desc "init", "Initialize the toolbelt"
198
- map "d" => :deploy
246
+ package_name "#{CLIENT_FULLNAME}: version #{C66::Utils::VERSION}\n"
247
+
248
+ default_task :default
199
249
 
200
- compare_versions
201
-
250
+ desc "default", "hidden method", :hide => true
251
+ def default
252
+ before_each_action
253
+ help
254
+ end
202
255
 
256
+ desc "init", "Initialize the toolbelt"
257
+ map "d" => :deploy
203
258
  long_desc <<-LONGDESC
204
259
  Initialize Cloud 66 toolbelt
205
260
  LONGDESC
@@ -226,63 +281,269 @@ module C66
226
281
 
227
282
  desc "list", "Lists all the stacks"
228
283
  def list
229
- response = parse_response(token.get("#{base_url}/stacks.json"))
284
+ before_each_action
285
+ begin
286
+ response = parse_response(token.get("#{base_url}/stacks.json"))
230
287
 
231
- if response['count'] != 0
232
- response['response'].each do |stack|
233
- say "#{stack['name']} (#{stack['uid']}) : #{stack['environment']} - #{STATUS[stack['status']]}"
288
+ if response['count'] != 0
289
+ response['response'].each do |stack|
290
+ say "#{stack['name']} (#{stack['uid']}) : #{stack['environment']} - #{STATUS[stack['status']]}"
291
+ end
292
+ else
293
+ say "No stacks found"
234
294
  end
235
- else
236
- say "No stacks found"
295
+ rescue OAuth2::Error => e
296
+ error_message(e)
237
297
  end
238
298
  end
239
299
 
240
300
  desc "settings", "Get the list of settings for this stack"
241
301
  option :stack, :aliases => "-s", :required => false
242
- def settings()
302
+ def settings
303
+ before_each_action
243
304
  begin
244
305
  get_stack(options[:stack])
245
- abort "No stack provided or saved, please use '--stack' or '-s' option" if @stack.nil?
306
+ abort_no_stack if @stack.nil?
246
307
  response = token.get("#{base_url}/stacks/#{@stack}/settings.json")
247
308
  settings = JSON.parse(response.body)['response']
309
+ number_settings = JSON.parse(response.body)['count']
310
+ stack_details = parse_response(token.get("#{base_url}/stacks/#{@stack}.json"))
311
+ stack_name = stack_details['response']['name']
248
312
 
249
313
  abort "No settings found" if settings.nil?
250
-
314
+ say "Getting #{stack_name} settings:"
251
315
  settings.each do |setting|
252
- say "#{setting['key']}\t\t#{setting['value']}\t#{setting['readonly'] ? '(readonly)' : ''}\r\n"
316
+ say "#{setting['key'].ljust(20)}\t\t#{setting['value']}\t#{setting['readonly'] ? '(readonly)' : ''}\r\n"
253
317
  end
254
318
  rescue OAuth2::Error => e
255
- abort e.message
319
+ error_message(e)
256
320
  end
257
321
  end
258
322
 
323
+ desc "ssh", "Start a SSH shell terminal with the given server"
324
+ option :stack, :aliases => "-s", :required => false
325
+ option :server_name, :aliases => "-n", :required => true
326
+ def ssh
327
+ before_each_action
328
+ begin
329
+ get_stack(options[:stack])
330
+ abort_no_stack if @stack.nil?
331
+ server = get_server_by_name(options[:stack], options[:server_name])
332
+ abort_no_server if server.nil?
333
+ say "Found server #{server['uid']} with name #{options[:server_name]}"
334
+
335
+ # get the SSH key
336
+ say "Requesting the SSH keys"
337
+ response = token.get("#{base_url}/servers/#{server['uid']}/ssh_private_key.json")
338
+ prv_key = parse_response(response)['response']['private_key']
339
+
340
+ say "Opening firewall temporarily for this IP address"
341
+ lease
342
+
343
+ path = File.join(Dir.home, '.ssh', "server_#{server['name'].downcase}")
344
+
345
+ # save it to user directory
346
+ File.open(path, 'w') do |file|
347
+ file.write(prv_key)
348
+ file.chmod(0600)
349
+ end
350
+
351
+ # let's do it
352
+ say "ssh '#{server['user_name']}'@'#{server['address']}' -i '#{path}'"
353
+ system "ssh '#{server['user_name']}'@'#{server['address']}' -i '#{path}'"
354
+ rescue OAuth2::Error => e
355
+ error_message(e)
356
+ end
357
+ end
358
+
259
359
  desc "set", "Set the value of a specific setting"
260
360
  option :stack, :aliases => "-s", :required => false
261
361
  option :setting_name, :aliases => "-n", :required => true
262
362
  option :value, :aliases => "-v", :required => true
263
363
  def set()
364
+ before_each_action
264
365
  begin
265
366
  get_stack(options[:stack])
266
- abort "No stack provided or saved, please use '--stack' or '-s' option" if @stack.nil?
367
+ abort_no_stack if @stack.nil?
368
+ stack_details = parse_response(token.get("#{base_url}/stacks/#{@stack}.json"))
369
+ stack_name = stack_details['response']['name']
267
370
  response = token.post("#{base_url}/stacks/#{@stack}/setting.json", { :body => { :setting_name => options[:setting_name], :setting_value => options[:value] }})
268
- say "Setting applied" if JSON.parse(response.body)['response']['ok']
371
+ if JSON.parse(response.body)['response']['ok']
372
+ say "On #{stack_name}: applied value '#{options[:value]}' to setting '#{options[:setting_name]}'"
373
+ else
374
+ say JSON.parse(response.body)['response']['message']
375
+ end
269
376
  rescue OAuth2::Error => e
270
- abort e.message
377
+ error_message(e)
271
378
  end
272
379
  end
273
380
 
274
381
  desc "deploy", "Deploy the given stack"
275
382
  option :stack, :aliases => "-s", :required => false
276
- def deploy()
383
+ def deploy
384
+ before_each_action
277
385
  begin
278
386
  get_stack(options[:stack])
279
- abort "No stack provided or saved, please use '--stack' or '-s' option" if @stack.nil?
387
+ abort_no_stack if @stack.nil?
388
+ stack_details = parse_response(token.get("#{base_url}/stacks/#{@stack}.json"))
389
+ stack_name = stack_details['response']['name']
390
+ say stack_name+": "
280
391
  response = token.post("#{base_url}/stacks/#{@stack}/redeploy.json", {})
281
392
  say JSON.parse(response.body)['response']['message']
282
393
  rescue OAuth2::Error => e
283
- abort "Could't find your stack, maybe it was moved, deleted or you accidentally mistyped the stack ID"
394
+ error_message(e)
395
+ end
396
+ end
397
+
398
+ desc "restart", "Restart the given stack"
399
+ option :stack, :aliases => "-s", :required => false
400
+ option :target, :aliases => "-t", :required => false
401
+ def restart
402
+ before_each_action
403
+ begin
404
+ get_stack(options[:stack])
405
+ abort_no_stack if @stack.nil?
406
+
407
+ target_option = options[:target]
408
+ target = target_option.nil? ? 'web' : target_option.to_s
409
+ abort "Only 'web' target is currently supported" unless target == 'web'
410
+
411
+ stack_details = parse_response(token.get("#{base_url}/stacks/#{@stack}.json"))
412
+ stack_name = stack_details['response']['name']
413
+ say stack_name+": "
414
+ response = token.post("#{base_url}/stacks/#{@stack}/restart.json", {})
415
+ say JSON.parse(response.body)['response']['message']
416
+
417
+ rescue OAuth2::Error => e
418
+ error_message(e)
419
+ end
420
+ end
421
+
422
+ desc "save", "Save the given stack information in the current directory"
423
+ option :stack, :aliases => "-s", :required => true
424
+ option :alias, :aliases => "-a", :required => false
425
+ def save
426
+ before_each_action
427
+ begin
428
+ stack_details = parse_response(token.get("#{base_url}/stacks/#{options[:stack]}.json"))
429
+ stack_name = stack_details['response']['name']
430
+ if !File.directory?(stack_path)
431
+ Dir.mkdir(stack_path)
432
+ end
433
+ @stack_json = { :stack_id => options[:stack], :stack_name => stack_name}
434
+ if (!FORBIDDEN_STACKS_ALIAS.include? options[:alias])
435
+ File.open(stack_file(options[:alias]),"w") do |f|
436
+ f.write(@stack_json.to_json)
437
+ end
438
+ else
439
+ abort 'Stack alias "'+options[:alias]+'" is forbidden, please retry with another alias.'
440
+ end
441
+ @stack = options[:stack]
442
+
443
+ say "Linked stack #{stack_name} to #{stack_file(options[:alias])}.\n"
444
+ if !options[:alias]
445
+ say "You are now able to use other commands without specify the stack UID."
446
+ else
447
+ say "You are now able to use other commands and specific this stack's alias, like so: "\
448
+ "`c66 deploy -s #{options[:alias]}`"
449
+ end
450
+ rescue OAuth2::Error => e
451
+ error_message(e)
452
+ end
453
+ end
454
+
455
+ desc "info", "#{CLIENT_FULLNAME} information"
456
+ def info
457
+ before_each_action
458
+ begin
459
+ say "#{CLIENT_FULLNAME} version #{C66::Utils::VERSION}\n\n"
460
+ Dir.glob("#{stack_path}/*.json") do |stack_file|
461
+ stack_alias = File.basename(stack_file, ".json")
462
+ if (!FORBIDDEN_STACKS_ALIAS.include? stack_alias)
463
+ load_stack(stack_alias)
464
+ if stack_alias == "stack"
465
+ say "Default stack: no alias"
466
+ else
467
+ say "Alias: #{stack_alias}"
468
+ end
469
+ stack_details = parse_response(token.get("#{base_url}/stacks/#{@stack}.json"))
470
+ say "Name: #{stack_details['response']['name']}"
471
+ say "UID: #{stack_details['response']['uid']}"
472
+ say "Environment: #{stack_details['response']['environment']}"
473
+ say "Status: #{STATUS[stack_details['response']['status']]}\n\n"
474
+ end
475
+ end
476
+ rescue OAuth2::Error => e
477
+ puts "Didn't find any valid stacks, please use the 'save' method."
478
+ error_message(e)
284
479
  end
285
480
  end
481
+
482
+ desc "lease", "Allow an IP address to connect temporarily to the specified stack through the specified port - the default port is ssh (22)"
483
+ option :stack, :aliases => "-s", :required => false
484
+ option :ip_address, :aliases => "-i", :required => false
485
+ option :time_to_open, :aliases => "-t", :required => false, :default => 20
486
+ option :port, :aliases => "-p", :required => false, :default => 22
487
+ option :show_ip, :aliases => "-a", :required => false
488
+ def lease()
489
+ before_each_action
490
+ begin
491
+ abort "time_to_open value is invalid. The value must be an integer between 0 and 240 (~4 hours)." unless (0..240).include?(options[:time_to_open].to_i)
492
+ port = options.fetch(:port) { 22 }
493
+ abort "port value is invalid. The value must be a valid port." unless port.to_i > 0
494
+ get_stack(options[:stack])
495
+ abort_no_stack if @stack.nil?
496
+ response = token.post("#{base_url}/stacks/#{@stack}/lease.json", { :body => { :ip_address => options[:ip_address], :time_to_open => options[:time_to_open], :port => port }})
497
+ say JSON.parse(response.body)['response']['message'] if JSON.parse(response.body)['response']['ok']
498
+
499
+ if options[:show_ip]
500
+ server_groups = token.get("#{base_url}/stacks/#{@stack}/server_groups.json")
501
+ rails_server_group = JSON.parse(server_groups.body)['response'].find { |sg| sg['name'] == 'Rails Server' }
502
+ servers = token.get("#{base_url}/stacks/#{@stack}/server_groups/#{rails_server_group['id']}/servers.json")
503
+ server_ip_address = JSON.parse(servers.body)['response'][0]['address']
504
+ say "For reference, here's the IP address for your first Rails server: #{server_ip_address}"
505
+ end
506
+ rescue OAuth2::Error => e
507
+ error_message(e)
508
+ end
509
+ end
510
+
511
+ desc "download_backup","Download a backup"
512
+ option :backup_id, :aliases => "-b", :required => true
513
+ def download_backup()
514
+ before_each_action
515
+ begin
516
+ next_extension = nil
517
+ downloaded_files = []
518
+ begin
519
+ response = parse_response(token.get("#{base_url}/backups/#{options[:backup_id]}/export/#{next_extension.nil? ? '' : next_extension }"))
520
+ file_name = response['response']['file_name']
521
+ unless file_name.nil?
522
+ File.open(file_name, "wb") do |f|
523
+ f.write HTTParty.get(response['response']['url']).parsed_response
524
+ end
525
+ downloaded_files << file_name
526
+ end
527
+ next_extension = response['response']['next_extension']
528
+ end while (!next_extension.nil?)
529
+
530
+ if downloaded_files.size > 0
531
+ if downloaded_files.size > 1
532
+ File.open("#{options[:backup_id]}.tar", "wb") do |output_f|
533
+ downloaded_files.sort.each {|f| output_f.write(File.open(f, 'r').read)}
534
+ end
535
+ downloaded_files.each {|f| File.delete f }
536
+ else
537
+ File.rename downloaded_files.first, "#{options[:backup_id]}.tar"
538
+ end
539
+ say "Your backup is downloaded : #{options[:backup_id]}.tar"
540
+ else
541
+ say "There is no file associated to #{options[:backup_id]}"
542
+ end
543
+ rescue OAuth2::Error => e
544
+ error_message(e)
545
+ end
546
+ end
286
547
  end
287
548
  end
288
549
  end
@@ -1,6 +1,7 @@
1
1
  module C66
2
2
  module Utils
3
- VERSION = "0.1.0"
3
+ #Version format
4
+ VERSION = Gem::Version.new("0.1.97")
4
5
  end
5
6
  end
6
7
 
metadata CHANGED
@@ -1,96 +1,114 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: c66
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.1.97
6
5
  platform: ruby
7
6
  authors:
8
7
  - Cloud 66
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-07-10 00:00:00.000000000 Z
11
+ date: 2014-03-14 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
- requirement: &70093107966800 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
19
  version: '1.3'
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *70093107966800
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: rake
27
- requirement: &70093107966180 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - ~>
31
32
  - !ruby/object:Gem::Version
32
33
  version: 10.1.0
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *70093107966180
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 10.1.0
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: thor
38
- requirement: &70093107965480 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
45
  - - ~>
42
46
  - !ruby/object:Gem::Version
43
47
  version: 0.18.1
44
48
  type: :runtime
45
49
  prerelease: false
46
- version_requirements: *70093107965480
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.18.1
47
55
  - !ruby/object:Gem::Dependency
48
56
  name: oauth2
49
- requirement: &70093107964520 !ruby/object:Gem::Requirement
50
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
51
58
  requirements:
52
59
  - - ~>
53
60
  - !ruby/object:Gem::Version
54
61
  version: 0.9.2
55
62
  type: :runtime
56
63
  prerelease: false
57
- version_requirements: *70093107964520
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.2
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: json
60
- requirement: &70093107963280 !ruby/object:Gem::Requirement
61
- none: false
71
+ requirement: !ruby/object:Gem::Requirement
62
72
  requirements:
63
73
  - - ~>
64
74
  - !ruby/object:Gem::Version
65
75
  version: 1.7.7
66
76
  type: :runtime
67
77
  prerelease: false
68
- version_requirements: *70093107963280
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.7.7
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: httparty
71
- requirement: &70093107962820 !ruby/object:Gem::Requirement
72
- none: false
85
+ requirement: !ruby/object:Gem::Requirement
73
86
  requirements:
74
87
  - - ~>
75
88
  - !ruby/object:Gem::Version
76
89
  version: 0.11.0
77
90
  type: :runtime
78
91
  prerelease: false
79
- version_requirements: *70093107962820
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 0.11.0
80
97
  description: See https://www.cloud66.com for more info
81
98
  email:
82
99
  - hello@cloud66.com
83
100
  executables:
84
- - c66.rb
101
+ - c66
85
102
  extensions: []
86
103
  extra_rdoc_files: []
87
104
  files:
88
105
  - .gitignore
89
106
  - Gemfile
107
+ - Gemfile.lock
90
108
  - LICENSE.txt
91
109
  - README.md
92
110
  - Rakefile
93
- - bin/c66.rb
111
+ - bin/c66
94
112
  - c66.gemspec
95
113
  - lib/c66.rb
96
114
  - lib/c66/commands/c66_toolbelt.rb
@@ -98,26 +116,25 @@ files:
98
116
  homepage: https://www.cloud66.com
99
117
  licenses:
100
118
  - MIT
119
+ metadata: {}
101
120
  post_install_message:
102
121
  rdoc_options: []
103
122
  require_paths:
104
123
  - lib
105
124
  required_ruby_version: !ruby/object:Gem::Requirement
106
- none: false
107
125
  requirements:
108
126
  - - ! '>='
109
127
  - !ruby/object:Gem::Version
110
128
  version: '0'
111
129
  required_rubygems_version: !ruby/object:Gem::Requirement
112
- none: false
113
130
  requirements:
114
131
  - - ! '>='
115
132
  - !ruby/object:Gem::Version
116
133
  version: '0'
117
134
  requirements: []
118
135
  rubyforge_project:
119
- rubygems_version: 1.8.15
136
+ rubygems_version: 2.1.11
120
137
  signing_key:
121
- specification_version: 3
138
+ specification_version: 4
122
139
  summary: Cloud 66 Toolbelt
123
140
  test_files: []
File without changes