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.
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