visor-image 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/visor +423 -0
- data/bin/visor-image +10 -0
- data/config/server.rb +14 -0
- data/lib/image/auth.rb +147 -0
- data/lib/image/cli.rb +397 -0
- data/lib/image/client.rb +490 -0
- data/lib/image/meta.rb +219 -0
- data/lib/image/routes/delete_all_images.rb +40 -0
- data/lib/image/routes/delete_image.rb +62 -0
- data/lib/image/routes/get_image.rb +78 -0
- data/lib/image/routes/get_images.rb +54 -0
- data/lib/image/routes/get_images_detail.rb +54 -0
- data/lib/image/routes/head_image.rb +51 -0
- data/lib/image/routes/post_image.rb +189 -0
- data/lib/image/routes/put_image.rb +205 -0
- data/lib/image/server.rb +307 -0
- data/lib/image/store/cumulus.rb +126 -0
- data/lib/image/store/file_system.rb +119 -0
- data/lib/image/store/hdfs.rb +149 -0
- data/lib/image/store/http.rb +78 -0
- data/lib/image/store/lunacloud.rb +126 -0
- data/lib/image/store/s3.rb +121 -0
- data/lib/image/store/store.rb +39 -0
- data/lib/image/store/walrus.rb +130 -0
- data/lib/image/version.rb +5 -0
- data/lib/visor-image.rb +30 -0
- data/spec/lib/client_spec.rb +0 -0
- data/spec/lib/meta_spec.rb +230 -0
- data/spec/lib/routes/delete_image_spec.rb +98 -0
- data/spec/lib/routes/get_image_spec.rb +78 -0
- data/spec/lib/routes/get_images_detail_spec.rb +104 -0
- data/spec/lib/routes/get_images_spec.rb +104 -0
- data/spec/lib/routes/head_image_spec.rb +51 -0
- data/spec/lib/routes/post_image_spec.rb +112 -0
- data/spec/lib/routes/put_image_spec.rb +109 -0
- data/spec/lib/server_spec.rb +62 -0
- data/spec/lib/store/cumulus_spec.rb +0 -0
- data/spec/lib/store/file_system_spec.rb +32 -0
- data/spec/lib/store/http_spec.rb +56 -0
- data/spec/lib/store/s3_spec.rb +37 -0
- data/spec/lib/store/store_spec.rb +36 -0
- data/spec/lib/store/walrus_spec.rb +0 -0
- metadata +217 -0
data/bin/visor
ADDED
@@ -0,0 +1,423 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'uri'
|
5
|
+
require 'progressbar'
|
6
|
+
require 'visor-common'
|
7
|
+
|
8
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
9
|
+
require 'image/version'
|
10
|
+
require 'image/client'
|
11
|
+
|
12
|
+
# VISoR management command line interface script.
|
13
|
+
#
|
14
|
+
# Commands:
|
15
|
+
#
|
16
|
+
# brief Show brief metadata of all public images
|
17
|
+
# detail Show detailed metadata of all public images
|
18
|
+
# head Show image detailed metadata
|
19
|
+
# get Retrieve an image metadata and file
|
20
|
+
# add Add a new image metadata and optionally upload its file
|
21
|
+
# update Update an image metadata and/or upload its file
|
22
|
+
# delete Delete an image metadata and its file
|
23
|
+
# help <cmd> Show help message for one of the above commands
|
24
|
+
#
|
25
|
+
# Run <visor -h> to get more usage help.
|
26
|
+
#
|
27
|
+
class VisorCLI
|
28
|
+
include Visor::Common::Exception
|
29
|
+
include Visor::Common::Config
|
30
|
+
|
31
|
+
# VISoR Image CLI version
|
32
|
+
VERSION = '0.0.1'
|
33
|
+
|
34
|
+
attr_reader :argv, :options, :parser, :command
|
35
|
+
|
36
|
+
# Initialize a new CLI
|
37
|
+
def initialize(argv=ARGV)
|
38
|
+
@argv = argv
|
39
|
+
@options = load_conf_file
|
40
|
+
@parser = parser
|
41
|
+
@command = parse!
|
42
|
+
end
|
43
|
+
|
44
|
+
# OptionParser parser
|
45
|
+
def parser
|
46
|
+
OptionParser.new do |opts|
|
47
|
+
opts.banner = "Usage: visor <command> [options]"
|
48
|
+
|
49
|
+
opts.separator ""
|
50
|
+
opts.separator "Commands:"
|
51
|
+
opts.separator " brief Show brief metadata of all public images"
|
52
|
+
opts.separator " detail Show detailed metadata of all public images"
|
53
|
+
opts.separator " head Show image detailed metadata"
|
54
|
+
opts.separator " get Retrieve an image metadata and file"
|
55
|
+
opts.separator " add Add a new image metadata and optionally upload its file"
|
56
|
+
opts.separator " update Update an image metadata and/or upload its file"
|
57
|
+
opts.separator " delete Delete an image metadata and its file"
|
58
|
+
opts.separator " clean Delete all images metadata and files"
|
59
|
+
opts.separator " help <cmd> Show help message for one of the above commands"
|
60
|
+
|
61
|
+
|
62
|
+
opts.separator ""
|
63
|
+
opts.separator "Options:"
|
64
|
+
opts.on("-a", "--address HOST", "Address of VISoR Image host (default: #{options[:host]})") { |addr| options[:host] = addr }
|
65
|
+
opts.on("-p", "--port PORT", "Port were VISoR Image host listens (default: #{options[:port]})") { |port| options[:port] = port }
|
66
|
+
opts.on("-q", "--query QUERY", "HTTP query like string to filter results") do |query|
|
67
|
+
begin
|
68
|
+
options[:query] = URI.decode_www_form(query)
|
69
|
+
rescue
|
70
|
+
abort "The provided query string is not valid."
|
71
|
+
end
|
72
|
+
end
|
73
|
+
opts.on("-s", "--sort ATTRIBUTE", "Attribute to sort results (default: _id)") do |attr|
|
74
|
+
options[:query] = [] unless options[:query]
|
75
|
+
options[:query] << URI.decode_www_form("sort=#{attr}").flatten
|
76
|
+
end
|
77
|
+
opts.on("-d", "--dir DIRECTION", "Direction to sort results (asc/desc) (default: asc)") do |dir|
|
78
|
+
options[:query] = [] unless options[:query]
|
79
|
+
options[:query] << URI.decode_www_form("dir=#{dir}").flatten
|
80
|
+
end
|
81
|
+
opts.on("-f", "--file IMAGE", "Image file path to upload") { |path| options[:file] = path }
|
82
|
+
opts.on("-S", "--save DIRECTORY", "Directory to save downloaded image (default: './')") { |path| options[:save] = path }
|
83
|
+
|
84
|
+
opts.separator ""
|
85
|
+
opts.separator "Common options:"
|
86
|
+
opts.on_tail("-D", "--dry-run", "Don't persist results, just print what would it do") { options[:dry] = true }
|
87
|
+
opts.on_tail('-v', '--verbose', "Enable verbose") { options[:verbose] = true }
|
88
|
+
opts.on_tail("-h", "--help", "Show this help message") { puts opts; exit 0 }
|
89
|
+
opts.on_tail('-V', '--version', "Show version") { puts "VISoR Image CLI v#{VERSION}"; exit 0 }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Parse argv arguments
|
94
|
+
def parse!
|
95
|
+
parser.parse! ARGV
|
96
|
+
ARGV.shift
|
97
|
+
end
|
98
|
+
|
99
|
+
# Parse the current shell arguments and run the command
|
100
|
+
def run!
|
101
|
+
abort parser.to_s if command.nil?
|
102
|
+
start = Time.now
|
103
|
+
case command
|
104
|
+
when 'brief' then
|
105
|
+
brief
|
106
|
+
when 'detail' then
|
107
|
+
detail
|
108
|
+
when 'head' then
|
109
|
+
head
|
110
|
+
when 'get' then
|
111
|
+
get
|
112
|
+
when 'add' then
|
113
|
+
add
|
114
|
+
when 'update' then
|
115
|
+
update
|
116
|
+
when 'delete' then
|
117
|
+
delete
|
118
|
+
when 'help' then
|
119
|
+
help
|
120
|
+
else
|
121
|
+
abort "Unknown command '#{command}'"
|
122
|
+
end
|
123
|
+
finish = Time.now
|
124
|
+
printf("Done in %-0.4f seconds", finish - start) if verbose?
|
125
|
+
exit 0
|
126
|
+
end
|
127
|
+
|
128
|
+
# Show brief metadata of all public images
|
129
|
+
def brief
|
130
|
+
str = "%-37s %-21s %-13s %-12s %-8s %-10s %-10s\n"
|
131
|
+
images = client.get_images(options[:query])
|
132
|
+
|
133
|
+
puts "Found #{images.size} public images records..."
|
134
|
+
printf(str, 'ID', 'NAME', 'ARCHITECTURE', 'TYPE', 'FORMAT', 'STORE', 'SIZE')
|
135
|
+
puts "#{'-'*36+" "+'-'*20+" "+'-'*12+" "+'-'*11+" "+'-'*6+" "+'-'*10+" "+'-'*10}"
|
136
|
+
|
137
|
+
images.each do |image|
|
138
|
+
printf(str, image[:_id], image[:name], image[:architecture], image[:type] || '-', image[:format] || '-', image[:store] || '-', image[:size] || '-')
|
139
|
+
end
|
140
|
+
rescue NotFound => e
|
141
|
+
puts e.message
|
142
|
+
rescue => e
|
143
|
+
abort "Failure while executing 'brief':\n#{e.message}"
|
144
|
+
end
|
145
|
+
|
146
|
+
# Show detailed metadata of all public images
|
147
|
+
def detail
|
148
|
+
images = client.get_images_detail(options[:query])
|
149
|
+
puts "Found #{images.size} public images records..."
|
150
|
+
|
151
|
+
images.each do |image|
|
152
|
+
puts('-'*80)
|
153
|
+
print_meta(image, false)
|
154
|
+
end
|
155
|
+
rescue NotFound => e
|
156
|
+
puts e.message
|
157
|
+
rescue => e
|
158
|
+
abort "Failure while executing 'detail':\n#{e.message}"
|
159
|
+
end
|
160
|
+
|
161
|
+
# Show image detailed metadata
|
162
|
+
def head
|
163
|
+
id = argv.shift
|
164
|
+
abort "No image ID provided as first argument, please provide it." unless id
|
165
|
+
image = client.head_image(id)
|
166
|
+
print_meta(image)
|
167
|
+
rescue NotFound => e
|
168
|
+
puts e.message
|
169
|
+
rescue => e
|
170
|
+
abort "Failure while executing 'head':\n#{e.message}"
|
171
|
+
end
|
172
|
+
|
173
|
+
# Retrieve an image metadata and file
|
174
|
+
def get
|
175
|
+
id = argv.shift
|
176
|
+
abort "No image ID provided as first argument, please provide it." unless id
|
177
|
+
|
178
|
+
image = client.head_image(id)
|
179
|
+
print_meta(image)
|
180
|
+
path = File.expand_path(options[:save] || './')
|
181
|
+
fp = File.expand_path(File.join(path, "#{id}.#{image[:format] || 'none'}"))
|
182
|
+
|
183
|
+
raise "Cannot locate directory '#{path}'." unless Dir.exists? path
|
184
|
+
#raise "File #{fp} already exists." if File.exists? fp #TODO: restore this
|
185
|
+
|
186
|
+
if File.exists? fp
|
187
|
+
require "securerandom"
|
188
|
+
fp = "#{fp}_#{SecureRandom.random_number.to_s[11..-1]}"
|
189
|
+
end
|
190
|
+
|
191
|
+
file = File.open(fp, 'wb')
|
192
|
+
pbar = ProgressBar.new("Progress", image[:size])
|
193
|
+
pbar.bar_mark = '='
|
194
|
+
|
195
|
+
puts "Downloading image #{id}..."
|
196
|
+
client.get_image(id) do |chunk|
|
197
|
+
pbar.inc(chunk.size)
|
198
|
+
file.write(chunk)
|
199
|
+
end
|
200
|
+
pbar.finish
|
201
|
+
file.close
|
202
|
+
rescue NotFound => e
|
203
|
+
puts e.message
|
204
|
+
rescue => e
|
205
|
+
abort "Failure while executing 'get':\n#{e.message}"
|
206
|
+
end
|
207
|
+
|
208
|
+
# Add a new image metadata and optionally upload its file
|
209
|
+
def add
|
210
|
+
file = options[:file]
|
211
|
+
if file
|
212
|
+
fp = File.expand_path(file)
|
213
|
+
abort "Cannot locate image file at #{fp}." unless File.exists? fp
|
214
|
+
puts "Adding new metadata and uploading file..."
|
215
|
+
end
|
216
|
+
|
217
|
+
begin
|
218
|
+
meta = parse_meta_from_args
|
219
|
+
image = client.post_image(meta, file)
|
220
|
+
puts "Successfully added new metadata and image with ID #{image[:_id]}." if file
|
221
|
+
puts "Successfully added new metadata with ID #{image[:_id]}." unless file
|
222
|
+
print_meta(image)
|
223
|
+
rescue NotFound => e
|
224
|
+
puts e.message
|
225
|
+
rescue => e
|
226
|
+
abort "Failure while executing 'add':\n#{e.message}"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Update an image metadata and/or upload its file
|
231
|
+
def update
|
232
|
+
id = argv.shift
|
233
|
+
file = options[:file]
|
234
|
+
abort "No image ID provided as first argument, please provide it." unless id
|
235
|
+
if file
|
236
|
+
fp = File.expand_path(file)
|
237
|
+
abort "Cannot locate image file at #{fp}." unless File.exists?(fp)
|
238
|
+
puts "Updating metadata and uploading file..."
|
239
|
+
end
|
240
|
+
|
241
|
+
begin
|
242
|
+
meta = parse_meta_from_args
|
243
|
+
image = client.put_image(id, meta, file)
|
244
|
+
puts "Successfully updated and uploaded image #{id}." if file
|
245
|
+
puts "Successfully updated image #{id}." unless file
|
246
|
+
print_meta(image) if verbose?
|
247
|
+
rescue NotFound => e
|
248
|
+
puts e.message
|
249
|
+
rescue => e
|
250
|
+
abort "Failure while executing 'update':\n#{e.message}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Delete an image metadata and its file
|
255
|
+
def delete
|
256
|
+
begin
|
257
|
+
if query = options[:query]
|
258
|
+
result = client.delete_by_query(query)
|
259
|
+
result.each { |image| puts "Successfully deleted image #{image[:_id]}." }
|
260
|
+
else
|
261
|
+
argv.each do |id|
|
262
|
+
abort "No image ID provided as first argument, please provide it." unless id
|
263
|
+
image = client.delete_image(id)
|
264
|
+
puts "Successfully deleted image #{id}."
|
265
|
+
print_meta(image) if verbose?
|
266
|
+
end
|
267
|
+
end
|
268
|
+
rescue NotFound => e
|
269
|
+
puts e.message
|
270
|
+
rescue => e
|
271
|
+
abort "Failure while executing 'delete':\n#{e.message}"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# Show help message for one of the above commands
|
276
|
+
def help
|
277
|
+
cmd = argv[0]
|
278
|
+
abort "Please provide a command name as argument (example: visor help brief)." unless cmd
|
279
|
+
|
280
|
+
case cmd.to_sym
|
281
|
+
when :brief
|
282
|
+
puts %q[Usage: visor brief [options]
|
283
|
+
|
284
|
+
Returns brief metadata of all public images.
|
285
|
+
|
286
|
+
You can filter results based on a query using the --query (-q) option.
|
287
|
+
It's possible to sort and order results with the --sort (-s) and --dir (-d) options.
|
288
|
+
|
289
|
+
Examples:
|
290
|
+
$ visor brief --query 'architecture=i386'
|
291
|
+
$ visor brief --query 'architecture=i386&format=iso'
|
292
|
+
$ visor detail --sort name --dir desc]
|
293
|
+
when :detail
|
294
|
+
%q[Usage: visor detail [options]
|
295
|
+
|
296
|
+
Returns detailed metadata of all public images.
|
297
|
+
|
298
|
+
You can filter results based on a query using the --query (-q) option.
|
299
|
+
It's possible to sort and order results with the --sort (-s) and --dir (-d) options.
|
300
|
+
|
301
|
+
Examples:
|
302
|
+
$ visor detail --query 'architecture=i386'
|
303
|
+
$ visor detail --query 'architecture=i386&format=iso'
|
304
|
+
$ visor detail --sort name --dir desc]
|
305
|
+
when :head
|
306
|
+
puts "Usage: visor head <ID> [options]\n\nReturns detailed metadata of the image with the given ID."
|
307
|
+
when :get
|
308
|
+
puts %q[Usage: visor get <ID> [options]
|
309
|
+
|
310
|
+
Returns detailed metadata and image file of the image with the given ID.
|
311
|
+
You can provide the --save (-S) option defining the path where the image should be saved.
|
312
|
+
|
313
|
+
Examples:
|
314
|
+
$ visor get 8074d23e-a9c0-454d-b935-cda5f6eb1bc8 --save '~/VMs/']
|
315
|
+
when :add
|
316
|
+
puts %q[Usage: visor add <ATTRIBUTES> [options]
|
317
|
+
|
318
|
+
Add new metadata and optionally upload the image file.
|
319
|
+
|
320
|
+
The following attributes can be specified as key/value pairs:
|
321
|
+
|
322
|
+
name: The image name
|
323
|
+
architecture: The Image operating system architecture (available: i386 x86_64)
|
324
|
+
access: If the image is public or private (available: public private)
|
325
|
+
format: The format of the image's disk (available: none iso vhd vdi vmdk ami aki ari)
|
326
|
+
type: The type of the image (available: none kernel ramdisk amazon eucalyptus openstack opennebula nimbus)
|
327
|
+
store: The storage system to save image in (available: s3 http file)
|
328
|
+
location: The location URI of the already somewhere stored image
|
329
|
+
|
330
|
+
Any other custom image property can be passed too as additional key/value pairs.
|
331
|
+
|
332
|
+
Provide the --file option with the path to the image to be uploaded and the 'store' attribute,
|
333
|
+
defining the store where the image should be uploaded to.
|
334
|
+
|
335
|
+
Examples:
|
336
|
+
$ visor add name='Ubuntu 11.10' architecture='x86_64' location='http://www.domain.com/path-to-image'
|
337
|
+
$ visor add name='Ubuntu 11.10' architecture='x86_64' store='s3' --file '~/VMs/ubuntu-11.10-x86_64.iso']
|
338
|
+
when :update
|
339
|
+
puts %q[Usage: visor update <ID> [options]
|
340
|
+
|
341
|
+
Updates metadata and/or uploads image file of the image with the given ID.
|
342
|
+
|
343
|
+
The following attributes can be specified as key/value pairs:
|
344
|
+
|
345
|
+
name: The image name
|
346
|
+
architecture: The Image operating system architecture (available: i386 x86_64)
|
347
|
+
access: If the image is public or private (available: public private)
|
348
|
+
format: The format of the image's disk (available: none iso vhd vdi vmdk ami aki ari)
|
349
|
+
type: The type of the image (available: none kernel ramdisk amazon eucalyptus openstack opennebula nimbus)
|
350
|
+
store: The storage system to save image in (available: s3 http file)
|
351
|
+
location: The location URI of the already somewhere stored image
|
352
|
+
|
353
|
+
Any other custom image property can be passed too as additional key/value pairs.
|
354
|
+
|
355
|
+
It is possible to upload and assign an image file to an already registered metadata:
|
356
|
+
|
357
|
+
Provide the --file option with the path to the image to be uploaded and the 'store' attribute,
|
358
|
+
defining the store where the image should be uploaded to.
|
359
|
+
|
360
|
+
Examples:
|
361
|
+
$ visor update 8074d23e... format='iso'
|
362
|
+
$ visor update 8074d23e... format='iso' location='http://www.domain.com/path-to-image'
|
363
|
+
$ visor update 8074d23e... store='s3' --file '~/VMs/ubuntu-11.10-x86_64.iso']
|
364
|
+
when :delete
|
365
|
+
puts "Usage: visor delete <ID> [options]\n\nDeletes metadata and image file of the image with the given ID."
|
366
|
+
else
|
367
|
+
abort "Unknown command '#{cmd}'"
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
private
|
372
|
+
|
373
|
+
# Pretty print image metadata
|
374
|
+
def print_meta(meta, breaklines=true)
|
375
|
+
puts '' if breaklines
|
376
|
+
order_attributes(meta).each { |k, v| puts "#{k.upcase.to_s.rjust(12)}: #{v}" }
|
377
|
+
puts '' if breaklines
|
378
|
+
end
|
379
|
+
|
380
|
+
# Load configuration file options
|
381
|
+
def load_conf_file
|
382
|
+
config = Visor::Common::Config.load_config(:visor_image)
|
383
|
+
{host: config[:bind_host], port: config[:bind_port]}
|
384
|
+
rescue => e
|
385
|
+
raise "There was an error loading the configuration file: #{e.message}"
|
386
|
+
end
|
387
|
+
|
388
|
+
# Get a new VISoR Image Client instance
|
389
|
+
def client
|
390
|
+
Visor::Image::Client.new(options)
|
391
|
+
end
|
392
|
+
|
393
|
+
# Find if verbose mode is active
|
394
|
+
def verbose?
|
395
|
+
options[:verbose]
|
396
|
+
end
|
397
|
+
|
398
|
+
# Properly order metadata hashes
|
399
|
+
def order_attributes(hash)
|
400
|
+
order = {_id: '-', uri: '-', name: '-', architecture: '-', access: '-', status: '-', type: '-', format: '-',
|
401
|
+
size: '-', store: '-', location: '-', kernel: '-', ramdisk: '-', created_at: '-', updated_at: '-', checksum: '-'}
|
402
|
+
order.merge(hash)
|
403
|
+
end
|
404
|
+
|
405
|
+
# Parse key/value pair arguments to a valid metadata hash
|
406
|
+
def parse_meta_from_args
|
407
|
+
meta = {}
|
408
|
+
raise "You should provide at least one key=value pair." if argv.empty?
|
409
|
+
argv.each do |arg|
|
410
|
+
k, v = arg.split('=')
|
411
|
+
raise "Arguments should be in the form of key=value pairs." unless k && v
|
412
|
+
meta[k.downcase.sub('-', '_')] = v
|
413
|
+
end
|
414
|
+
meta
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
# Execute if file is called
|
420
|
+
#if __FILE__ == $0
|
421
|
+
VisorCLI.new.run!
|
422
|
+
#end
|
423
|
+
|
data/bin/visor-image
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# VISoR Image Server command line interface script.
|
4
|
+
# Run <visor-image -h> to get more usage help.
|
5
|
+
|
6
|
+
require File.expand_path('../../lib/visor-image', __FILE__)
|
7
|
+
|
8
|
+
ENV['GOLIATH_CONF'] = File.expand_path('../../config/server.rb', __FILE__)
|
9
|
+
STDERR.puts ENV['GOLIATH_CONF']
|
10
|
+
Visor::Image::CLI.new(ARGV).run!
|
data/config/server.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
conf = Visor::Common::Config.load_config
|
2
|
+
|
3
|
+
meta_host = conf[:visor_meta][:bind_host]
|
4
|
+
meta_port = conf[:visor_meta][:bind_port]
|
5
|
+
|
6
|
+
auth_host = conf[:visor_auth][:bind_host]
|
7
|
+
auth_port = conf[:visor_auth][:bind_port]
|
8
|
+
|
9
|
+
log_level = conf[:visor_image][:log_level]
|
10
|
+
|
11
|
+
logger.level = (log_level == 'INFO' ? 1 : 0)
|
12
|
+
config['vms'] = Visor::Image::Meta.new(host: meta_host, port: meta_port)
|
13
|
+
config['vas'] = Visor::Image::Auth.new(host: auth_host, port: auth_port)
|
14
|
+
config['configs'] = conf[:visor_store]
|
data/lib/image/auth.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Visor
|
7
|
+
module Image
|
8
|
+
|
9
|
+
# The Client API for the VISoR Auth.
|
10
|
+
#
|
11
|
+
# After Instantiate a Client object its possible to directly interact with the auth server and its
|
12
|
+
# database backend.
|
13
|
+
#
|
14
|
+
class Auth
|
15
|
+
|
16
|
+
include Visor::Common::Exception
|
17
|
+
|
18
|
+
configs = Common::Config.load_config :visor_auth
|
19
|
+
|
20
|
+
DEFAULT_HOST = configs[:bind_host] || '0.0.0.0'
|
21
|
+
DEFAULT_PORT = configs[:bind_port] || 4567
|
22
|
+
|
23
|
+
attr_reader :host, :port, :ssl
|
24
|
+
|
25
|
+
def initialize(opts = {})
|
26
|
+
@host = opts[:host] || DEFAULT_HOST
|
27
|
+
@port = opts[:port] || DEFAULT_PORT
|
28
|
+
@ssl = opts[:ssl] || false
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_users(query = {})
|
32
|
+
str = build_query(query)
|
33
|
+
request = Net::HTTP::Get.new("/users#{str}")
|
34
|
+
do_request(request)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_user(access_key)
|
38
|
+
request = Net::HTTP::Get.new("/users/#{access_key}")
|
39
|
+
do_request(request)
|
40
|
+
end
|
41
|
+
|
42
|
+
def post_user(info)
|
43
|
+
request = Net::HTTP::Post.new('/users')
|
44
|
+
request.body = prepare_body(info)
|
45
|
+
do_request(request)
|
46
|
+
end
|
47
|
+
|
48
|
+
def put_user(access_key, info)
|
49
|
+
request = Net::HTTP::Put.new("/users/#{access_key}")
|
50
|
+
request.body = prepare_body(info)
|
51
|
+
do_request(request)
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete_user(access_key)
|
55
|
+
request = Net::HTTP::Delete.new("/users/#{access_key}")
|
56
|
+
do_request(request)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Parses a response body with the JSON parser and extracts and returns a single
|
62
|
+
# key value from it if defined, otherwise returns all the body.
|
63
|
+
#
|
64
|
+
# @param key (nil) [Symbol] The hash key to extract the wanted value.
|
65
|
+
# @param response [Net::HTTPResponse] The response which contains the body to parse.
|
66
|
+
#
|
67
|
+
# @return [String, Hash] If key is provided and exists on the response body, them return
|
68
|
+
# its value, otherwise return all the body hash.
|
69
|
+
#
|
70
|
+
def parse(key=nil, response)
|
71
|
+
parsed = JSON.parse(response.body, symbolize_names: true)
|
72
|
+
key ? parsed[key] : parsed
|
73
|
+
end
|
74
|
+
|
75
|
+
# Generate a valid URI query string from key/value pairs of the given hash.
|
76
|
+
#
|
77
|
+
# @param opts [Hash] The hash with the key/value pairs to generate query from.
|
78
|
+
#
|
79
|
+
# @return [String] The generated query in the form of "?k=v&k1=v1".
|
80
|
+
#
|
81
|
+
def build_query(opts)
|
82
|
+
opts.empty? ? '' : '?' + URI.encode_www_form(opts)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Fill common header keys before each request. This sets the 'User-Agent' and 'Accept'
|
86
|
+
# headers for every request and additionally sets the 'content-type' header
|
87
|
+
# for POST and PUT requests.
|
88
|
+
#
|
89
|
+
# @param request [Net::HTTPResponse] The request which will be modified in its headers.
|
90
|
+
#
|
91
|
+
def prepare_headers(request)
|
92
|
+
request['User-Agent'] = 'VISoR image server'
|
93
|
+
request['Accept'] = 'application/json'
|
94
|
+
request['content-type'] = 'application/json' if ['POST', 'PUT'].include?(request.method)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Generate a valid JSON request body for POST and PUT requests.
|
98
|
+
# It generates a JSON object encapsulated inside a :image key and then returns it.
|
99
|
+
#
|
100
|
+
# @param hash [Hash] The hash with the key/value pairs to generate a JSON object from.
|
101
|
+
#
|
102
|
+
# @return [Hash] If an :image key is already present in the hash, it just returns the plain
|
103
|
+
# JSON object, otherwise, encapsulate the hash inside a :image key and returns it.
|
104
|
+
#
|
105
|
+
def prepare_body(hash)
|
106
|
+
hash.has_key?(:user) ? hash.to_json : {user: hash}.to_json
|
107
|
+
end
|
108
|
+
|
109
|
+
# Process requests by preparing its headers, launch them and assert or raise their response.
|
110
|
+
#
|
111
|
+
# @param request [Net::HTTPResponse] The request which will be launched.
|
112
|
+
#
|
113
|
+
# @return [String, Hash] If an error is raised, then it parses and returns its message,
|
114
|
+
# otherwise it properly parse and return the response body.
|
115
|
+
#
|
116
|
+
# @raise [NotFound] If required image was not found (on a GET, PUT or DELETE request).
|
117
|
+
# @raise [Invalid] If image meta validation fails (on a POST or PUT request).
|
118
|
+
#
|
119
|
+
def do_request(request)
|
120
|
+
prepare_headers(request)
|
121
|
+
response = http_or_https.request(request)
|
122
|
+
case response
|
123
|
+
when Net::HTTPNotFound then
|
124
|
+
raise NotFound, parse(:message, response)
|
125
|
+
when Net::HTTPBadRequest then
|
126
|
+
raise Invalid, parse(:message, response)
|
127
|
+
else
|
128
|
+
parse(:user, response) or parse(:users, response)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Generate a new HTTP or HTTPS connection based on initialization parameters.
|
133
|
+
#
|
134
|
+
# @return [Net::HTTP] A HTTP or HTTPS (not done yet) connection ready to use.
|
135
|
+
#
|
136
|
+
def http_or_https
|
137
|
+
if @ssl
|
138
|
+
#TODO: ssl connection
|
139
|
+
#https://github.com/augustl/net-http-cheat-sheet/blob/master/ssl_and_https.rb
|
140
|
+
else
|
141
|
+
Net::HTTP.new(@host, @port)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|