hyperkit 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +23 -0
- data/Guardfile +43 -0
- data/LICENSE.txt +47 -0
- data/README.md +341 -0
- data/Rakefile +6 -0
- data/Vagrantfile +123 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/hyperkit.gemspec +33 -0
- data/lib/hyperkit.rb +58 -0
- data/lib/hyperkit/client.rb +82 -0
- data/lib/hyperkit/client/certificates.rb +102 -0
- data/lib/hyperkit/client/containers.rb +1100 -0
- data/lib/hyperkit/client/images.rb +672 -0
- data/lib/hyperkit/client/networks.rb +47 -0
- data/lib/hyperkit/client/operations.rb +123 -0
- data/lib/hyperkit/client/profiles.rb +59 -0
- data/lib/hyperkit/configurable.rb +110 -0
- data/lib/hyperkit/connection.rb +196 -0
- data/lib/hyperkit/default.rb +140 -0
- data/lib/hyperkit/error.rb +267 -0
- data/lib/hyperkit/middleware/follow_redirects.rb +131 -0
- data/lib/hyperkit/response/raise_error.rb +47 -0
- data/lib/hyperkit/version.rb +3 -0
- metadata +116 -0
@@ -0,0 +1,672 @@
|
|
1
|
+
module Hyperkit
|
2
|
+
|
3
|
+
class Client
|
4
|
+
|
5
|
+
# Methods for the images API
|
6
|
+
#
|
7
|
+
# @see Hyperkit::Client
|
8
|
+
# @see https://github.com/lxc/lxd/blob/master/specs/rest-api.md
|
9
|
+
module Images
|
10
|
+
|
11
|
+
# @!group Retrieval
|
12
|
+
|
13
|
+
# List of images on the server (public or private)
|
14
|
+
#
|
15
|
+
# @return [Array<String>] An array of image fingerprints
|
16
|
+
#
|
17
|
+
# @example Get list of images
|
18
|
+
# Hyperkit.images #=> ["54c8caac1f61901ed86c68f24af5f5d3672bdc62c71d04f06df3a59e95684473",
|
19
|
+
# "97d97a3d1d053840ca19c86cdd0596cf1be060c5157d31407f2a4f9f350c78cc"]
|
20
|
+
def images
|
21
|
+
response = get(images_path)
|
22
|
+
response.metadata.map { |path| path.split('/').last }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get information on an image by its fingerprint
|
26
|
+
#
|
27
|
+
# @param fingerprint [String] The image's fingerprint. Can be a prefix of a fingerprint, as long as it is unambiguous.
|
28
|
+
# @param options [Hash] Additional data to be passed
|
29
|
+
# @option options [String] :secret Secret to access private image by untrusted client
|
30
|
+
# @return [Sawyer::Resource] Image information
|
31
|
+
#
|
32
|
+
# @example Get information about an image on images.linuxcontainers.org
|
33
|
+
# Hyperkit.api_endpoint = "https://images.linuxcontainers.org:8443"
|
34
|
+
# Hyperkit.image("45bcc353f629b23ce30ef4cca14d2a4990c396d85ea68905795cc7579c145123") #=> {
|
35
|
+
# :properties=>{:description=>"Centos 6 (amd64) (20160314_02:16)"},
|
36
|
+
# :expires_at=>1970-01-01 00:00:00 UTC,
|
37
|
+
# :filename=>"centos-6-amd64-default-20160314_02:16.tar.xz",
|
38
|
+
# :uploaded_at=>2016-03-15 01:20:10 UTC,
|
39
|
+
# :size=>54717798,
|
40
|
+
# :public=>true,
|
41
|
+
# :architecture=>"x86_64",
|
42
|
+
# :aliases=>[],
|
43
|
+
# :created_at=>2016-03-15 01:20:10 UTC,
|
44
|
+
# :fingerprint=> "45bcc353f629b23ce30ef4cca14d2a4990c396d85ea68905795cc7579c145123"
|
45
|
+
# }
|
46
|
+
#
|
47
|
+
# @example Get information about an image via an image fingerprint
|
48
|
+
# Hyperkit.image("45b")
|
49
|
+
#
|
50
|
+
# @example Get information about a private image using a secret (created with {#create_image_secret}):
|
51
|
+
# Hyperkit.image("45bcc353f629b23ce30ef4cca14d2a4990c396d85ea68905795cc7579c145123",
|
52
|
+
# secret: "secret-issued-by-create_image_secret")
|
53
|
+
def image(fingerprint, options={})
|
54
|
+
|
55
|
+
url = image_path(fingerprint)
|
56
|
+
url << "?secret=#{options[:secret]}" if options[:secret]
|
57
|
+
|
58
|
+
get(url).metadata
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get information on an image by one of its aliases
|
62
|
+
#
|
63
|
+
# @param alias_name [String] An alias of the image
|
64
|
+
# @param options [Hash] Additional data to be passed
|
65
|
+
# @option options [String] :secret Secret to access private image by untrusted client
|
66
|
+
# @return [Sawyer::Resource] Image information
|
67
|
+
#
|
68
|
+
# @example Get information about an image on images.linuxcontainers.org
|
69
|
+
# Hyperkit.api_endpoint = "https://images.linuxcontainers.org:8443"
|
70
|
+
# Hyperkit.image_by_alias("ubuntu/xenial/amd64") #=> {
|
71
|
+
# :fingerprint=> "878cf0c70e14fec80aaf4d5e923670e68c45aa89fb05a481019bf086aec42649",
|
72
|
+
# :expires_at=>1970-01-01 00:00:00 UTC,
|
73
|
+
# :size=>88239818,
|
74
|
+
# :architecture=>"x86_64",
|
75
|
+
# :created_at=>2016-03-16 04:53:46 UTC,
|
76
|
+
# :filename=>"ubuntu-xenial-amd64-default-20160316_03:49.tar.xz",
|
77
|
+
# :uploaded_at=>2016-03-16 04:53:46 UTC,
|
78
|
+
# :aliases=>
|
79
|
+
# [{:name=>"ubuntu/xenial/amd64/default",
|
80
|
+
# :target=>"ubuntu/xenial/amd64/default",
|
81
|
+
# :description=>"Ubuntu xenial (amd64) (default)"},
|
82
|
+
# {:name=>"ubuntu/xenial/amd64",
|
83
|
+
# :target=>"ubuntu/xenial/amd64",
|
84
|
+
# :description=>"Ubuntu xenial (amd64)"}],
|
85
|
+
# :properties=>{:description=>"Ubuntu xenial (amd64) (20160316_03:49)"},
|
86
|
+
# :public=>true}
|
87
|
+
# }
|
88
|
+
#
|
89
|
+
# @example Get information about a private image using a secret (created with {#create_image_secret}):
|
90
|
+
# Hyperkit.image_by_alias("ubuntu/xenial/amd64",
|
91
|
+
# secret: "secret-issued-by-create_image_secret")
|
92
|
+
def image_by_alias(alias_name, options={})
|
93
|
+
a = image_alias(alias_name)
|
94
|
+
image(a.target, options)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @!endgroup
|
98
|
+
|
99
|
+
# @!group Creation and deletion
|
100
|
+
|
101
|
+
# Upload an image from a local file
|
102
|
+
#
|
103
|
+
# @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
|
104
|
+
#
|
105
|
+
# @param file [String] Path of image tarball to be uploaded
|
106
|
+
# @param options [Hash] Additional data to be passed
|
107
|
+
# @option options [String] :fingerprint SHA-256 fingerprint of the tarball. If the fingerprint of the uploaded image does not match, the image will not be saved on the server.
|
108
|
+
# @option options [String] :filename Tarball filename to store with the image on the server and used when exporting the image (default: name of file being uploaded).
|
109
|
+
# @option options [Boolean] :public Whether or not the image should be publicly-accessible by unauthenticated users (default: false).
|
110
|
+
# @option options [Hash] :properties Hash of additional properties to store with the image
|
111
|
+
# @option options [Boolean] :sync If <code>false</code>, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If <code>true</code>, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}.
|
112
|
+
# @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
|
113
|
+
#
|
114
|
+
# @example Upload a private image
|
115
|
+
# Hyperkit.create_image_from_file("/tmp/ubuntu-14.04-amd64-lxc.tar.gz")
|
116
|
+
#
|
117
|
+
# @example Upload a public image
|
118
|
+
# Hyperkit.create_image_from_file("/tmp/ubuntu-14.04-amd64-lxc.tar.gz", public: true)
|
119
|
+
#
|
120
|
+
# @example Store properties with the uploaded image, and override its filename
|
121
|
+
# Hyperkit.create_image_from_file("/tmp/ubuntu-14.04-amd64-lxc.tar.gz",
|
122
|
+
# filename: "ubuntu-trusty.tar.gz",
|
123
|
+
# properties: {
|
124
|
+
# os: "ubuntu"
|
125
|
+
# codename: "trusty"
|
126
|
+
# version: "14.04"
|
127
|
+
# }
|
128
|
+
# )
|
129
|
+
#
|
130
|
+
# @example Upload an image, but only store it if the uploaded file has the same fingerprint
|
131
|
+
# Hyperkit.create_image_from_file("/tmp/ubuntu-14.04-amd64-lxc.tar.gz",
|
132
|
+
# fingerprint: "3dc37b8185cc811bcad5e319a9f38daeae823065dd6264334ac07b8324a42f2d")
|
133
|
+
def create_image_from_file(file, options={})
|
134
|
+
headers = { "Content-Type" => "application/octet-stream" }
|
135
|
+
|
136
|
+
headers["X-LXD-fingerprint"] = options[:fingerprint] if options[:fingerprint]
|
137
|
+
headers["X-LXD-filename"] = options[:filename] || File.basename(file)
|
138
|
+
headers["X-LXD-public"] = 1.to_s if options[:public]
|
139
|
+
|
140
|
+
if options[:properties]
|
141
|
+
properties = options[:properties].map do |k,v|
|
142
|
+
"#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}"
|
143
|
+
end
|
144
|
+
headers["X-LXD-properties"] = properties.join("&")
|
145
|
+
end
|
146
|
+
|
147
|
+
response = post(images_path, {
|
148
|
+
raw_body: File.read(file),
|
149
|
+
headers: headers
|
150
|
+
}).metadata
|
151
|
+
|
152
|
+
handle_async(response, options[:sync])
|
153
|
+
end
|
154
|
+
|
155
|
+
# Import an image from a remote server
|
156
|
+
#
|
157
|
+
# @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
|
158
|
+
#
|
159
|
+
# @param server [String] Source server
|
160
|
+
# @param options [Hash] Additional data to be passed
|
161
|
+
# @option options [String] :alias Alias of the source image to import. <b>Either <code>:alias</code> or <code>:fingerprint</code> must be specified</b>.
|
162
|
+
# @option options [Boolean] :auto_update Whether or not the image should be automatically updated from the source server (source image must be public and must be referenced by alias -- not fingerprint; default: <code>false</code>).
|
163
|
+
# @option options [String] :certificate PEM certificate to use to authenticate with the source server. If not specified, and the source image is private, the target LXD server's certificate is used for authentication.
|
164
|
+
# @option options [String] :filename Tarball filename to store with the image on the server and used when exporting the image (default: filename retrieved from the source server).
|
165
|
+
# @option options [String] :fingerprint SHA-256 fingerprint of the source image to import. Depending on the version of the source LXD server, you may be able to specify an image by its fingerprint prefix rather than a full fingerprint, as long as the prefix is unambiguous. <b>Either <code>:alias</code> or <code>:fingerprint</code> must be specified</b>.
|
166
|
+
# @option options [String] :protocol Protocol to use in transferring the image (<code>lxd</code> or <code>simplestreams</code>; defaults to <code>lxd</code>)
|
167
|
+
# @option options [Boolean] :public Whether or not the image should be publicly-accessible by unauthenticated users (default: <code>false</code>).
|
168
|
+
# @option options [Hash] :properties Hash of additional properties to store with the image
|
169
|
+
# @option options [String] :secret Secret to use to retrieve the image
|
170
|
+
# @option options [Boolean] :sync If <code>false</code>, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If <code>true</code>, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}.
|
171
|
+
# @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
|
172
|
+
#
|
173
|
+
# @example Import image by alias
|
174
|
+
# Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
|
175
|
+
# alias: "ubuntu/xenial/amd64")
|
176
|
+
#
|
177
|
+
# @example Import image by fingerprint
|
178
|
+
# Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
|
179
|
+
# fingerprint: "b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b")
|
180
|
+
#
|
181
|
+
# @example Import image and automatically update it when it is updated on the remote server
|
182
|
+
# Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
|
183
|
+
# alias: "ubuntu/xenial/amd64",
|
184
|
+
# auto_update: true)
|
185
|
+
#
|
186
|
+
# @example Store properties with the imported image (will be applied on top of any source properties)
|
187
|
+
# Hyperkit.create_image_from_remote("https://images.linuxcontainers.org:8443",
|
188
|
+
# alias: "ubuntu/xenial/amd64",
|
189
|
+
# properties: {
|
190
|
+
# hello: "world"
|
191
|
+
# }
|
192
|
+
# )
|
193
|
+
def create_image_from_remote(server, options={})
|
194
|
+
|
195
|
+
opts = options.slice(:filename, :public, :auto_update)
|
196
|
+
|
197
|
+
if options[:protocol] && ! %w[lxd simplestreams].include?(options[:protocol])
|
198
|
+
raise Hyperkit::InvalidProtocol.new("Invalid protocol. Valid choices: lxd, simplestreams")
|
199
|
+
end
|
200
|
+
|
201
|
+
opts[:source] = options.slice(:secret, :protocol, :certificate)
|
202
|
+
opts[:source].merge!({
|
203
|
+
type: "image",
|
204
|
+
mode: "pull",
|
205
|
+
server: server
|
206
|
+
})
|
207
|
+
|
208
|
+
if options[:alias].nil? && options[:fingerprint].nil?
|
209
|
+
raise Hyperkit::ImageIdentifierRequired.new("Please specify either :alias or :fingerprint")
|
210
|
+
end
|
211
|
+
|
212
|
+
opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
|
213
|
+
|
214
|
+
if options[:alias]
|
215
|
+
opts[:source][:alias] = options[:alias]
|
216
|
+
else
|
217
|
+
opts[:source][:fingerprint] = options[:fingerprint]
|
218
|
+
end
|
219
|
+
|
220
|
+
response = post(images_path, opts).metadata
|
221
|
+
handle_async(response, options[:sync])
|
222
|
+
end
|
223
|
+
|
224
|
+
# Import an image from a remote URL.
|
225
|
+
#
|
226
|
+
# Note: the URL passed to this method is <b>not</b> the URL of a tarball.
|
227
|
+
# Instead, the URL must return the following headers:
|
228
|
+
#
|
229
|
+
# * <code>LXD-Image-URL</code> - URL of the image tarball
|
230
|
+
# * <code>LXD-Image-Hash</code> - SHA-256 fingerprint of the image tarball
|
231
|
+
#
|
232
|
+
# The LXD server will first access the URL to obtain the values of these headers.
|
233
|
+
# It will then download the tarball specified in the <code>LXD-Image-URL</code> header
|
234
|
+
# and verify that its fingerprint matches the value in the <code>LXD-Image-Hash</code> header.
|
235
|
+
# If they match, the image will be imported.
|
236
|
+
#
|
237
|
+
# In Apache, you can set this up using a <code>.htaccess</code> file. See the <b>Examples</b> section
|
238
|
+
# for a sample.
|
239
|
+
#
|
240
|
+
# @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
|
241
|
+
#
|
242
|
+
# @param url [String] URL containing image headers (see above)
|
243
|
+
# @param options [Hash] Additional data to be passed
|
244
|
+
# @option options [String] :filename Tarball filename to store with the image on the server and used when exporting the image (default: name of file being uploaded).
|
245
|
+
# @option options [Boolean] :public Whether or not the image should be publicly-accessible by unauthenticated users (default: false).
|
246
|
+
# @option options [Hash] :properties Hash of additional properties to store with the image
|
247
|
+
# @option options [Boolean] :sync If <code>false</code>, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If <code>true</code>, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}.
|
248
|
+
# @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
|
249
|
+
#
|
250
|
+
# @example Import a private image
|
251
|
+
# Hyperkit.create_image_from_url("http://example.com/ubuntu-14.04")
|
252
|
+
#
|
253
|
+
# @example Import a public image
|
254
|
+
# Hyperkit.create_image_from_url("http://example.com/ubuntu-14.04", public: true)
|
255
|
+
#
|
256
|
+
# @example Store properties with the uploaded image, and override its filename
|
257
|
+
# Hyperkit.create_image_from_url("http://example.com/ubuntu-14.04",
|
258
|
+
# filename: "ubuntu-trusty.tar.gz",
|
259
|
+
# properties: {
|
260
|
+
# os: "ubuntu"
|
261
|
+
# codename: "trusty"
|
262
|
+
# version: "14.04"
|
263
|
+
# }
|
264
|
+
# )
|
265
|
+
#
|
266
|
+
# @example Example .htaccess file on the source server, located in the /ubuntu-14.04 directory:
|
267
|
+
# # Put an empty index.html in the ubuntu-14.04 directory.
|
268
|
+
# # Note that the 'headers' Apache module must be enabled (a2enmod headers).
|
269
|
+
# <Files index.html>
|
270
|
+
#
|
271
|
+
# # Image of the file to download
|
272
|
+
# Header set LXD-Image-URL http://example.com/ubuntu-14.04/ubuntu-14.04-amd64-lxc.tar.xz
|
273
|
+
#
|
274
|
+
# # SHA-256 of the file to download
|
275
|
+
# Header set LXD-Image-Hash 097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415
|
276
|
+
# </Files>
|
277
|
+
def create_image_from_url(url, options={})
|
278
|
+
|
279
|
+
opts = options.slice(:filename, :public)
|
280
|
+
opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
|
281
|
+
opts[:source] = {
|
282
|
+
type: "url",
|
283
|
+
url: url
|
284
|
+
}
|
285
|
+
|
286
|
+
response = post(images_path, opts).metadata
|
287
|
+
handle_async(response, options[:sync])
|
288
|
+
end
|
289
|
+
|
290
|
+
# Create an image from an existing container.
|
291
|
+
#
|
292
|
+
# @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
|
293
|
+
#
|
294
|
+
# @param name [String] Source container name
|
295
|
+
# @param options [Hash] Additional data to be passed
|
296
|
+
# @option options [String] :filename Tarball filename to store with the image on the server and used when exporting the image (default: name of file being uploaded).
|
297
|
+
# @option options [Boolean] :public Whether or not the image should be publicly-accessible by unauthenticated users (default: false).
|
298
|
+
# @option options [Hash] :properties Hash of additional properties to store with the image
|
299
|
+
# @option options [Boolean] :sync If <code>false</code>, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If <code>true</code>, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}.
|
300
|
+
# @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
|
301
|
+
#
|
302
|
+
# @example Create a private image from container 'test-container'
|
303
|
+
# Hyperkit.create_image_from_container("test-container")
|
304
|
+
#
|
305
|
+
# @example Create a public image from container 'test-container'
|
306
|
+
# Hyperkit.create_image_from_container("test-container", public: true)
|
307
|
+
#
|
308
|
+
# @example Store properties with the new image, and override its filename
|
309
|
+
# Hyperkit.create_image_from_container("test-container",
|
310
|
+
# filename: "ubuntu-trusty.tar.gz",
|
311
|
+
# properties: {
|
312
|
+
# os: "ubuntu"
|
313
|
+
# codename: "trusty"
|
314
|
+
# version: "14.04"
|
315
|
+
# }
|
316
|
+
# )
|
317
|
+
def create_image_from_container(name, options={})
|
318
|
+
|
319
|
+
opts = options.slice(:filename, :public, :description)
|
320
|
+
opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
|
321
|
+
opts[:source] = {
|
322
|
+
type: "container",
|
323
|
+
name: name
|
324
|
+
}
|
325
|
+
|
326
|
+
response = post(images_path, opts).metadata
|
327
|
+
handle_async(response, options[:sync])
|
328
|
+
end
|
329
|
+
|
330
|
+
# Create an image from an existing snapshot.
|
331
|
+
#
|
332
|
+
# @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
|
333
|
+
#
|
334
|
+
# @param container [String] Source container name
|
335
|
+
# @param snapshot [String] Source snapshot name
|
336
|
+
# @param options [Hash] Additional data to be passed
|
337
|
+
# @option options [String] :filename Tarball filename to store with the image on the server and used when exporting the image (default: name of file being uploaded).
|
338
|
+
# @option options [Boolean] :public Whether or not the image should be publicly-accessible by unauthenticated users (default: false).
|
339
|
+
# @option options [Hash] :properties Hash of additional properties to store with the image
|
340
|
+
# @option options [Boolean] :sync If <code>false</code>, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If <code>true</code>, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}.
|
341
|
+
# @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
|
342
|
+
#
|
343
|
+
# @example Create a private image from snapshot 'test-container/snapshot1'
|
344
|
+
# Hyperkit.create_image_from_snapshot("test-container", "snapshot1")
|
345
|
+
#
|
346
|
+
# @example Create a public image from snapshot 'test-container/snapshot1'
|
347
|
+
# Hyperkit.create_image_from_snapshot("test-container", "snapshot1", public: true)
|
348
|
+
#
|
349
|
+
# @example Store properties with the new image, and override its filename
|
350
|
+
# Hyperkit.create_image_from_snapshot("test-container", "snapshot1",
|
351
|
+
# filename: "ubuntu-trusty.tar.gz",
|
352
|
+
# properties: {
|
353
|
+
# os: "ubuntu"
|
354
|
+
# codename: "trusty"
|
355
|
+
# version: "14.04"
|
356
|
+
# }
|
357
|
+
# )
|
358
|
+
def create_image_from_snapshot(container, snapshot, options={})
|
359
|
+
|
360
|
+
opts = options.slice(:filename, :public, :description)
|
361
|
+
opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
|
362
|
+
opts[:source] = {
|
363
|
+
type: "snapshot",
|
364
|
+
name: "#{container}/#{snapshot}"
|
365
|
+
}
|
366
|
+
|
367
|
+
response = post(images_path, opts).metadata
|
368
|
+
handle_async(response, options[:sync])
|
369
|
+
end
|
370
|
+
|
371
|
+
# Delete an image
|
372
|
+
#
|
373
|
+
# @async This method is asynchronous. See {Hyperkit::Configurable#auto_sync} for more information.
|
374
|
+
#
|
375
|
+
# @param fingerprint [String] Fingerprint of image to delete. Can be a prefix of a fingerprint, as long as it is unambiguous.
|
376
|
+
# @param options [Hash] Additional data to be passed
|
377
|
+
# @option options [Boolean] :sync If <code>false</code>, returns an asynchronous operation that must be passed to {Hyperkit::Client::Operations#wait_for_operation}. If <code>true</code>, automatically waits and returns the result of the operation. Defaults to value of {Hyperkit::Configurable#auto_sync}.
|
378
|
+
# @return [Sawyer::Resource] Operation or result, depending value of <code>:sync</code> parameter and/or {Hyperkit::Client::auto_sync}
|
379
|
+
#
|
380
|
+
# @example Delete an image using its fingerprint
|
381
|
+
# image = Hyperkit.image_by_alias("ubuntu/xenial/amd64")
|
382
|
+
# Hyperkit.delete_image(image.fingerprint)
|
383
|
+
#
|
384
|
+
# @example Delete an image using a prefix of its fingerprint
|
385
|
+
# Hyperkit.delete_image("b41")
|
386
|
+
def delete_image(fingerprint, options={})
|
387
|
+
response = delete(image_path(fingerprint)).metadata
|
388
|
+
handle_async(response, options[:sync])
|
389
|
+
end
|
390
|
+
|
391
|
+
# @!endgroup
|
392
|
+
|
393
|
+
# @!group Editing
|
394
|
+
|
395
|
+
# Update the attributes of an image
|
396
|
+
#
|
397
|
+
# @param fingerprint [String] Fingerprint of image to update
|
398
|
+
# @param options [Hash] Additional data to be passed
|
399
|
+
# @option options [Boolean] :auto_update Whether or not the image should be automatically updated from the source server (source image must be public and must have been referenced by alias when originally created -- not fingerprint)
|
400
|
+
# @option options [Hash] :properties Hash of additional properties to store with the image. Existing properties are removed. See the Examples section.
|
401
|
+
# @option options [Boolean] :public Whether or not the image should be publicly-accessible to unauthenticated users
|
402
|
+
# @return [Sawyer::Resource]
|
403
|
+
#
|
404
|
+
# @example Set image to be publicly-accessible
|
405
|
+
# Hyperkit.update_image("b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b",
|
406
|
+
# public: true)
|
407
|
+
#
|
408
|
+
# @example Overwrite image properties (removes all existing properties, sets "hello" property to "world")
|
409
|
+
# Hyperkit.update_image("b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b",
|
410
|
+
# properties: {
|
411
|
+
# hello: "world"
|
412
|
+
# }
|
413
|
+
# )
|
414
|
+
#
|
415
|
+
# @example Update image properties (leaves all existing properties intact, sets "hello" property to "world")
|
416
|
+
# fingerprint = "b1cf3d836196c316897d39872ff25e2d912ea933207b0c591334a67b290a5f1b"
|
417
|
+
#
|
418
|
+
# image = Hyperkit.image(fingerprint)
|
419
|
+
#
|
420
|
+
# Hyperkit.update_image(fingerprint)
|
421
|
+
# image.properties.to_hash.merge({
|
422
|
+
# hello: "world"
|
423
|
+
# })
|
424
|
+
# )
|
425
|
+
def update_image(fingerprint, options={})
|
426
|
+
opts = options.slice(:public, :auto_update)
|
427
|
+
opts[:properties] = stringify_properties(options[:properties]) if options[:properties]
|
428
|
+
|
429
|
+
put(image_path(fingerprint), opts).metadata
|
430
|
+
end
|
431
|
+
|
432
|
+
# Generate a secret for an image that can be used by an untrusted client
|
433
|
+
# to retrieve information on and/or export a private image.
|
434
|
+
#
|
435
|
+
# The secret is automatically invalidated 5 seconds after first using it
|
436
|
+
# (e.g. after calling Hyperkit.image(fingerprint, secret: "...").
|
437
|
+
# This allows one to both retrieve the image information and then export it
|
438
|
+
# with the same secret.
|
439
|
+
#
|
440
|
+
# If you wish to delete a created secret without using it, you can pass the operation
|
441
|
+
# ID returned by this method to {#Hyperkit::Operations::cancel_operation}, as shown
|
442
|
+
# in the examples below.
|
443
|
+
#
|
444
|
+
# @param fingerprint [String] Fingerprint of the image. This can be a prefix of an image's fingerprint, as long as it is unambiguous.
|
445
|
+
# @return [Sawyer::Response] An asynchronous response containing the generated secret
|
446
|
+
# @todo Add test for fingerprint prefix
|
447
|
+
#
|
448
|
+
# @example Generate a secret for an image
|
449
|
+
# response = Hyperkit.create_image_secret("878cf0c70e14fec80aaf4d5e923670e68c45aa89fb05a481019bf086aec42649") #=> {
|
450
|
+
# :id => "c8e949d4-0b6e-45de-83c9-5b886ed0256b",
|
451
|
+
# :class => "token",
|
452
|
+
# :created_at => 2016-04-05 15:12:58 UTC,
|
453
|
+
# :updated_at => 2016-04-05 15:12:58 UTC,
|
454
|
+
# :status => "Running",
|
455
|
+
# :status_code => 103,
|
456
|
+
# :resources => {
|
457
|
+
# :images => ["/1.0/images/097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415"]
|
458
|
+
# },
|
459
|
+
# :metadata => {
|
460
|
+
# :secret => "be517e0a22918980ab76013a78dc55fb62f1d7f1d97f445a77819fa0e643dd4f"
|
461
|
+
# },
|
462
|
+
# :may_cancel => true,
|
463
|
+
# :err => ""
|
464
|
+
# }
|
465
|
+
# secret = response.metadata.secret
|
466
|
+
#
|
467
|
+
# @example Generate a secret for an image using a prefix of its fingerprint
|
468
|
+
# Hyperkit.create_image_secret("878").metadata.secret
|
469
|
+
#
|
470
|
+
# @example Delete a secret for an image without using it
|
471
|
+
# response = Hyperkit.create_image_secret("878")
|
472
|
+
# Hyperkit.cancel_operation(response.id)
|
473
|
+
def create_image_secret(fingerprint)
|
474
|
+
post(File.join(image_path(fingerprint), "secret")).metadata
|
475
|
+
end
|
476
|
+
|
477
|
+
# @!endgroup
|
478
|
+
|
479
|
+
# @!group Export
|
480
|
+
|
481
|
+
# Export an image to a local file.
|
482
|
+
#
|
483
|
+
# @param fingerprint [String] Fingerprint of the image
|
484
|
+
# @param output_dir [String] Output directory
|
485
|
+
# @param options [Hash] Additional data to be passed
|
486
|
+
# @option options [String] :filename Name of file in which to store exported image (default: image filename obtained from the server)
|
487
|
+
# @option options [String] :secret Secret to export private image by untrusted client
|
488
|
+
# @return [String] The name of the file saved
|
489
|
+
#
|
490
|
+
# @example Export image
|
491
|
+
# image = Hyperkit.image_by_alias("busybox/default/amd64")
|
492
|
+
# Hyperkit.export_image(image.fingerprint, "/tmp") => "/tmp/busybox-v1.21.1-lxc.tar.xz"
|
493
|
+
#
|
494
|
+
# @example Override output filename
|
495
|
+
# image = Hyperkit.image_by_alias("busybox/default/amd64")
|
496
|
+
# Hyperkit.export_image(image.fingerprint,
|
497
|
+
# "/tmp", filename: "test.tar.xz") => "/tmp/test.tar.xz"
|
498
|
+
#
|
499
|
+
# @example Export private image via secret (created by {#create_image_secret})
|
500
|
+
# image = Hyperkit.image_by_alias("busybox/default/amd64")
|
501
|
+
# Hyperkit.export_image(image.fingerprint,
|
502
|
+
# "/tmp", secret: "secret-issued-by-create_image_secret") => "/tmp/busybox-v1.21.1-lxc.tar.xz"
|
503
|
+
def export_image(fingerprint, output_dir, options={})
|
504
|
+
|
505
|
+
img = image(fingerprint)
|
506
|
+
filename = options[:filename] || img.filename
|
507
|
+
output_file = File.join(output_dir, filename)
|
508
|
+
|
509
|
+
url = File.join(image_path(fingerprint), "export")
|
510
|
+
url << "?secret=#{options[:secret]}" if options[:secret]
|
511
|
+
|
512
|
+
response = get(url)
|
513
|
+
|
514
|
+
File.open(output_file, "wb") do |f|
|
515
|
+
f.write response
|
516
|
+
end
|
517
|
+
|
518
|
+
output_file
|
519
|
+
|
520
|
+
end
|
521
|
+
|
522
|
+
# @!endgroup
|
523
|
+
|
524
|
+
# @!group Aliases
|
525
|
+
|
526
|
+
# List of image aliases on the server (public or private)
|
527
|
+
#
|
528
|
+
# @return [Array<String>] An array of image aliases
|
529
|
+
#
|
530
|
+
# @example Get list of image aliases
|
531
|
+
# Hyperkit.images #=> [
|
532
|
+
# "ubuntu/xenial/amd64/default",
|
533
|
+
# "ubuntu/xenial/amd64",
|
534
|
+
# "ubuntu/xenial/armhf/default",
|
535
|
+
# "ubuntu/xenial/armhf",
|
536
|
+
# "ubuntu/xenial/i386/default",
|
537
|
+
# "ubuntu/xenial/i386",
|
538
|
+
# "ubuntu/xenial/powerpc/default",
|
539
|
+
# "ubuntu/xenial/powerpc",
|
540
|
+
# "ubuntu/xenial/ppc64el/default",
|
541
|
+
# "ubuntu/xenial/ppc64el",
|
542
|
+
# "ubuntu/xenial/s390x/default",
|
543
|
+
# "ubuntu/xenial/s390x"
|
544
|
+
# ]
|
545
|
+
def image_aliases
|
546
|
+
response = get(image_aliases_path)
|
547
|
+
response.metadata.map { |path| path.sub("#{image_aliases_path}/","") }
|
548
|
+
end
|
549
|
+
|
550
|
+
# Get information on an image alias
|
551
|
+
#
|
552
|
+
# @param alias_name [String] An image alias
|
553
|
+
# @return [Sawyer::Resource] Alias information
|
554
|
+
#
|
555
|
+
# @example Get information about an alias on images.linuxcontainers.org
|
556
|
+
# Hyperkit.api_endpoint = "https://images.linuxcontainers.org:8443"
|
557
|
+
# Hyperkit.image_alias("ubuntu/xenial/amd64/default") #=> {
|
558
|
+
# :name=>"ubuntu/xenial/amd64/default",
|
559
|
+
# :target=>"878cf0c70e14fec80aaf4d5e923670e68c45aa89fb05a481019bf086aec42649"
|
560
|
+
# }
|
561
|
+
def image_alias(alias_name)
|
562
|
+
get(image_alias_path(alias_name)).metadata
|
563
|
+
end
|
564
|
+
|
565
|
+
# Assign an alias for an image
|
566
|
+
#
|
567
|
+
# @param fingerprint [String] Fingerprint of the image
|
568
|
+
# @param alias_name [String] Alias to assign to the image
|
569
|
+
# @param options [Hash] Additional data to be passed
|
570
|
+
# @option options [String] :description Alias description
|
571
|
+
# @return [Sawyer::Resource]
|
572
|
+
#
|
573
|
+
# @example Assign alias "ubuntu/xenial/amd64" to an image
|
574
|
+
# Hyperkit.create_image_alias(
|
575
|
+
# "878cf0c70e14fec80aaf4d5e923670e68c45aa89fb05a481019bf086aec42649",
|
576
|
+
# "ubuntu/xenial/amd64")
|
577
|
+
#
|
578
|
+
# @example Assign alias "ubuntu/xenial/amd64" with a description
|
579
|
+
# Hyperkit.create_image_alias(
|
580
|
+
# "878cf0c70e14fec80aaf4d5e923670e68c45aa89fb05a481019bf086aec42649",
|
581
|
+
# "ubuntu/xenial/amd64",
|
582
|
+
# description: "Ubuntu Xenial amd64")
|
583
|
+
def create_image_alias(fingerprint, alias_name, options={})
|
584
|
+
opts = options.slice(:description).merge({
|
585
|
+
target: fingerprint,
|
586
|
+
name: alias_name
|
587
|
+
})
|
588
|
+
|
589
|
+
post(image_aliases_path, opts).metadata
|
590
|
+
end
|
591
|
+
|
592
|
+
# Delete an alias for an image
|
593
|
+
#
|
594
|
+
# @param alias_name [String] Alias to delete
|
595
|
+
# @return [Sawyer::Resource]
|
596
|
+
#
|
597
|
+
# @example Delete alias "ubuntu/xenial/amd64"
|
598
|
+
# Hyperkit.delete_image_alias("ubuntu/xenial/amd64")
|
599
|
+
def delete_image_alias(alias_name)
|
600
|
+
delete(image_alias_path(alias_name)).metadata
|
601
|
+
end
|
602
|
+
|
603
|
+
# Rename an image alias
|
604
|
+
#
|
605
|
+
# @param old_alias [String] Alias to rename
|
606
|
+
# @param new_alias [String] New alias
|
607
|
+
# @return [Sawyer::Resource]
|
608
|
+
#
|
609
|
+
# @example Rename alias "ubuntu/xenial/amd64" to "ubuntu/xenial/default"
|
610
|
+
# Hyperkit.rename_image_alias("ubuntu/xenial/amd64", "ubuntu/xenial/default")
|
611
|
+
def rename_image_alias(old_alias, new_alias)
|
612
|
+
post(image_alias_path(old_alias), { name: new_alias }).metadata
|
613
|
+
end
|
614
|
+
|
615
|
+
# Update an image alias
|
616
|
+
#
|
617
|
+
# @param alias_name [String] Alias to update
|
618
|
+
# @param options [Hash] Additional data to be passed
|
619
|
+
# @option options [String] :target Image fingerprint
|
620
|
+
# @option options [String] :description Alias description
|
621
|
+
# @return [Sawyer::Resource]
|
622
|
+
#
|
623
|
+
# @example Update alias "ubuntu/xenial/amd64" to point to image "097..."
|
624
|
+
# Hyperkit.update_image_alias("ubuntu/xenial/amd64",
|
625
|
+
# target: "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415")
|
626
|
+
#
|
627
|
+
# @example Update alias "ubuntu/xenial/amd64" with a new description
|
628
|
+
# Hyperkit.update_image_alias("ubuntu/xenial/amd64", description: "Ubuntu 16.04")
|
629
|
+
def update_image_alias(alias_name, options={})
|
630
|
+
|
631
|
+
if options.empty?
|
632
|
+
raise Hyperkit::AliasAttributesRequired.new("At least one of :target or :description required")
|
633
|
+
end
|
634
|
+
|
635
|
+
existing_options = image_alias(alias_name).to_hash
|
636
|
+
opts = existing_options.slice(:description, :target).
|
637
|
+
merge(options.slice(:description, :target))
|
638
|
+
|
639
|
+
put(image_alias_path(alias_name), opts).metadata
|
640
|
+
end
|
641
|
+
|
642
|
+
# @!endgroup
|
643
|
+
|
644
|
+
private
|
645
|
+
|
646
|
+
def image_path(fingerprint)
|
647
|
+
File.join(images_path, fingerprint)
|
648
|
+
end
|
649
|
+
|
650
|
+
def image_alias_path(alias_name)
|
651
|
+
File.join(image_aliases_path, alias_name)
|
652
|
+
end
|
653
|
+
|
654
|
+
def image_aliases_path
|
655
|
+
File.join(images_path, "aliases")
|
656
|
+
end
|
657
|
+
|
658
|
+
def images_path
|
659
|
+
"/1.0/images"
|
660
|
+
end
|
661
|
+
|
662
|
+
# Stringify any property values. LXD returns an error if
|
663
|
+
# integers are passed, for example
|
664
|
+
def stringify_properties(properties)
|
665
|
+
properties.inject({}){|h,(k,v)| h[k.to_s] = v.to_s; h}
|
666
|
+
end
|
667
|
+
|
668
|
+
end
|
669
|
+
|
670
|
+
end
|
671
|
+
|
672
|
+
end
|