ruby-triton 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.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +21 -0
- data/example.rb +17 -0
- data/lib/ruby-triton.rb +5 -0
- data/lib/ruby-triton/cloudapi_client.rb +1665 -0
- data/lib/ruby-triton/version.rb +3 -0
- data/ruby-triton.gemspec +31 -0
- data/test/unit/triton_client_test.rb +0 -0
- metadata +109 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 6ec6c7ecda93e72915f6370e0b5c8d89622a6993
|
|
4
|
+
data.tar.gz: ed330e27e36e6272b66360ae0a05b57c73982796
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ed8ef30ad0b7f605c6ff9c1ce5bd77b70e52a579f437c2cac38ba9f321f2bf37253306c0030114a9e9668f3ab8c93cce7ab94f36850d5194a7837a4eaf5cd241
|
|
7
|
+
data.tar.gz: 425d74abe4b5291e26d71db8e3172941ba824c35f7f90e953127c99537bdba995d5fda4890704134c1c1c4ef05812bf560fcec63119acc77d09c9fcd5ec044b9
|
data/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
The MIT License (MIT) Copyright (c) 2016 Les Technologies Alesium Inc.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
ruby-triton
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
What's ruby-triton?
|
|
5
|
+
------------------
|
|
6
|
+
|
|
7
|
+
ruby-triton is a client for communicating with cloudapi from Triton container
|
|
8
|
+
services to interact with the datacenter.
|
|
9
|
+
|
|
10
|
+
Acknolegement
|
|
11
|
+
=============
|
|
12
|
+
|
|
13
|
+
Joyent for creating Triton cloudapi
|
|
14
|
+
@dekobon for creating ruby-manta which was used as the base for this.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
License
|
|
18
|
+
=======
|
|
19
|
+
|
|
20
|
+
(c) 2016 Les Technologies Alesium Inc, licensed under MIT. See LICENSE for
|
|
21
|
+
details, you legal geek you.
|
data/example.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative 'lib/ruby-triton'
|
|
2
|
+
|
|
3
|
+
host = ENV['SDC_URL']
|
|
4
|
+
account = ENV['SDC_ACCOUNT']
|
|
5
|
+
#subuser = ENV['SDC_USER']
|
|
6
|
+
priv_key = ENV['SDC_KEY' ]
|
|
7
|
+
|
|
8
|
+
priv_key_data = File.read(priv_key)
|
|
9
|
+
|
|
10
|
+
client = RubyTriton::CloudApiClient.new(host, account, priv_key_data,
|
|
11
|
+
:verify_ssl => false,
|
|
12
|
+
# :subuser => 'monte',
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
client.list_machines(:name => "lala").each do | instances |
|
|
16
|
+
puts instances.inspect
|
|
17
|
+
end
|
data/lib/ruby-triton.rb
ADDED
|
@@ -0,0 +1,1665 @@
|
|
|
1
|
+
# Copyright (c) 2016, Les Technologies Alesium, Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# ruby-triton is a simple low-abstraction layer which communicates with Joyent's
|
|
4
|
+
# Triton container as a service.
|
|
5
|
+
#
|
|
6
|
+
# ruby-triton should be thread-safe, and supports pooling of keep-alive
|
|
7
|
+
# connections to the same server (through HTTPClient). It only relies on the
|
|
8
|
+
# standard library and two pure Ruby libraries, so it should work anywhere.
|
|
9
|
+
#
|
|
10
|
+
# For more information about Triton and general ruby-triton usage, please see
|
|
11
|
+
# README.md.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
require 'openssl'
|
|
16
|
+
require 'net/ssh'
|
|
17
|
+
require 'rest-client'
|
|
18
|
+
require 'base64'
|
|
19
|
+
require 'digest'
|
|
20
|
+
require 'time'
|
|
21
|
+
require 'json'
|
|
22
|
+
require 'cgi'
|
|
23
|
+
require 'uri'
|
|
24
|
+
|
|
25
|
+
require File.expand_path('../version', __FILE__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
module RubyTriton
|
|
29
|
+
class CloudApiClient
|
|
30
|
+
DEFAULT_ATTEMPTS = 3
|
|
31
|
+
DEFAULT_OPEN_TIMEOUT = 20
|
|
32
|
+
DEFAULT_READ_TIMEOUT = 20
|
|
33
|
+
DEFAULT_VERIFY_SSL = OpenSSL::SSL::VERIFY_PEER
|
|
34
|
+
|
|
35
|
+
MAX_LIMIT = 1000
|
|
36
|
+
HTTP_AGENT = "ruby-triton/#{VERSION} (#{RUBY_PLATFORM}; #{OpenSSL::OPENSSL_VERSION}) ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
|
37
|
+
HTTP_SIGNATURE = 'Signature keyId="/%s/keys/%s",algorithm="%s",signature="%s"'
|
|
38
|
+
|
|
39
|
+
CLOUDAPI_VERSION = 8
|
|
40
|
+
## TODO: Add this check
|
|
41
|
+
#CLOUDAPI_PATH_REGEX = Regexp.new('^/[^/]+(?:/?/$|/keys|/users|/roles|/jobs)(?:/|$)')
|
|
42
|
+
|
|
43
|
+
ERROR_CLASSES = [ 'BadRequest', 'InternalError', 'InUseError',
|
|
44
|
+
'InvalidArgument', 'InvalidCredentials', 'InvalidHeader',
|
|
45
|
+
'InvalidVersion', 'MissingParameter', 'NotAuthorized',
|
|
46
|
+
'RequestThrottled', 'RequestTooLarge', 'RequestMoved',
|
|
47
|
+
'ResourceNotFound', 'UnknownError']
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Initialize a MantaClient instance.
|
|
53
|
+
#
|
|
54
|
+
# priv_key_data is data read directly from an SSH private key (i.e. RFC 4716
|
|
55
|
+
# format). The method can also accept several optional args: :connect_timeout,
|
|
56
|
+
# :send_timeout, :receive_timeout, :disable_ssl_verification and :attempts.
|
|
57
|
+
# The timeouts are in seconds, and :attempts determines the default number of
|
|
58
|
+
# attempts each method will make upon receiving recoverable errors.
|
|
59
|
+
#
|
|
60
|
+
# Will throw an exception if given a key whose format it doesn't understand.
|
|
61
|
+
def initialize(host, account, priv_key_data, opts = {})
|
|
62
|
+
raise ArgumentError unless host =~ /^https{0,1}:\/\/.*[^\/]/
|
|
63
|
+
raise ArgumentError unless account.is_a?(String) && account.size > 0
|
|
64
|
+
|
|
65
|
+
@host = host
|
|
66
|
+
@account = account
|
|
67
|
+
@subuser = opts[:subuser] ? opts[:subuser] : nil
|
|
68
|
+
|
|
69
|
+
@attempts = opts[:attempts] || DEFAULT_ATTEMPTS
|
|
70
|
+
raise ArgumentError unless @attempts > 0
|
|
71
|
+
|
|
72
|
+
if priv_key_data =~ /BEGIN RSA/
|
|
73
|
+
@digest = OpenSSL::Digest::SHA1.new
|
|
74
|
+
@digest_name = 'rsa-sha1'
|
|
75
|
+
algorithm = OpenSSL::PKey::RSA
|
|
76
|
+
elsif priv_key_data =~ /BEGIN DSA/
|
|
77
|
+
@digest = OpenSSL::Digest::DSS1.new
|
|
78
|
+
@digest_name = 'dsa-sha1'
|
|
79
|
+
algorithm = OpenSSL::PKey::DSA
|
|
80
|
+
else
|
|
81
|
+
raise UnsupportedKey
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
@priv_key = algorithm.new(priv_key_data)
|
|
85
|
+
@fingerprint = OpenSSL::Digest::MD5.hexdigest(@priv_key.to_blob).
|
|
86
|
+
scan(/../).join(':')
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if opts[:verify_ssl] == false
|
|
90
|
+
verify_ssl = OpenSSL::SSL::VERIFY_NONE
|
|
91
|
+
end
|
|
92
|
+
@client = RestClient::Resource.new(@host,
|
|
93
|
+
:open_timeout => opts[:open_timeout] || DEFAULT_OPEN_TIMEOUT,
|
|
94
|
+
:read_timeout => opts[:read_timeout] || DEFAULT_READ_TIMEOUT,
|
|
95
|
+
:verify_ssl => verify_ssl || DEFAULT_VERIFY_SSL
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Account
|
|
101
|
+
##
|
|
102
|
+
|
|
103
|
+
# You can obtain your account details and update them through CloudAPI,
|
|
104
|
+
# although login cannot be changed, and password can not be retrieved.
|
|
105
|
+
#
|
|
106
|
+
# Retrieves your account details. Instead of providing your login name, you
|
|
107
|
+
# can also provide 'my' (i.e. GET /my).
|
|
108
|
+
def get_account(opts= {})
|
|
109
|
+
c = @client["#{@account}"]
|
|
110
|
+
headers = gen_headers(opts)
|
|
111
|
+
attempt(opts[:attempts]) do
|
|
112
|
+
do_get(c, headers)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Update your account details with the given parameters.
|
|
117
|
+
#
|
|
118
|
+
# ==== Options
|
|
119
|
+
#
|
|
120
|
+
# * +:email+ - String
|
|
121
|
+
# * +:companyName+ - String
|
|
122
|
+
# * +:firstName - String
|
|
123
|
+
# * +:lastName - String
|
|
124
|
+
# * +:address - String
|
|
125
|
+
# * +:postalCode - String
|
|
126
|
+
# * +:city - String
|
|
127
|
+
# * +:state - String
|
|
128
|
+
# * +:country - String
|
|
129
|
+
# * +:phone - String
|
|
130
|
+
# * +:triton_cns_enabled - Boolean
|
|
131
|
+
def update_account(opts= {})
|
|
132
|
+
c = @client["#{@account}"]
|
|
133
|
+
headers = gen_headers(opts)
|
|
134
|
+
attempt(opts[:attempts]) do
|
|
135
|
+
do_post(c, headers, opts)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
##
|
|
140
|
+
# Keys
|
|
141
|
+
##
|
|
142
|
+
|
|
143
|
+
# This part of the API is the means by which you operate on your SSH/signing
|
|
144
|
+
# keys. These keys are needed in order to login to instances over SSH, as
|
|
145
|
+
# well as signing requests to this API.
|
|
146
|
+
#
|
|
147
|
+
# Currently CloudAPI supports uploads of public keys in the OpenSSH format.
|
|
148
|
+
#
|
|
149
|
+
# Note that while it's possible to provide a name attribute for an SSH key
|
|
150
|
+
# in order to use it as a human-friendly alias, this attribute's presence is
|
|
151
|
+
# optional. When it's not provided, the ssh key fingerprint will be used as
|
|
152
|
+
# the name instead.
|
|
153
|
+
#
|
|
154
|
+
# For the following routes, the parameter placeholder :key can be replaced
|
|
155
|
+
# with with either the key's name or its fingerprint. It's strongly
|
|
156
|
+
# recommended to use fingerprint when possible, since the name attribute does
|
|
157
|
+
# not have any uniqueness constraints.
|
|
158
|
+
#
|
|
159
|
+
# Lists all public keys we have on record for the specified account.
|
|
160
|
+
def list_keys(opts= {})
|
|
161
|
+
c = @client["#{@user_path}/keys"]
|
|
162
|
+
headers = gen_headers(opts)
|
|
163
|
+
attempt(opts[:attempts]) do
|
|
164
|
+
do_get(c, headers)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Retrieves the record for an individual key.
|
|
169
|
+
#
|
|
170
|
+
# ==== Attributes
|
|
171
|
+
#
|
|
172
|
+
# * +:key String - Public key in OpenSSH format
|
|
173
|
+
def get_key(key, opts= {})
|
|
174
|
+
raise ArgumentError unless key.is_a? String
|
|
175
|
+
c = @client["#{@user_path}/keys/#{key}"]
|
|
176
|
+
headers = gen_headers(opts)
|
|
177
|
+
attempt(opts[:attempts]) do
|
|
178
|
+
do_get(c, headers)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Uploads a new OpenSSH key to Triton for use in HTTP signing and SSH.
|
|
183
|
+
#
|
|
184
|
+
# ==== Attributes
|
|
185
|
+
#
|
|
186
|
+
# * +:key String - Public key in OpenSSH format
|
|
187
|
+
#
|
|
188
|
+
# ==== Options
|
|
189
|
+
#
|
|
190
|
+
# * +:name - String Name for this key
|
|
191
|
+
def create_key(key, opts= {})
|
|
192
|
+
raise ArgumentError unless key.is_a? String
|
|
193
|
+
c = @client["#{@user_path}/keys"]
|
|
194
|
+
headers = gen_headers(opts)
|
|
195
|
+
opts[:key] = key
|
|
196
|
+
attempt(opts[:attempts]) do
|
|
197
|
+
do_post(c, headers, opts)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Deletes a single SSH key, by name or fingerprint.
|
|
202
|
+
#
|
|
203
|
+
# ==== Attributes
|
|
204
|
+
#
|
|
205
|
+
# * +:key String - Public key in OpenSSH format
|
|
206
|
+
def delete_key(key, opts= {})
|
|
207
|
+
raise ArgumentError unless key.is_a? String
|
|
208
|
+
c = @client["#{@user_path}/keys/#{key}"]
|
|
209
|
+
headers = gen_headers(opts)
|
|
210
|
+
attempt(opts[:attempts]) do
|
|
211
|
+
do_delete(c, headers)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
##
|
|
216
|
+
# Users
|
|
217
|
+
##
|
|
218
|
+
# These are users (also known as sub-users); additional users who are
|
|
219
|
+
# authorized to use the same account, but are subject to the RBAC system.
|
|
220
|
+
|
|
221
|
+
# Returns a list of an account's user objects. These have the same format
|
|
222
|
+
# as the main account object.
|
|
223
|
+
def list_users(opts= {})
|
|
224
|
+
c = @client["#{@account}/users"]
|
|
225
|
+
headers = gen_headers(opts)
|
|
226
|
+
attempt(opts[:attempts]) do
|
|
227
|
+
do_get(c, headers)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Get one user for an account.
|
|
232
|
+
#
|
|
233
|
+
# ==== Attributes
|
|
234
|
+
#
|
|
235
|
+
# * +:user String - User name
|
|
236
|
+
def get_user(user, opts = {})
|
|
237
|
+
raise unless user.is_a? String
|
|
238
|
+
c = @client["#{@account}/users/#{user}"]
|
|
239
|
+
headers = gen_headers(opts)
|
|
240
|
+
attempt(opts[:attempts]) do
|
|
241
|
+
do_get(c, headers)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Creates a new user under an account.
|
|
247
|
+
#
|
|
248
|
+
# ==== Attributes
|
|
249
|
+
#
|
|
250
|
+
# * +:email String - (Required) Email address
|
|
251
|
+
# * +:login String - (Required) Login
|
|
252
|
+
# * +:password String - (Required) Password
|
|
253
|
+
#
|
|
254
|
+
# ==== Options
|
|
255
|
+
#
|
|
256
|
+
# * +:companyName+ - String
|
|
257
|
+
# * +:firstName - String
|
|
258
|
+
# * +:lastName - String
|
|
259
|
+
# * +:address - String
|
|
260
|
+
# * +:postalCode - String
|
|
261
|
+
# * +:city - String
|
|
262
|
+
# * +:state - String
|
|
263
|
+
# * +:country - String
|
|
264
|
+
# * +:phone - String
|
|
265
|
+
def create_user(email, login, password, opts = {})
|
|
266
|
+
raise ArgumentError unless email.is_a? String
|
|
267
|
+
raise ArgumentError unless login.is_a? String
|
|
268
|
+
raise ArgumentError unless password.is_a? String
|
|
269
|
+
|
|
270
|
+
c = @client["#{@account}/users/#{user}"]
|
|
271
|
+
headers = gen_headers(opts)
|
|
272
|
+
opts[:email] = email
|
|
273
|
+
opts[:login] = login
|
|
274
|
+
opts[:password] = password
|
|
275
|
+
attempt(opts[:attempts]) do
|
|
276
|
+
do_post(c, headers, opts)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Update a user's modifiable properties.
|
|
282
|
+
# Note: Password changes are not allowed using this endpoint; there is an
|
|
283
|
+
# additional methode (change_user_password) for password changes so it can
|
|
284
|
+
# be selectively allowed/disallowed for users using policies.
|
|
285
|
+
#
|
|
286
|
+
# ==== Attributes
|
|
287
|
+
#
|
|
288
|
+
# *+:id - String
|
|
289
|
+
#
|
|
290
|
+
# ==== Options
|
|
291
|
+
#
|
|
292
|
+
# * +:email - String
|
|
293
|
+
# * +:login - String
|
|
294
|
+
# * +:password - String
|
|
295
|
+
# * +:companyName+ - String
|
|
296
|
+
# * +:firstName - String
|
|
297
|
+
# * +:lastName - String
|
|
298
|
+
# * +:address - String
|
|
299
|
+
# * +:postalCode - String
|
|
300
|
+
# * +:city - String
|
|
301
|
+
# * +:state - String
|
|
302
|
+
# * +:country - String
|
|
303
|
+
# * +:phone - String
|
|
304
|
+
def update_user(id, opts = {})
|
|
305
|
+
raise ArgumentError unless id.is_a? String
|
|
306
|
+
c = @client["#{@account}/users/#{id}"]
|
|
307
|
+
headers = gen_headers(opts)
|
|
308
|
+
attempt(opts[:attempts]) do
|
|
309
|
+
do_post(c, headers, opts)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# This is a separate rule for password changes, so different policies can
|
|
315
|
+
# be used for an user trying to modify other data, or only their own password.
|
|
316
|
+
#
|
|
317
|
+
# ==== Attributes
|
|
318
|
+
#
|
|
319
|
+
# *+:user - String
|
|
320
|
+
# *+:password - String
|
|
321
|
+
# *+:password_confirmation - String
|
|
322
|
+
def change_user_password(user, password, password_confirmation, opts = {})
|
|
323
|
+
raise ArgumentError unless user.is_a? String
|
|
324
|
+
raise ArgumentError unless password.is_a? String
|
|
325
|
+
raise ArgumentError unless password_confirmation.is_a? String
|
|
326
|
+
raise InvalidArgument unless password != password_confirmation
|
|
327
|
+
c = @client["#{@account}/users/#{user}/change_password"]
|
|
328
|
+
headers = gen_headers(opts)
|
|
329
|
+
opts[:password] = password
|
|
330
|
+
opts[:password_confirmation] = password_confirmation
|
|
331
|
+
attempt(opts[:attempts]) do
|
|
332
|
+
do_post(c, headers, opts)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Remove a user. They will no longer be able to use this API.
|
|
338
|
+
#
|
|
339
|
+
# ==== Attributes
|
|
340
|
+
#
|
|
341
|
+
# *+:user - String
|
|
342
|
+
def delete_user(user, opts = {})
|
|
343
|
+
raise ArgumentError unless user.is_a? String
|
|
344
|
+
c = @client["#{@account}/users/#{user}"]
|
|
345
|
+
headers = gen_headers(opts)
|
|
346
|
+
attempt(opts[:attempts]) do
|
|
347
|
+
do_delete(c, headers)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
##
|
|
353
|
+
# Roles
|
|
354
|
+
##
|
|
355
|
+
# Roles a sub-users can adopt when attempting to access a resource. See the RBAC section for more details.
|
|
356
|
+
|
|
357
|
+
# Returns an array of account roles.
|
|
358
|
+
def list_roles(opts= {})
|
|
359
|
+
c = @client["#{@account}/roles"]
|
|
360
|
+
headers = gen_headers(opts)
|
|
361
|
+
attempt(opts[:attempts]) do
|
|
362
|
+
do_get(c, headers)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Get an account role (:role) by id or name.
|
|
367
|
+
#
|
|
368
|
+
# ==== Attributes
|
|
369
|
+
#
|
|
370
|
+
# * +:role String - id or name of the role
|
|
371
|
+
def get_role(role, opts = {})
|
|
372
|
+
raise unless role.is_a? String
|
|
373
|
+
c = @client["#{@account}/roles/#{role}"]
|
|
374
|
+
headers = gen_headers(opts)
|
|
375
|
+
attempt(opts[:attempts]) do
|
|
376
|
+
do_get(c, headers)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Create a new role for your account.
|
|
382
|
+
#
|
|
383
|
+
# ==== Attributes
|
|
384
|
+
#
|
|
385
|
+
# * +:name - String (Required) The role's name
|
|
386
|
+
#
|
|
387
|
+
# ==== Options
|
|
388
|
+
#
|
|
389
|
+
# * +:policies - Array This account's policies to be given to this role
|
|
390
|
+
# * +:members - Array This account's user logins to be added to this role (Optional)
|
|
391
|
+
# * +:default_members - Array This account's user logins to be added to this role and have it enabled by default (Optional)
|
|
392
|
+
def create_role(name, opts = {})
|
|
393
|
+
raise ArgumentError unless name.is_a? String
|
|
394
|
+
c = @client["#{@account}/roles"]
|
|
395
|
+
headers = gen_headers(opts)
|
|
396
|
+
attempt(opts[:attempts]) do
|
|
397
|
+
do_post(c, headers, opts)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Modifies an account role. Anything but id can be modified.
|
|
403
|
+
#
|
|
404
|
+
# ==== Attributes
|
|
405
|
+
#
|
|
406
|
+
# *+:name - String role name or id
|
|
407
|
+
#
|
|
408
|
+
# ==== Options
|
|
409
|
+
#
|
|
410
|
+
# *+:policies - Array This account's policies to be given to this role (Optional)
|
|
411
|
+
# *+:members - Array This account's user logins to be added to this role (Optional)
|
|
412
|
+
# *+:default_members - Array This account's user logins to be added to this role and have it enabled by default (Optional)
|
|
413
|
+
def update_role(name, opts = {})
|
|
414
|
+
raise ArgumentError unless name.is_a? String
|
|
415
|
+
c = @client["#{@account}/roles/#{name}"]
|
|
416
|
+
headers = gen_headers(opts)
|
|
417
|
+
attempt(opts[:attempts]) do
|
|
418
|
+
do_post(c, headers, opts)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Remove a role. :role must be the role id (a UUID).
|
|
424
|
+
#
|
|
425
|
+
# ==== Attributes
|
|
426
|
+
#
|
|
427
|
+
# *+:role - String UUID of the role
|
|
428
|
+
def delete_role(role, opts = {})
|
|
429
|
+
raise ArgumentError unless role.is_a? String
|
|
430
|
+
c = @client["#{@account}/roles/#{role}"]
|
|
431
|
+
headers = gen_headers(opts)
|
|
432
|
+
attempt(opts[:attempts]) do
|
|
433
|
+
do_delete(c, headers)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
##
|
|
439
|
+
# Role tags
|
|
440
|
+
##
|
|
441
|
+
|
|
442
|
+
# Sets the given role tags to the provided resource path. resource_path can
|
|
443
|
+
# be the path to any of the CloudAPI resources described in this document:
|
|
444
|
+
# account, keys, users, roles, policies, user's ssh keys, datacenters,
|
|
445
|
+
# images, packages, instances, analytics, instrumentations, firewall rules
|
|
446
|
+
# and networks.
|
|
447
|
+
# For each of these you can set role tags either for an individual resource
|
|
448
|
+
# or for the whole group.
|
|
449
|
+
#
|
|
450
|
+
# ==== Attributes
|
|
451
|
+
#
|
|
452
|
+
# * +:resource_url - String (Required) The resource path to assign a role tags to.
|
|
453
|
+
# * +:role-tag - Array (Required) Array of tags to be assigned/modified
|
|
454
|
+
def create_role_tags(resource_url, role-tag, opts = {})
|
|
455
|
+
raise ArgumentError unless resource_url.is_a? String
|
|
456
|
+
raise ArgumentError unless role-tag.is_a? Array
|
|
457
|
+
c = @client[resource_url]
|
|
458
|
+
headers = gen_headers(opts)
|
|
459
|
+
opts['role-tag'] = role-tag
|
|
460
|
+
attempt(opts[:attempts]) do
|
|
461
|
+
do_put(c, headers, opts)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
##
|
|
467
|
+
# Policies
|
|
468
|
+
##
|
|
469
|
+
# Retrieves a list of account policies.
|
|
470
|
+
def list_policies(opts= {})
|
|
471
|
+
c = @client["#{@account}/policies"]
|
|
472
|
+
headers = gen_headers(opts)
|
|
473
|
+
attempt(opts[:attempts]) do
|
|
474
|
+
do_get(c, headers)
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# Get an account policy (:policy) by id.
|
|
479
|
+
#
|
|
480
|
+
# ==== Attributes
|
|
481
|
+
#
|
|
482
|
+
# * +:policy - String id of the policy
|
|
483
|
+
def get_policy(policy, opts = {})
|
|
484
|
+
raise ArgumentError unless policy.is_a? String
|
|
485
|
+
c = @client["#{@account}/policies/#{policy}"]
|
|
486
|
+
headers = gen_headers(opts)
|
|
487
|
+
attempt(opts[:attempts]) do
|
|
488
|
+
do_get(c, headers)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Creates a new account policy.
|
|
494
|
+
#
|
|
495
|
+
# ==== Attributes
|
|
496
|
+
#
|
|
497
|
+
# * +:name - String The policy name
|
|
498
|
+
# * +:rules - One or more Aperture sentences to be added to the current policy
|
|
499
|
+
#
|
|
500
|
+
# ==== Options
|
|
501
|
+
#
|
|
502
|
+
# * +:description - String A description for this policy (Optional)
|
|
503
|
+
def create_policy(name, rules, opts = {})
|
|
504
|
+
raise ArgumentError unless name.is_a? String
|
|
505
|
+
raise ArgumentError unless rules.is_a? Array
|
|
506
|
+
c = @client["#{@account}/policies"]
|
|
507
|
+
headers = gen_headers(opts)
|
|
508
|
+
opts[:rules] = rules
|
|
509
|
+
attempt(opts[:attempts]) do
|
|
510
|
+
do_post(c, headers, opts)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# Updates an existing account policy. Everything but id can be modified.
|
|
516
|
+
#
|
|
517
|
+
# ==== Attributes
|
|
518
|
+
#
|
|
519
|
+
# * +:policy - String id of the policy
|
|
520
|
+
#
|
|
521
|
+
# ==== Options
|
|
522
|
+
#
|
|
523
|
+
# * +:name - String The policy name
|
|
524
|
+
# * +:rules - One or more Aperture sentences to be added to the current policy
|
|
525
|
+
# * +:description - String A description for this policy (Optional)
|
|
526
|
+
def update_policy(policy, opts = {})
|
|
527
|
+
raise ArgumentError unless name.is_a? String
|
|
528
|
+
c = @client["#{@account}/policies/#{policy}"]
|
|
529
|
+
headers = gen_headers(opts)
|
|
530
|
+
attempt(opts[:attempts]) do
|
|
531
|
+
do_post(c, headers, opts)
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# Delete an RBAC policy. :policy must be the policy id (a UUID).
|
|
537
|
+
#
|
|
538
|
+
# ==== Attributes
|
|
539
|
+
#
|
|
540
|
+
# *+:policy - String UUID of the role
|
|
541
|
+
def delete_policy(policy, opts = {})
|
|
542
|
+
raise ArgumentError unless policy.is_a? String
|
|
543
|
+
c = @client["#{@account}/policies/#{policy}"]
|
|
544
|
+
headers = gen_headers(opts)
|
|
545
|
+
attempt(opts[:attempts]) do
|
|
546
|
+
do_delete(c, headers)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
##
|
|
552
|
+
# User SSH Keys
|
|
553
|
+
##
|
|
554
|
+
# See account keys for a detailed description. Only difference is the path
|
|
555
|
+
# from where you can access users' keys:
|
|
556
|
+
|
|
557
|
+
# Lists all public keys we have on record for the specified account user.
|
|
558
|
+
# See list_keys.
|
|
559
|
+
def list_user_keys(opts= {})
|
|
560
|
+
raise unless @subuser is not nil
|
|
561
|
+
c = @client["#{@account}/users/#{@subuser}/keys"]
|
|
562
|
+
headers = gen_headers(opts)
|
|
563
|
+
attempt(opts[:attempts]) do
|
|
564
|
+
do_get(c, headers)
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# Retrieves the given key record either by fingerprint or name. See get_key.
|
|
569
|
+
#
|
|
570
|
+
# ==== Attributes
|
|
571
|
+
#
|
|
572
|
+
# * +:key - String id or fingerprint of key
|
|
573
|
+
def get_user_key(key, opts = {})
|
|
574
|
+
raise InvalidCredentials unless @subuser is not nil
|
|
575
|
+
raise ArgumentError unless key.is_a? String
|
|
576
|
+
c = @client["#{@account}/users/#{@subuser}/keys/#{key}"]
|
|
577
|
+
headers = gen_headers(opts)
|
|
578
|
+
attempt(opts[:attempts]) do
|
|
579
|
+
do_get(c, headers)
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
# Creates a new key record. See create_key.
|
|
585
|
+
#
|
|
586
|
+
# ==== Attributes
|
|
587
|
+
#
|
|
588
|
+
# * +:key - String Public key in OpenSSH format
|
|
589
|
+
#
|
|
590
|
+
# ==== Options
|
|
591
|
+
#
|
|
592
|
+
# * +:name - String Name for this key
|
|
593
|
+
def create_user_key(key, opts = {})
|
|
594
|
+
raise InvalidCredentials unless @subuser is not nil
|
|
595
|
+
raise ArgumentError unless key.is_a? String
|
|
596
|
+
c = @client["#{@account}/users/#{@subuser}/keys"]
|
|
597
|
+
headers = gen_headers(opts)
|
|
598
|
+
opts[:key] = key
|
|
599
|
+
attempt(opts[:attempts]) do
|
|
600
|
+
do_post(c, headers, opts)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# Removes a key. See get_key.
|
|
606
|
+
#
|
|
607
|
+
# ==== Attributes
|
|
608
|
+
#
|
|
609
|
+
# *+:key - String id or fingerprint of key
|
|
610
|
+
def delete_user_key(key, opts = {})
|
|
611
|
+
raise InvalidCredentials unless @subuser is not nil
|
|
612
|
+
raise ArgumentError unless key.is_a? String
|
|
613
|
+
c = @client["#{@account}/users/#{@subuser}/keys/#{key}"]
|
|
614
|
+
headers = gen_headers(opts)
|
|
615
|
+
attempt(opts[:attempts]) do
|
|
616
|
+
do_delete(c, headers)
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
##
|
|
623
|
+
# Config
|
|
624
|
+
##
|
|
625
|
+
# These endpoints allow you to get and set configuration values related to
|
|
626
|
+
# your account.
|
|
627
|
+
|
|
628
|
+
# Outputs configuration for your account. The configuration values that are
|
|
629
|
+
# currently configurable are:
|
|
630
|
+
# default_network: the network that docker containers are provisioned on.
|
|
631
|
+
def get_config(opts = {})
|
|
632
|
+
c = @client["#{@account}/config"]
|
|
633
|
+
headers = gen_headers(opts)
|
|
634
|
+
attempt(opts[:attempts]) do
|
|
635
|
+
do_get(c, headers)
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
# Updates configuration values for your account.
|
|
641
|
+
#
|
|
642
|
+
# ==== Attributes
|
|
643
|
+
#
|
|
644
|
+
# * +:default_network - String ID of the network used for provisioning docker containers
|
|
645
|
+
def update_config(default_network, opts = {})
|
|
646
|
+
raise ArgumentError unless default_network.is_a? String
|
|
647
|
+
c = @client["#{@account}/config"]
|
|
648
|
+
headers = gen_headers(opts)
|
|
649
|
+
opts[:default_network] = default_network
|
|
650
|
+
attempt(opts[:attempts]) do
|
|
651
|
+
do_put(c, headers, opts)
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
##
|
|
657
|
+
# datacenters
|
|
658
|
+
##
|
|
659
|
+
|
|
660
|
+
# Provides a list of all datacenters this cloud is aware of.
|
|
661
|
+
def list_datacenters(opts= {})
|
|
662
|
+
c = @client["#{@account}/datacenters"]
|
|
663
|
+
headers = gen_headers(opts)
|
|
664
|
+
attempt(opts[:attempts]) do
|
|
665
|
+
do_get(c, headers)
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
# Gets an individual datacenter by name. Returns an HTTP redirect to
|
|
670
|
+
# your client, where the datacenter url is in the Location header.
|
|
671
|
+
#
|
|
672
|
+
# ==== Attributes
|
|
673
|
+
#
|
|
674
|
+
# * +:name - String datacenter name
|
|
675
|
+
def get_datacenter(name, opts = {})
|
|
676
|
+
raise ArgumentError unless name.is_a? String
|
|
677
|
+
c = @client["#{@account}/datacenters/#{name}"]
|
|
678
|
+
headers = gen_headers(opts)
|
|
679
|
+
attempt(opts[:attempts]) do
|
|
680
|
+
raise InvalidArgument unless c.is_a? RestClient::Resource
|
|
681
|
+
result = c.get(headers)
|
|
682
|
+
raise InternalError unless result.is_a? RestClient::Response
|
|
683
|
+
|
|
684
|
+
if result.code == 302
|
|
685
|
+
return JSON.parse(result.body)
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
raise_error(result)
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
##
|
|
694
|
+
# Services
|
|
695
|
+
##
|
|
696
|
+
|
|
697
|
+
# Provides the URL endpoints for services for this datacenter. It is a
|
|
698
|
+
# mapping of service name to URL endpoint.
|
|
699
|
+
def list_services(opts= {})
|
|
700
|
+
c = @client["#{@account}/services"]
|
|
701
|
+
headers = gen_headers(opts)
|
|
702
|
+
attempt(opts[:attempts]) do
|
|
703
|
+
do_get(c, headers)
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
##
|
|
708
|
+
# Images
|
|
709
|
+
##
|
|
710
|
+
# An image contains the software packages that will be available on
|
|
711
|
+
# newly-provisioned instance. In the case of hardware virtual machines,
|
|
712
|
+
# the image also includes the operating system.
|
|
713
|
+
|
|
714
|
+
# Provides a list of images available in this datacenter.
|
|
715
|
+
# Note: Currently, Docker images are not included in this endpoint's
|
|
716
|
+
# responses. You must use docker images against the docker service for this
|
|
717
|
+
# datacenter.
|
|
718
|
+
#
|
|
719
|
+
# ==== Options
|
|
720
|
+
#
|
|
721
|
+
# * +:name - String The "friendly" name for this image
|
|
722
|
+
# * +:os - String The underlying operating system for this image
|
|
723
|
+
# * +:version - String The version for this image
|
|
724
|
+
# * +:public - Boolean Filter public/private images
|
|
725
|
+
# * +:state - String Filter on image state. By default only active images are shown. Use ?state=all to list all images
|
|
726
|
+
# * +:owner - String Filter on owner UUID
|
|
727
|
+
# * +:type - String Filter on image type. The types changed in v8.0.0
|
|
728
|
+
def list_images(opts= {})
|
|
729
|
+
url = "#{@account}/images"
|
|
730
|
+
if opts.size > 0
|
|
731
|
+
url = url + '?' + URI.encode_www_form(opts)
|
|
732
|
+
end
|
|
733
|
+
c = @client[url]
|
|
734
|
+
headers = gen_headers(opts)
|
|
735
|
+
attempt(opts[:attempts]) do
|
|
736
|
+
do_get(c, headers)
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
# Gets an individual image by id.
|
|
741
|
+
#
|
|
742
|
+
# ==== Attributes
|
|
743
|
+
#
|
|
744
|
+
# * +:image - String id of the image
|
|
745
|
+
def get_image(image, opts = {})
|
|
746
|
+
raise ArgumentError unless image.is_a? String
|
|
747
|
+
c = @client["#{@account}/images/#{image}"]
|
|
748
|
+
headers = gen_headers(opts)
|
|
749
|
+
attempt(opts[:attempts]) do
|
|
750
|
+
do_get(c, headers)
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# Delete an image. Caller must be the owner of the image to delete it.
|
|
756
|
+
#
|
|
757
|
+
# ==== Attributes
|
|
758
|
+
#
|
|
759
|
+
# *+:image - String id of the image
|
|
760
|
+
def delete_image(image, opts = {})
|
|
761
|
+
raise ArgumentError unless image.is_a? String
|
|
762
|
+
c = @client["#{@account}/images/#{policy}"]
|
|
763
|
+
headers = gen_headers(opts)
|
|
764
|
+
attempt(opts[:attempts]) do
|
|
765
|
+
do_delete(c, headers)
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
# Exports an image to the specified Manta path. Caller must be the owner of
|
|
771
|
+
# the image, and the correspondent Manta path prefix, in order to export it.
|
|
772
|
+
# Both the image manifest and the image file will be exported, and their
|
|
773
|
+
# filenames will default to the following format when the specified manta
|
|
774
|
+
# path is a directory
|
|
775
|
+
#
|
|
776
|
+
# ==== Attributes
|
|
777
|
+
#
|
|
778
|
+
# * +:image - String id of the image
|
|
779
|
+
# * +:manta_path - String Manta path prefix used when exporting the image
|
|
780
|
+
def export_image(image, manta_path, opts = {})
|
|
781
|
+
raise ArgumentError unless image.is_a? String
|
|
782
|
+
raise ArgumentError unless manta_path.is_a? String
|
|
783
|
+
c = @client["#{@account}/images/#{image}?action=export"]
|
|
784
|
+
headers = gen_headers(opts)
|
|
785
|
+
opts[:manta_path] = manta_path
|
|
786
|
+
attempt(opts[:attempts]) do
|
|
787
|
+
do_post(c, headers, opts)
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
# Create a new custom image from an instance. The typical process is:
|
|
793
|
+
#
|
|
794
|
+
# 1. Customize an instance the way you want it.
|
|
795
|
+
# 2. Call this method (create_image_from_machine) to create a new image.
|
|
796
|
+
# 3. Repeat from step 1 if more customizations are desired with different images.
|
|
797
|
+
# 4. Use the new image(s) for provisioning via create_machine.
|
|
798
|
+
#
|
|
799
|
+
# ==== Attributes
|
|
800
|
+
#
|
|
801
|
+
# * +:machine - String The prepared and stopped instance UUID from which the image is to be created
|
|
802
|
+
# * +:name - String The name of the custom image, e.g. "my-image". See the IMGAPI docs for details
|
|
803
|
+
# * +:version - String The version of the custom image, e.g. "1.0.0". See the IMGAPI docs for details
|
|
804
|
+
#
|
|
805
|
+
# ==== Options
|
|
806
|
+
#
|
|
807
|
+
# * +:description - String The image description
|
|
808
|
+
# * +:homepage - String The image homepage
|
|
809
|
+
# * +:eula - String The image eula
|
|
810
|
+
# * +:acl - String The image acl
|
|
811
|
+
# * +:tags - String The image tags
|
|
812
|
+
def create_image_from_machine(machine, name, version, opts = {})
|
|
813
|
+
raise ArgumentError unless machine.is_a? String
|
|
814
|
+
raise ArgumentError unless name.is_a? String
|
|
815
|
+
raise ArgumentError unless version.is_a? String
|
|
816
|
+
c = @client["#{@account}/images"]
|
|
817
|
+
headers = gen_headers(opts)
|
|
818
|
+
opts[:machine] = machine
|
|
819
|
+
opts[:name] = name
|
|
820
|
+
opts[:version] = version
|
|
821
|
+
attempt(opts[:attempts]) do
|
|
822
|
+
do_post(c, headers, opts)
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
# Updates metadata about an image.
|
|
828
|
+
#
|
|
829
|
+
# ==== Attributes
|
|
830
|
+
#
|
|
831
|
+
# * +:machine - String The prepared and stopped instance UUID from which the image is to be created
|
|
832
|
+
#
|
|
833
|
+
# ==== Options
|
|
834
|
+
#
|
|
835
|
+
# * +:name - String The name of the custom image, e.g. "my-image". See the IMGAPI docs for details
|
|
836
|
+
# * +:version - String The version of the custom image, e.g. "1.0.0". See the IMGAPI docs for details
|
|
837
|
+
# * +:description - String The image description
|
|
838
|
+
# * +:homepage - String The image homepage
|
|
839
|
+
# * +:eula - String The image eula
|
|
840
|
+
# * +:acl - String The image acl
|
|
841
|
+
# * +:tags - String The image tags
|
|
842
|
+
def update_image(machine, opts = {})
|
|
843
|
+
raise ArgumentError unless machine.is_a? String
|
|
844
|
+
c = @client["#{@account}/images/#{image}?action=update"]
|
|
845
|
+
headers = gen_headers(opts)
|
|
846
|
+
attempt(opts[:attempts]) do
|
|
847
|
+
do_post(c, headers, opts)
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
##
|
|
853
|
+
# Packages
|
|
854
|
+
##
|
|
855
|
+
# Packages are named collections of resources that are used to describe the
|
|
856
|
+
# dimensions of either a container or a hardware virtual machine. These
|
|
857
|
+
# resources include (but are not limited to) RAM size, CPUs, CPU caps,
|
|
858
|
+
# lightweight threads, disk space, swap size, and logical networks.
|
|
859
|
+
|
|
860
|
+
# Provides a list of packages available in this datacenter.
|
|
861
|
+
#
|
|
862
|
+
# ==== Options
|
|
863
|
+
#
|
|
864
|
+
# * +:name - String The "friendly" name for this package
|
|
865
|
+
# * +:memory - Number How much memory will by available (in MiB)
|
|
866
|
+
# * +:disk - Number How much disk space will be available (in MiB)
|
|
867
|
+
# * +:swap - Number How much swap space will be available (in MiB)
|
|
868
|
+
# * +:lwps - Number Maximum number of light-weight processes (threads) allowed
|
|
869
|
+
# * +:vcpus - Number Number of vCPUs for this package
|
|
870
|
+
# * +:version - String The version of this package
|
|
871
|
+
# * +:group - String The group this package belongs to
|
|
872
|
+
def list_packages(opts= {})
|
|
873
|
+
url = "#{@account}/packages"
|
|
874
|
+
if opts.size > 0
|
|
875
|
+
url = url + '?' + URI.encode_www_form(opts)
|
|
876
|
+
end
|
|
877
|
+
c = @client[url]
|
|
878
|
+
headers = gen_headers(opts)
|
|
879
|
+
attempt(opts[:attempts]) do
|
|
880
|
+
do_get(c, headers)
|
|
881
|
+
end
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
# Gets an individual image by id.
|
|
885
|
+
#
|
|
886
|
+
# ==== Attributes
|
|
887
|
+
#
|
|
888
|
+
# * +:package - String id of the package
|
|
889
|
+
def get_package(package, opts = {})
|
|
890
|
+
raise ArgumentError unless package.is_a? String
|
|
891
|
+
c = @client["#{@account}/packages/#{package}"]
|
|
892
|
+
headers = gen_headers(opts)
|
|
893
|
+
attempt(opts[:attempts]) do
|
|
894
|
+
do_get(c, headers)
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
##
|
|
900
|
+
# Instances
|
|
901
|
+
##
|
|
902
|
+
# Triton supports three different types of instances:
|
|
903
|
+
#
|
|
904
|
+
# Docker containers. OS-virtualized instances managed through the Docker client.
|
|
905
|
+
# Infrastructure containers. More traditional OS-virtualized instances
|
|
906
|
+
# running SmartOS or more Linux distributions.
|
|
907
|
+
# Hardware-virtualized machines. Hardware-virtualized instances (KVM) for
|
|
908
|
+
# running legacy or special-purpose operating systems.
|
|
909
|
+
#
|
|
910
|
+
# Infrastructure and Docker containers are lightweight, offering the most
|
|
911
|
+
# performance, observability and operational flexibility. Harware-virtualized
|
|
912
|
+
# machines are useful for non-SmartOS or non-Linux stacks.
|
|
913
|
+
|
|
914
|
+
# Lists all instances we have on record for your account. If you have a large
|
|
915
|
+
# number of instances, you can filter using the input parameters listed below.
|
|
916
|
+
# Note that deleted instances are returned only if the instance history has
|
|
917
|
+
# not been purged from Triton.
|
|
918
|
+
#
|
|
919
|
+
# You can paginate this API by passing in offset and limit. HTTP responses
|
|
920
|
+
# will contain the additional headers x-resource-count and x-query-limit.
|
|
921
|
+
# If x-resource-count is less than x-query-limit, you're done, otherwise call
|
|
922
|
+
# the API again with offset set to offset + limit to fetch additional instances.
|
|
923
|
+
#
|
|
924
|
+
# Note that there is a opts[:method] = :head form of this API, so you can
|
|
925
|
+
# retrieve the number of instances without retrieving a JSON describing the
|
|
926
|
+
# instances themselves.
|
|
927
|
+
#
|
|
928
|
+
# ==== Options
|
|
929
|
+
# *+:type - String (deprecated) The type of instance (virtualmachine or smartmachine)
|
|
930
|
+
# *+:brand - String (v8.0+) The type of instance (e.g. lx)
|
|
931
|
+
# *+:name - String Machine name to find (will make your list size 1, or 0 if nothing found)
|
|
932
|
+
# *+:image - String Image id; returns instances provisioned with that image
|
|
933
|
+
# *+:state - String The current state of the instance (e.g. running)
|
|
934
|
+
# *+:memory - Integer The current size of the RAM deployed for the instance (in MiB)
|
|
935
|
+
# *+:tombstone - Boolean Include destroyed and failed instances available in instance history
|
|
936
|
+
# *+:limit - Number Return a max of N instances; default is 1000 (which is also the maximum allowable result set size)
|
|
937
|
+
# *+:offset - Number Get a limit number of instances starting at this offset
|
|
938
|
+
# *+:tag.$name - String An arbitrary set of tags can be used for querying, assuming they are prefixed with "tag."
|
|
939
|
+
# *+:docker - Boolean Whether to only list Docker instances, or only non-Docker instances, if present. Defaults to showing all instances.
|
|
940
|
+
# *+:credentials -Boolean Whether to include the generated credentials for instances, if present. Defaults to false
|
|
941
|
+
def list_machines(opts= {})
|
|
942
|
+
url = "#{@account}/machines"
|
|
943
|
+
# TODO fix for head query
|
|
944
|
+
opts[:limit] = opts[:limit] ? MAX_LIMIT
|
|
945
|
+
raise ArgumentError unless 0 < opts[:limit] && opts[:limit] <= MAX_LIMIT
|
|
946
|
+
if opts.size > 0
|
|
947
|
+
url = url + '?' + URI.encode_www_form(opts)
|
|
948
|
+
end
|
|
949
|
+
c = @client[url]
|
|
950
|
+
headers = gen_headers(opts)
|
|
951
|
+
attempt(opts[:attempts]) do
|
|
952
|
+
if opts[:head]
|
|
953
|
+
do_head(c, headers)
|
|
954
|
+
else
|
|
955
|
+
do_get(c, headers)
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
# Gets the details for an individual instance.
|
|
962
|
+
#
|
|
963
|
+
# Deleted instances are returned only if the instance history has not been
|
|
964
|
+
# purged from Triton.
|
|
965
|
+
#
|
|
966
|
+
# ==== Attributes
|
|
967
|
+
#
|
|
968
|
+
# * +:instance - String id of the instance
|
|
969
|
+
def get_instance(instance, opts = {})
|
|
970
|
+
raise ArgumentError unless instance.is_a? String
|
|
971
|
+
c = @client["#{@account}/instances/#{instance}"]
|
|
972
|
+
headers = gen_headers(opts)
|
|
973
|
+
attempt(opts[:attempts]) do
|
|
974
|
+
do_get(c, headers)
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
# Allows you to provision an instance.
|
|
980
|
+
# If you do not specify a name, CloudAPI will generate a random one for you.
|
|
981
|
+
# If you have enabled Triton CNS on your account, this name will also be
|
|
982
|
+
# used in DNS to refer to the new instance (and must therefore consist of
|
|
983
|
+
# DNS-safe characters only).
|
|
984
|
+
#
|
|
985
|
+
# Your instance will initially be not available for login (Triton must
|
|
986
|
+
# provision and boot it); you can poll GetMachine for its status. When the
|
|
987
|
+
# state field is equal to running, you can log in. If the instance is a
|
|
988
|
+
# brand other than kvm, you can usually use any of the SSH keys managed
|
|
989
|
+
# under the keys section of CloudAPI to login as any POSIX user on the OS.
|
|
990
|
+
# You can add/remove keys over time, and the instance will automatically
|
|
991
|
+
# work with that set.
|
|
992
|
+
#
|
|
993
|
+
# If the the instance has a brand kvm, and of a UNIX-derived OS (e.g. Linux),
|
|
994
|
+
# you must have keys uploaded before provisioning; that entire set of keys
|
|
995
|
+
# will be written out to /root/.ssh/authorized_keys in the new instance,
|
|
996
|
+
# and you can SSH in using one of those keys. Changing the keys over time
|
|
997
|
+
# under your account will not affect a running hardware virtual machine in
|
|
998
|
+
# any way; those keys are statically written at provisioning-time only, and
|
|
999
|
+
# you will need to manually manage them on the instance itself.
|
|
1000
|
+
#
|
|
1001
|
+
# ==== Attributes
|
|
1002
|
+
#
|
|
1003
|
+
# * +:image - String id of the image
|
|
1004
|
+
# * +:package - String id of the packge
|
|
1005
|
+
#
|
|
1006
|
+
# ==== Options
|
|
1007
|
+
#
|
|
1008
|
+
# * +:name - String Friendly name for this instance; default is the first 8 characters of the machine id
|
|
1009
|
+
# * +:networks - Array Desired networks ids, obtained from list_networks
|
|
1010
|
+
# * +:locality - Object[String => Array] Optionally specify which instances the new instance should be near or far from
|
|
1011
|
+
# * +:metadata.$name - String An arbitrary set of metadata key/value pairs can be set at provision time, but they must be prefixed with "metadata."
|
|
1012
|
+
# * +:tag.$name - String An arbitrary set of tags can be set at provision time, but they must be prefixed with "tag."
|
|
1013
|
+
# * +:firewall_enabled - Boolean Completely enable or disable firewall for this instance. Default is false
|
|
1014
|
+
|
|
1015
|
+
def create_machine(image, package, opts= {})
|
|
1016
|
+
raise ArgumentError unless image.is_a? String
|
|
1017
|
+
raise ArgumentError unless package.is_a? String
|
|
1018
|
+
c = @client["#{@account}/machines"]
|
|
1019
|
+
headers = gen_headers(opts)
|
|
1020
|
+
opts[:image] = machine
|
|
1021
|
+
opts[:package] = name
|
|
1022
|
+
attempt(opts[:attempts]) do
|
|
1023
|
+
do_post(c, headers, opts)
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
# Allows you to shut down an instance. POST to the instance name with an
|
|
1029
|
+
# action of stop.
|
|
1030
|
+
#
|
|
1031
|
+
# You can poll on get_machine until the state is stopped.
|
|
1032
|
+
# ==== Attributes
|
|
1033
|
+
#
|
|
1034
|
+
# * +:machine - String id of the machine
|
|
1035
|
+
def stop_machine(machine, opts= {})
|
|
1036
|
+
raise ArgumentError unless machine.is_a? String
|
|
1037
|
+
c = @client["#{@account}/machines/#{machine}?action=stop"]
|
|
1038
|
+
attempt(opts[:attempts]) do
|
|
1039
|
+
do_post(c, headers, opts)
|
|
1040
|
+
end
|
|
1041
|
+
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
# Allows you to boot up an instance. POST to the instance name with an
|
|
1045
|
+
# action of start.
|
|
1046
|
+
#
|
|
1047
|
+
# You can poll on get_machine until the state is running.
|
|
1048
|
+
# ==== Attributes
|
|
1049
|
+
#
|
|
1050
|
+
# * +:machine - String id of the machine
|
|
1051
|
+
def start_machine(machine, opts= {})
|
|
1052
|
+
raise ArgumentError unless machine.is_a? String
|
|
1053
|
+
c = @client["#{@account}/machines/#{machine}?action=start"]
|
|
1054
|
+
attempt(opts[:attempts]) do
|
|
1055
|
+
do_post(c, headers, opts)
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
# Allows you to reboot an instance. POST to the instance name with an
|
|
1061
|
+
# action of reboot.
|
|
1062
|
+
#
|
|
1063
|
+
# You can poll on get_machine until the state is running.
|
|
1064
|
+
# ==== Attributes
|
|
1065
|
+
#
|
|
1066
|
+
# * +:machine - String id of the machine
|
|
1067
|
+
def reboot_machine(machine, opts= {})
|
|
1068
|
+
raise ArgumentError unless machine.is_a? String
|
|
1069
|
+
c = @client["#{@account}/machines/#{machine}?action=reboot"]
|
|
1070
|
+
attempt(opts[:attempts]) do
|
|
1071
|
+
do_post(c, headers, opts)
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
# Resize an instance to a new package (a.k.a. instance type).
|
|
1078
|
+
#
|
|
1079
|
+
# Resizing is only supported for containers (instances which are not
|
|
1080
|
+
# hardware virtual machines -- they have brand=kvm). Hardware virtual machines
|
|
1081
|
+
# cannot be resized.
|
|
1082
|
+
#
|
|
1083
|
+
# Resizing is not guaranteed to work, especially when resizing upwards in
|
|
1084
|
+
# resources. It is best-effort, and may fail. Resizing downwards will usually
|
|
1085
|
+
# succeed.
|
|
1086
|
+
# ==== Attributes
|
|
1087
|
+
#
|
|
1088
|
+
# * +:machine - String id of the machine
|
|
1089
|
+
# * +:package - String A package id, as returned from list_packages
|
|
1090
|
+
def resize_machine(machine, package, opts= {})
|
|
1091
|
+
raise ArgumentError unless machine.is_a? String
|
|
1092
|
+
raise ArgumentError unless package.is_a? String
|
|
1093
|
+
c = @client["#{@account}/machines/#{machine}?action=resize"]
|
|
1094
|
+
opts['package'] = package
|
|
1095
|
+
attempt(opts[:attempts]) do
|
|
1096
|
+
do_post(c, headers, opts)
|
|
1097
|
+
end
|
|
1098
|
+
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
# Allows you to rename an instance. POST to the instance id with an action
|
|
1102
|
+
# of rename. You must additionally include a new name for the instance.
|
|
1103
|
+
#
|
|
1104
|
+
# ==== Attributes
|
|
1105
|
+
#
|
|
1106
|
+
# * +:machine - String id of the machine
|
|
1107
|
+
# * +:name - String The new "friendly" name for this instance
|
|
1108
|
+
def rename_machine(machine, name, opts= {})
|
|
1109
|
+
raise ArgumentError unless machine.is_a? String
|
|
1110
|
+
raise ArgumentError unless name.is_a? String
|
|
1111
|
+
c = @client["#{@account}/machines/#{machine}?action=rename"]
|
|
1112
|
+
opts['name'] = name
|
|
1113
|
+
attempt(opts[:attempts]) do
|
|
1114
|
+
do_post(c, headers, opts)
|
|
1115
|
+
end
|
|
1116
|
+
|
|
1117
|
+
end
|
|
1118
|
+
|
|
1119
|
+
# Allows you to enable the firewall for an instance.
|
|
1120
|
+
#
|
|
1121
|
+
# ==== Attributes
|
|
1122
|
+
#
|
|
1123
|
+
# * +:machine - String id of the machine
|
|
1124
|
+
def enable_machine_firewall(machine, opts= {})
|
|
1125
|
+
raise ArgumentError unless machine.is_a? String
|
|
1126
|
+
c = @client["#{@account}/machines/#{machine}?action=enable_firewall"]
|
|
1127
|
+
attempt(opts[:attempts]) do
|
|
1128
|
+
do_post(c, headers, opts)
|
|
1129
|
+
end
|
|
1130
|
+
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
# Allows you to completely disable the firewall of an instance.
|
|
1134
|
+
#
|
|
1135
|
+
# ==== Attributes
|
|
1136
|
+
#
|
|
1137
|
+
# * +:machine - String id of the machine
|
|
1138
|
+
def disable_machine_firewall(machine, opts= {})
|
|
1139
|
+
raise ArgumentError unless machine.is_a? String
|
|
1140
|
+
c = @client["#{@account}/machines/#{machine}?action=disable_firewall"]
|
|
1141
|
+
attempt(opts[:attempts]) do
|
|
1142
|
+
do_post(c, headers, opts)
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
# Allows you to take a snapshot of an instance. Once you have one or more
|
|
1148
|
+
# snapshots, you can boot the instance from a previous snapshot.
|
|
1149
|
+
#
|
|
1150
|
+
# Snapshots are not usable with other instances; they are a point-in-time
|
|
1151
|
+
# snapshot of the current instance. Snapshots can also only be taken of
|
|
1152
|
+
# instances that are not of brand 'kvm'.
|
|
1153
|
+
#
|
|
1154
|
+
# Since instance instances use a copy-on-write filesystem, snapshots take up
|
|
1155
|
+
# increasing amounts of space as the filesystem changes over time. There is
|
|
1156
|
+
# a limit to how much space snapshots are allowed to take. Plan your
|
|
1157
|
+
# snapshots accordingly.
|
|
1158
|
+
#
|
|
1159
|
+
# You can poll on get_machine_snapshot until the state is created.
|
|
1160
|
+
#
|
|
1161
|
+
# ==== Attributes
|
|
1162
|
+
#
|
|
1163
|
+
# * +:machine - String id of the machine
|
|
1164
|
+
#
|
|
1165
|
+
# ==== Options
|
|
1166
|
+
#
|
|
1167
|
+
# * +:name - String The name to assign to the new snapshot
|
|
1168
|
+
def create_machine_snapshot(machine, opts= {})
|
|
1169
|
+
raise ArgumentError unless machine.is_a? String
|
|
1170
|
+
c = @client["#{@account}/machines/#{machine}/snapshots"]
|
|
1171
|
+
attempt(opts[:attempts]) do
|
|
1172
|
+
do_post(c, headers, opts)
|
|
1173
|
+
end
|
|
1174
|
+
|
|
1175
|
+
end
|
|
1176
|
+
|
|
1177
|
+
# If an instance is in the 'stopped' state, you can choose to start the
|
|
1178
|
+
# instance from the referenced snapshot. This is effectively a means to
|
|
1179
|
+
# roll back instance state.
|
|
1180
|
+
#
|
|
1181
|
+
# ==== Attributes
|
|
1182
|
+
#
|
|
1183
|
+
# * +:machine - String id of the machine
|
|
1184
|
+
# * +:snapshot - String The name of the snapshot
|
|
1185
|
+
#
|
|
1186
|
+
# ==== Options
|
|
1187
|
+
#
|
|
1188
|
+
# * +:name - String The name to assign to the new snapshot
|
|
1189
|
+
def start_machine_from_snapshot(machine, snapshot, opts= {})
|
|
1190
|
+
raise ArgumentError unless machine.is_a? String
|
|
1191
|
+
raise ArgumentError unless snapshot.is_a? String
|
|
1192
|
+
c = @client["#{@account}/machines/#{machine}/snapshots/#{snapshot}"]
|
|
1193
|
+
attempt(opts[:attempts]) do
|
|
1194
|
+
do_post(c, headers, opts)
|
|
1195
|
+
end
|
|
1196
|
+
|
|
1197
|
+
end
|
|
1198
|
+
|
|
1199
|
+
# Lists all snapshots taken for a given instance. There are no filtration
|
|
1200
|
+
# parameters for this API.
|
|
1201
|
+
#
|
|
1202
|
+
# ==== Attributes
|
|
1203
|
+
#
|
|
1204
|
+
# * +:machine - String id of the machine
|
|
1205
|
+
def list_machine_snapshots(machine, opts= {})
|
|
1206
|
+
raise ArgumentError unless machine.is_a? String
|
|
1207
|
+
c = @client["#{@account}/machines/#{machine}/snapshots"]
|
|
1208
|
+
attempt(opts[:attempts]) do
|
|
1209
|
+
do_get(c, headers)
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
# Gets the state of the named snapshot.
|
|
1215
|
+
#
|
|
1216
|
+
# ==== Attributes
|
|
1217
|
+
#
|
|
1218
|
+
# * +:machine - String id of the machine
|
|
1219
|
+
# * +:snapshot - String The name of the snapshot
|
|
1220
|
+
def get_machine_snapshots(machine, snapshot, opts= {})
|
|
1221
|
+
raise ArgumentError unless machine.is_a? String
|
|
1222
|
+
raise ArgumentError unless snapshot.is_a? String
|
|
1223
|
+
c = @client["#{@account}/machines/#{machine}/snapshots/#{snapshot}"]
|
|
1224
|
+
attempt(opts[:attempts]) do
|
|
1225
|
+
do_get(c, headers)
|
|
1226
|
+
end
|
|
1227
|
+
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
# Deletes the specified snapshot of an instance.
|
|
1231
|
+
#
|
|
1232
|
+
# ==== Attributes
|
|
1233
|
+
#
|
|
1234
|
+
# * +:machine - String id of the machine
|
|
1235
|
+
# * +:snapshot - String The name of the snapshot
|
|
1236
|
+
def delete_machine_snapshot(machine, snapshot, opts= {})
|
|
1237
|
+
raise ArgumentError unless machine.is_a? String
|
|
1238
|
+
raise ArgumentError unless snapshot.is_a? String
|
|
1239
|
+
c = @client["#{@account}/machines/#{machine}/snapshots/#{snapshot}"]
|
|
1240
|
+
attempt(opts[:attempts]) do
|
|
1241
|
+
do_delete(c, headers)
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1246
|
+
# Allows you to update the metadata for a given instance. Note that updating
|
|
1247
|
+
# the metadata via CloudAPI will result in the metadata being updated in the
|
|
1248
|
+
# running instance.
|
|
1249
|
+
#
|
|
1250
|
+
# The semantics of this call are subtly different that the add_machine_tags
|
|
1251
|
+
# call -- any metadata keys passed in here are created if they do not exist,
|
|
1252
|
+
# and overwritten if they do.
|
|
1253
|
+
#
|
|
1254
|
+
# ==== Attributes
|
|
1255
|
+
#
|
|
1256
|
+
# * +:machine - String id of the machine
|
|
1257
|
+
# * +:keys - String or Json object of keys to update
|
|
1258
|
+
#
|
|
1259
|
+
# ==== Options
|
|
1260
|
+
#
|
|
1261
|
+
# * +:name - String The name to assign to the new snapshot
|
|
1262
|
+
def update_machine_metadata(machine, keys, opts= {})
|
|
1263
|
+
raise ArgumentError unless machine.is_a? String
|
|
1264
|
+
raise ArgumentError unless keys.is_a? String || keys.is_a? JSON
|
|
1265
|
+
c = @client["#{@account}/machines/#{machine}/snapshots/#{snapshot}"]
|
|
1266
|
+
opts['keys'] = keys
|
|
1267
|
+
attempt(opts[:attempts]) do
|
|
1268
|
+
do_post(c, headers, opts)
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
end
|
|
1272
|
+
|
|
1273
|
+
# Returns the complete set of metadata associated with this instance.
|
|
1274
|
+
#
|
|
1275
|
+
# ==== Attributes
|
|
1276
|
+
#
|
|
1277
|
+
# * +:machine - String id of the machine
|
|
1278
|
+
#
|
|
1279
|
+
# ==== Options
|
|
1280
|
+
#
|
|
1281
|
+
# * +:credentials - Boolean Whether or not to return instance credentials. Defaults to false
|
|
1282
|
+
def list_machine_metadata(machine, opts= {})
|
|
1283
|
+
raise ArgumentError unless machine.is_a? String
|
|
1284
|
+
url = "#{@account}/machines/#{machine}/metadata"
|
|
1285
|
+
if opts.size > 0 && opts['credentials'].is_a? Boolean
|
|
1286
|
+
if opts['credentials']
|
|
1287
|
+
url = url + '?' + "credentials=true"
|
|
1288
|
+
end
|
|
1289
|
+
end
|
|
1290
|
+
c = @client[url]
|
|
1291
|
+
attempt(opts[:attempts]) do
|
|
1292
|
+
do_get(c, headers)
|
|
1293
|
+
end
|
|
1294
|
+
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
# Returns a single metadata entry associated with this instance.
|
|
1299
|
+
#
|
|
1300
|
+
# ==== Attributes
|
|
1301
|
+
#
|
|
1302
|
+
# * +:machine - String id of the machine
|
|
1303
|
+
# * +:key - String Name of metadata value to retrieve
|
|
1304
|
+
def get_machine_metadata(machine, key,opts= {})
|
|
1305
|
+
raise ArgumentError unless machine.is_a? String
|
|
1306
|
+
raise ArgumentError unless key.is_a? String
|
|
1307
|
+
c = @client["#{@account}/machines/#{machine}/metadata/#{key}"]
|
|
1308
|
+
attempt(opts[:attempts]) do
|
|
1309
|
+
do_get(c, headers)
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
end
|
|
1313
|
+
|
|
1314
|
+
# Deletes a single metadata key from this instance.
|
|
1315
|
+
#
|
|
1316
|
+
# ==== Attributes
|
|
1317
|
+
#
|
|
1318
|
+
# * +:machine - String id of the machine
|
|
1319
|
+
# * +:key - String Name of metadata value to delete
|
|
1320
|
+
def delete_machine_metadata(machine, key,opts= {})
|
|
1321
|
+
raise ArgumentError unless machine.is_a? String
|
|
1322
|
+
raise ArgumentError unless key.is_a? String
|
|
1323
|
+
c = @client["#{@account}/machines/#{machine}/metadata/#{key}"]
|
|
1324
|
+
attempt(opts[:attempts]) do
|
|
1325
|
+
do_delete(c, headers)
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
end
|
|
1329
|
+
|
|
1330
|
+
# Deletes all metadata keys from this instance.
|
|
1331
|
+
#
|
|
1332
|
+
# ==== Attributes
|
|
1333
|
+
#
|
|
1334
|
+
# * +:machine - String id of the machine
|
|
1335
|
+
def delete_all_machine_metadata(machine, opts= {})
|
|
1336
|
+
raise ArgumentError unless machine.is_a? String
|
|
1337
|
+
c = @client["#{@account}/machines/#{machine}/metadata"]
|
|
1338
|
+
attempt(opts[:attempts]) do
|
|
1339
|
+
do_delete(c, headers)
|
|
1340
|
+
end
|
|
1341
|
+
|
|
1342
|
+
end
|
|
1343
|
+
|
|
1344
|
+
# Set tags on the given instance. A pre-existing tag with the same name as
|
|
1345
|
+
# one given will be overwritten.
|
|
1346
|
+
# Note: This action is asynchronous. You can poll on ListMachineTags to wait
|
|
1347
|
+
# for the update to be complete (the triton instance tag set -w,--wait
|
|
1348
|
+
# option does this).
|
|
1349
|
+
#
|
|
1350
|
+
# ==== Attributes
|
|
1351
|
+
#
|
|
1352
|
+
# * +:machine - String id of the machine
|
|
1353
|
+
# * +:tags - Array tags to be added
|
|
1354
|
+
def add_machine_tags(machine, tags, opts= {})
|
|
1355
|
+
raise ArgumentError unless machine.is_a? String
|
|
1356
|
+
raise ArgumentError unless tags.is_a? Hash
|
|
1357
|
+
c = @client["#{@account}/machines/#{machine}/tags"]
|
|
1358
|
+
opts[:tags] = tags
|
|
1359
|
+
attempt(opts[:attempts]) do
|
|
1360
|
+
do_post(c, headers, opts)
|
|
1361
|
+
end
|
|
1362
|
+
|
|
1363
|
+
end
|
|
1364
|
+
|
|
1365
|
+
# Fully replace all tags on an instance with the given tags.
|
|
1366
|
+
# Note: This action is asynchronous. You can poll on ListMachineTags to wait
|
|
1367
|
+
# for the update to be complete (the triton instance tag set -w,--wait
|
|
1368
|
+
# option does this).
|
|
1369
|
+
#
|
|
1370
|
+
# ==== Attributes
|
|
1371
|
+
#
|
|
1372
|
+
# * +:machine - String id of the machine
|
|
1373
|
+
# * +:tags - Array tags to replace
|
|
1374
|
+
def replace_machine_tags(machine, tags, opts= {})
|
|
1375
|
+
raise ArgumentError unless machine.is_a? String
|
|
1376
|
+
raise ArgumentError unless tags.is_a? Hash
|
|
1377
|
+
c = @client["#{@account}/machines/#{machine}/tags"]
|
|
1378
|
+
opts[:tags] = tags
|
|
1379
|
+
attempt(opts[:attempts]) do
|
|
1380
|
+
do_put(c, headers, opts)
|
|
1381
|
+
end
|
|
1382
|
+
|
|
1383
|
+
end
|
|
1384
|
+
|
|
1385
|
+
# Fully replace all tags on an instance with the given tags.
|
|
1386
|
+
# Note: This action is asynchronous. You can poll on ListMachineTags to wait
|
|
1387
|
+
# for the update to be complete (the triton instance tag set -w,--wait
|
|
1388
|
+
# option does this).
|
|
1389
|
+
#
|
|
1390
|
+
# ==== Attributes
|
|
1391
|
+
#
|
|
1392
|
+
# * +:machine - String id of the machine
|
|
1393
|
+
def list_machine_tags(machine, opts= {})
|
|
1394
|
+
raise ArgumentError unless machine.is_a? String
|
|
1395
|
+
c = @client["#{@account}/machines/#{machine}/tags"]
|
|
1396
|
+
attempt(opts[:attempts]) do
|
|
1397
|
+
do_get(c, headers)
|
|
1398
|
+
end
|
|
1399
|
+
|
|
1400
|
+
end
|
|
1401
|
+
|
|
1402
|
+
# Returns the value for a single tag on this instance.
|
|
1403
|
+
#
|
|
1404
|
+
# Typically one calls CloudAPI endpoints with Accept: application/json.
|
|
1405
|
+
# This endpoint can be called that way, or alternatively with Accept:
|
|
1406
|
+
# text/plain to get the non-JSON value in the response.
|
|
1407
|
+
#
|
|
1408
|
+
# ==== Attributes
|
|
1409
|
+
#
|
|
1410
|
+
# * +:machine - String id of the machine
|
|
1411
|
+
# * +:tag - String tag to get
|
|
1412
|
+
def get_machine_tags(machine, tag, opts= {})
|
|
1413
|
+
raise ArgumentError unless machine.is_a? String
|
|
1414
|
+
raise ArgumentError unless tag.is_a? String
|
|
1415
|
+
c = @client["#{@account}/machines/#{machine}/tags/#{tag}"]
|
|
1416
|
+
attempt(opts[:attempts]) do
|
|
1417
|
+
do_get(c, headers)
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1420
|
+
end
|
|
1421
|
+
|
|
1422
|
+
# Deletes a single tag from this instance.
|
|
1423
|
+
#
|
|
1424
|
+
# Note: This action is asynchronous. You can poll on ListMachineTags to wait
|
|
1425
|
+
# for the update to be complete (the triton instance tag delete -w,--wait
|
|
1426
|
+
# option does this).
|
|
1427
|
+
#
|
|
1428
|
+
# ==== Attributes
|
|
1429
|
+
#
|
|
1430
|
+
# * +:machine - String id of the machine
|
|
1431
|
+
# * +:tag - String tag to get
|
|
1432
|
+
def delete_machine_tag(machine, tag, opts= {})
|
|
1433
|
+
raise ArgumentError unless machine.is_a? String
|
|
1434
|
+
raise ArgumentError unless tag.is_a? String
|
|
1435
|
+
c = @client["#{@account}/machines/#{machine}/tags/#{tag}"]
|
|
1436
|
+
attempt(opts[:attempts]) do
|
|
1437
|
+
do_delete(c, headers)
|
|
1438
|
+
end
|
|
1439
|
+
|
|
1440
|
+
end
|
|
1441
|
+
|
|
1442
|
+
# Deletes all tags from an instance.
|
|
1443
|
+
#
|
|
1444
|
+
# Note: This action is asynchronous. You can poll on ListMachineTags to wait
|
|
1445
|
+
# for the update to be complete (the triton instance tag delete -w,--wait
|
|
1446
|
+
# option does this).
|
|
1447
|
+
#
|
|
1448
|
+
# ==== Attributes
|
|
1449
|
+
#
|
|
1450
|
+
# * +:machine - String id of the machine
|
|
1451
|
+
def delete_machine_tags(machine, opts= {})
|
|
1452
|
+
raise ArgumentError unless machine.is_a? String
|
|
1453
|
+
c = @client["#{@account}/machines/#{machine}/tags"]
|
|
1454
|
+
attempt(opts[:attempts]) do
|
|
1455
|
+
do_delete(c, headers)
|
|
1456
|
+
end
|
|
1457
|
+
|
|
1458
|
+
end
|
|
1459
|
+
|
|
1460
|
+
# Allows you to completely destroy an instance.
|
|
1461
|
+
#
|
|
1462
|
+
# ==== Attributes
|
|
1463
|
+
#
|
|
1464
|
+
# * +:machine - String id of the machine
|
|
1465
|
+
def delete_machine(machine, opts= {})
|
|
1466
|
+
raise ArgumentError unless machine.is_a? String
|
|
1467
|
+
c = @client["#{@account}/machines/#{machine}"]
|
|
1468
|
+
attempt(opts[:attempts]) do
|
|
1469
|
+
do_delete(c, headers)
|
|
1470
|
+
end
|
|
1471
|
+
|
|
1472
|
+
end
|
|
1473
|
+
|
|
1474
|
+
# Provides a list of an instance's accomplished actions. Results are sorted
|
|
1475
|
+
# from newest to oldest action.
|
|
1476
|
+
#
|
|
1477
|
+
# Note that the complete audit trail is returned only if the instance history
|
|
1478
|
+
# and job records have not been purged from Triton.
|
|
1479
|
+
#
|
|
1480
|
+
# ==== Attributes
|
|
1481
|
+
#
|
|
1482
|
+
# * +:machine - String id of the machine
|
|
1483
|
+
def machine_audit(machine, opts= {})
|
|
1484
|
+
raise ArgumentError unless machine.is_a? String
|
|
1485
|
+
c = @client["#{@account}/machines/#{machine}/audit"]
|
|
1486
|
+
attempt(opts[:attempts]) do
|
|
1487
|
+
do_get(c, headers)
|
|
1488
|
+
end
|
|
1489
|
+
|
|
1490
|
+
end
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
# ---------------------------------------------------------------------------
|
|
1494
|
+
protected
|
|
1495
|
+
|
|
1496
|
+
# Executes a block. If there is a connection- or corruption-related exception
|
|
1497
|
+
# the block will be reexecuted up to the `tries' argument. It will sleep
|
|
1498
|
+
# for an exponentially-increasing number of seconds between retries.
|
|
1499
|
+
def attempt(tries, &blk)
|
|
1500
|
+
if tries
|
|
1501
|
+
raise ArgumentError unless tries > 0
|
|
1502
|
+
else
|
|
1503
|
+
tries ||= @attempts
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
attempt = 1
|
|
1507
|
+
|
|
1508
|
+
while true
|
|
1509
|
+
begin
|
|
1510
|
+
return yield blk
|
|
1511
|
+
rescue Errno::ECONNREFUSED, RestClient::ServerBrokeConnection
|
|
1512
|
+
raise e if attempt == tries
|
|
1513
|
+
sleep 2 ** attempt
|
|
1514
|
+
attempt += 1
|
|
1515
|
+
end
|
|
1516
|
+
end
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
# Creates a qualified user path consisting of the user and subuser if the
|
|
1520
|
+
# subuser is present. Otherwise, it returns the user
|
|
1521
|
+
def user_path
|
|
1522
|
+
@subuser ? "#{@account}/#{@subuser}" : @account
|
|
1523
|
+
end
|
|
1524
|
+
|
|
1525
|
+
# :m_some_header becomes "M-Some-Header"
|
|
1526
|
+
def symbol_to_header(header_symbol)
|
|
1527
|
+
header_symbol.to_s.split('_').map(&:capitalize).join('-')
|
|
1528
|
+
end
|
|
1529
|
+
|
|
1530
|
+
# Creates headers to be given to the HTTP client and sent to the Manta
|
|
1531
|
+
# service. The most important is the Authorization header, without which
|
|
1532
|
+
# none of this class would work.
|
|
1533
|
+
def gen_headers(opts)
|
|
1534
|
+
now = Time.now.httpdate
|
|
1535
|
+
sig = gen_signature('date: ' + now)
|
|
1536
|
+
|
|
1537
|
+
headers = { "Date" => now,
|
|
1538
|
+
"Authorization" => sig,
|
|
1539
|
+
"User-Agent" => HTTP_AGENT,
|
|
1540
|
+
"Api-Version" => "~#{CLOUDAPI_VERSION}"
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
return headers
|
|
1544
|
+
end
|
|
1545
|
+
|
|
1546
|
+
# Given a chunk of data, creates an HTTP signature which the Manta service
|
|
1547
|
+
# understands and uses for authentication.
|
|
1548
|
+
def gen_signature(data)
|
|
1549
|
+
raise ArgumentError unless data
|
|
1550
|
+
|
|
1551
|
+
sig = @priv_key.sign(@digest, data)
|
|
1552
|
+
base64sig = Base64.strict_encode64(sig)
|
|
1553
|
+
|
|
1554
|
+
return HTTP_SIGNATURE % [user_path, @fingerprint, @digest_name, base64sig]
|
|
1555
|
+
end
|
|
1556
|
+
|
|
1557
|
+
# Returns a full URL for a given path to an object.
|
|
1558
|
+
def cloudapi_url(path)
|
|
1559
|
+
## TODO: Add this check
|
|
1560
|
+
#raise ArgumentError unless path =~ CLOUDAPI_PATH_REGEX
|
|
1561
|
+
URI.encode(path)
|
|
1562
|
+
end
|
|
1563
|
+
|
|
1564
|
+
# Raises an appropriate exception given the HTTP response. If a 40* is
|
|
1565
|
+
# returned, attempts to look up an appropriate error class and raise,
|
|
1566
|
+
# otherwise raises an UnknownError.
|
|
1567
|
+
def raise_error(result)
|
|
1568
|
+
raise unless result.is_a? RestClient::Response
|
|
1569
|
+
|
|
1570
|
+
err = JSON.parse(result.body)
|
|
1571
|
+
klass = CloudApiClient.const_get err['code']
|
|
1572
|
+
raise klass, err['message']
|
|
1573
|
+
rescue NameError, TypeError, JSON::ParserError
|
|
1574
|
+
raise UnknownError, result.status.to_s + ': ' + result.body
|
|
1575
|
+
end
|
|
1576
|
+
|
|
1577
|
+
|
|
1578
|
+
#
|
|
1579
|
+
# do_get abstraction method to GET request
|
|
1580
|
+
#
|
|
1581
|
+
def do_get(c, headers)
|
|
1582
|
+
raise unless c.is_a? RestClient::Resource
|
|
1583
|
+
result = c.get(headers)
|
|
1584
|
+
raise unless result.is_a? RestClient::Response
|
|
1585
|
+
|
|
1586
|
+
if result.code == 200
|
|
1587
|
+
return JSON.parse(result.body)
|
|
1588
|
+
end
|
|
1589
|
+
|
|
1590
|
+
raise_error(result)
|
|
1591
|
+
end
|
|
1592
|
+
|
|
1593
|
+
#
|
|
1594
|
+
# do_post abstraction method to POST request
|
|
1595
|
+
#
|
|
1596
|
+
def do_post(c, headers, payload)
|
|
1597
|
+
raise unless c.is_a? RestClient::Resource
|
|
1598
|
+
result = c.post(payload.except!(:attempts), headers)
|
|
1599
|
+
raise unless result.is_a? RestClient::Response
|
|
1600
|
+
|
|
1601
|
+
if result.code == 200
|
|
1602
|
+
return JSON.parse(result.body)
|
|
1603
|
+
end
|
|
1604
|
+
|
|
1605
|
+
raise_error(result)
|
|
1606
|
+
end
|
|
1607
|
+
|
|
1608
|
+
#
|
|
1609
|
+
# do_put abstraction method to PUT request
|
|
1610
|
+
#
|
|
1611
|
+
def do_put(c, headers, payload)
|
|
1612
|
+
raise unless c.is_a? RestClient::Resource
|
|
1613
|
+
result = c.put(payload.except!(:attempts), headers)
|
|
1614
|
+
raise unless result.is_a? RestClient::Response
|
|
1615
|
+
|
|
1616
|
+
if result.code == 200
|
|
1617
|
+
return JSON.parse(result.body)
|
|
1618
|
+
end
|
|
1619
|
+
|
|
1620
|
+
raise_error(result)
|
|
1621
|
+
end
|
|
1622
|
+
#
|
|
1623
|
+
# do_delete abstraction method to delete request
|
|
1624
|
+
#
|
|
1625
|
+
def do_delete(c, headers)
|
|
1626
|
+
raise unless c.is_a? RestClient::Resource
|
|
1627
|
+
result = c.delete(headers)
|
|
1628
|
+
raise unless result.is_a? RestClient::Response
|
|
1629
|
+
|
|
1630
|
+
if result.code == 204
|
|
1631
|
+
return true
|
|
1632
|
+
end
|
|
1633
|
+
|
|
1634
|
+
raise_error(result)
|
|
1635
|
+
end
|
|
1636
|
+
|
|
1637
|
+
#
|
|
1638
|
+
# do_head abstraction method to head request
|
|
1639
|
+
#
|
|
1640
|
+
def do_head(c, headers)
|
|
1641
|
+
raise unless c.is_a? RestClient::Resource
|
|
1642
|
+
result = c.head(headers)
|
|
1643
|
+
raise unless result.is_a? RestClient::Response
|
|
1644
|
+
|
|
1645
|
+
if result.code == 200
|
|
1646
|
+
return JSON.parse(result.body)
|
|
1647
|
+
end
|
|
1648
|
+
|
|
1649
|
+
raise_error(result)
|
|
1650
|
+
end
|
|
1651
|
+
#
|
|
1652
|
+
# Validate input parameters
|
|
1653
|
+
# TODO: Not used yet.
|
|
1654
|
+
#
|
|
1655
|
+
def validate_parameters(query_parameters, valid_parameters, opts)
|
|
1656
|
+
raise unless query_parameters.is_a? Hash
|
|
1657
|
+
raise unless valid_parameters.is_a? Hash
|
|
1658
|
+
raise unless opts.is_a? Hash
|
|
1659
|
+
opts.each do | key, val |
|
|
1660
|
+
puts "key = #{key};"
|
|
1661
|
+
end
|
|
1662
|
+
end
|
|
1663
|
+
|
|
1664
|
+
end
|
|
1665
|
+
end
|