visor-image 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/bin/visor +423 -0
  2. data/bin/visor-image +10 -0
  3. data/config/server.rb +14 -0
  4. data/lib/image/auth.rb +147 -0
  5. data/lib/image/cli.rb +397 -0
  6. data/lib/image/client.rb +490 -0
  7. data/lib/image/meta.rb +219 -0
  8. data/lib/image/routes/delete_all_images.rb +40 -0
  9. data/lib/image/routes/delete_image.rb +62 -0
  10. data/lib/image/routes/get_image.rb +78 -0
  11. data/lib/image/routes/get_images.rb +54 -0
  12. data/lib/image/routes/get_images_detail.rb +54 -0
  13. data/lib/image/routes/head_image.rb +51 -0
  14. data/lib/image/routes/post_image.rb +189 -0
  15. data/lib/image/routes/put_image.rb +205 -0
  16. data/lib/image/server.rb +307 -0
  17. data/lib/image/store/cumulus.rb +126 -0
  18. data/lib/image/store/file_system.rb +119 -0
  19. data/lib/image/store/hdfs.rb +149 -0
  20. data/lib/image/store/http.rb +78 -0
  21. data/lib/image/store/lunacloud.rb +126 -0
  22. data/lib/image/store/s3.rb +121 -0
  23. data/lib/image/store/store.rb +39 -0
  24. data/lib/image/store/walrus.rb +130 -0
  25. data/lib/image/version.rb +5 -0
  26. data/lib/visor-image.rb +30 -0
  27. data/spec/lib/client_spec.rb +0 -0
  28. data/spec/lib/meta_spec.rb +230 -0
  29. data/spec/lib/routes/delete_image_spec.rb +98 -0
  30. data/spec/lib/routes/get_image_spec.rb +78 -0
  31. data/spec/lib/routes/get_images_detail_spec.rb +104 -0
  32. data/spec/lib/routes/get_images_spec.rb +104 -0
  33. data/spec/lib/routes/head_image_spec.rb +51 -0
  34. data/spec/lib/routes/post_image_spec.rb +112 -0
  35. data/spec/lib/routes/put_image_spec.rb +109 -0
  36. data/spec/lib/server_spec.rb +62 -0
  37. data/spec/lib/store/cumulus_spec.rb +0 -0
  38. data/spec/lib/store/file_system_spec.rb +32 -0
  39. data/spec/lib/store/http_spec.rb +56 -0
  40. data/spec/lib/store/s3_spec.rb +37 -0
  41. data/spec/lib/store/store_spec.rb +36 -0
  42. data/spec/lib/store/walrus_spec.rb +0 -0
  43. 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