visor-image 0.0.3 → 0.0.4

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 CHANGED
@@ -48,21 +48,20 @@ class VisorCLI
48
48
 
49
49
  opts.separator ""
50
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"
51
+ opts.separator " brief Show brief metadata of all public and user's private images"
52
+ opts.separator " detail Show detailed metadata of all public and user's private images"
53
+ opts.separator " head Show an image detailed metadata"
54
54
  opts.separator " get Retrieve an image metadata and file"
55
55
  opts.separator " add Add a new image metadata and optionally upload its file"
56
56
  opts.separator " update Update an image metadata and/or upload its file"
57
57
  opts.separator " delete Delete an image metadata and its file"
58
- opts.separator " clean Delete all images metadata and files"
59
58
  opts.separator " help <cmd> Show help message for one of the above commands"
60
59
 
61
60
 
62
61
  opts.separator ""
63
62
  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 }
63
+ opts.on("-a", "--address HOST", "Address of the VISOR Image System server (default: #{options[:host]})") { |addr| options[:host] = addr }
64
+ opts.on("-p", "--port PORT", "Port where the VISOR Image System server listens (default: #{options[:port]})") { |port| options[:port] = port }
66
65
  opts.on("-q", "--query QUERY", "HTTP query like string to filter results") do |query|
67
66
  begin
68
67
  options[:query] = URI.decode_www_form(query)
@@ -83,10 +82,9 @@ class VisorCLI
83
82
 
84
83
  opts.separator ""
85
84
  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
85
  opts.on_tail('-v', '--verbose', "Enable verbose") { options[:verbose] = true }
88
86
  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 }
87
+ opts.on_tail('-V', '--version', "Show version") { puts "visor CLI #{VERSION}"; exit 0 }
90
88
  end
91
89
  end
92
90
 
@@ -100,62 +98,62 @@ class VisorCLI
100
98
  def run!
101
99
  abort parser.to_s if command.nil?
102
100
  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}'"
101
+ begin
102
+ case command
103
+ when 'brief' then
104
+ brief
105
+ when 'detail' then
106
+ detail
107
+ when 'head' then
108
+ head
109
+ when 'get' then
110
+ get
111
+ when 'add' then
112
+ add
113
+ when 'update' then
114
+ update
115
+ when 'delete' then
116
+ delete
117
+ when 'help' then
118
+ help
119
+ else
120
+ abort "Unknown command '#{command}'"
121
+ end
122
+ rescue NotFound => e
123
+ puts e.message
124
+ rescue Errno::ECONNREFUSED
125
+ abort "Failure while executing '#{command}': VISOR Image System server not found. Is it running?"
126
+ #rescue => e
127
+ # abort "Failure while executing '#{command}': #{e.message}"
122
128
  end
123
129
  finish = Time.now
124
- printf("Done in %-0.4f seconds", finish - start) if verbose?
130
+ printf("Done in %-0.4f seconds\n", finish - start) if verbose?
125
131
  exit 0
126
132
  end
127
133
 
128
134
  # Show brief metadata of all public images
129
135
  def brief
130
- str = "%-37s %-21s %-13s %-12s %-8s %-10s %-10s\n"
136
+ str = "%-37s %-22s %-13s %-10s %-8s %-10s %-10s\n"
131
137
  images = client.get_images(options[:query])
132
138
 
133
- puts "Found #{images.size} public images records..."
139
+ puts "Found #{images.size} images records..."
134
140
  printf(str, 'ID', 'NAME', 'ARCHITECTURE', 'TYPE', 'FORMAT', 'STORE', 'SIZE')
135
- puts "#{'-'*36+" "+'-'*20+" "+'-'*12+" "+'-'*11+" "+'-'*6+" "+'-'*10+" "+'-'*10}"
141
+ puts "#{'-'*36+" "+'-'*21+" "+'-'*12+" "+'-'*9+" "+'-'*6+" "+'-'*10+" "+'-'*10}"
136
142
 
137
143
  images.each do |image|
138
144
  printf(str, image[:_id], image[:name], image[:architecture], image[:type] || '-', image[:format] || '-', image[:store] || '-', image[:size] || '-')
139
145
  end
140
- rescue NotFound => e
141
- puts e.message
142
- rescue => e
143
- abort "Failure while executing 'brief':\n#{e.message}"
144
146
  end
145
147
 
146
148
  # Show detailed metadata of all public images
147
149
  def detail
148
150
  images = client.get_images_detail(options[:query])
149
- puts "Found #{images.size} public images records..."
151
+ puts "Found #{images.size} images records..."
150
152
 
151
153
  images.each do |image|
152
154
  puts('-'*80)
153
155
  print_meta(image, false)
154
156
  end
155
- rescue NotFound => e
156
- puts e.message
157
- rescue => e
158
- abort "Failure while executing 'detail':\n#{e.message}"
159
157
  end
160
158
 
161
159
  # Show image detailed metadata
@@ -164,10 +162,6 @@ class VisorCLI
164
162
  abort "No image ID provided as first argument, please provide it." unless id
165
163
  image = client.head_image(id)
166
164
  print_meta(image)
167
- rescue NotFound => e
168
- puts e.message
169
- rescue => e
170
- abort "Failure while executing 'head':\n#{e.message}"
171
165
  end
172
166
 
173
167
  # Retrieve an image metadata and file
@@ -181,7 +175,7 @@ class VisorCLI
181
175
  fp = File.expand_path(File.join(path, "#{id}.#{image[:format] || 'none'}"))
182
176
 
183
177
  raise "Cannot locate directory '#{path}'." unless Dir.exists? path
184
- #raise "File #{fp} already exists." if File.exists? fp #TODO: restore this
178
+ raise "File #{fp} already exists." if File.exists? fp
185
179
 
186
180
  if File.exists? fp
187
181
  require "securerandom"
@@ -199,10 +193,6 @@ class VisorCLI
199
193
  end
200
194
  pbar.finish
201
195
  file.close
202
- rescue NotFound => e
203
- puts e.message
204
- rescue => e
205
- abort "Failure while executing 'get':\n#{e.message}"
206
196
  end
207
197
 
208
198
  # Add a new image metadata and optionally upload its file
@@ -214,17 +204,11 @@ class VisorCLI
214
204
  puts "Adding new metadata and uploading file..."
215
205
  end
216
206
 
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
207
+ meta = parse_meta_from_args
208
+ image = client.post_image(meta, file)
209
+ puts "Successfully added new metadata and image with ID #{image[:_id]}." if file
210
+ puts "Successfully added new metadata with ID #{image[:_id]}." unless file
211
+ print_meta(image)
228
212
  end
229
213
 
230
214
  # Update an image metadata and/or upload its file
@@ -238,37 +222,25 @@ class VisorCLI
238
222
  puts "Updating metadata and uploading file..."
239
223
  end
240
224
 
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
225
+ meta = parse_meta_from_args
226
+ image = client.put_image(id, meta, file)
227
+ puts "Successfully updated and uploaded image #{id}." if file
228
+ puts "Successfully updated image #{id}." unless file
229
+ print_meta(image) if verbose?
252
230
  end
253
231
 
254
232
  # Delete an image metadata and its file
255
233
  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
234
+ if query = options[:query]
235
+ result = client.delete_by_query(query)
236
+ result.each { |image| puts "Successfully deleted image #{image[:_id]}." }
237
+ else
238
+ argv.each do |id|
239
+ abort "No image ID provided as first argument, please provide it." unless id
240
+ image = client.delete_image(id)
241
+ puts "Successfully deleted image #{id}."
242
+ print_meta(image) if verbose?
267
243
  end
268
- rescue NotFound => e
269
- puts e.message
270
- rescue => e
271
- abort "Failure while executing 'delete':\n#{e.message}"
272
244
  end
273
245
  end
274
246
 
@@ -289,7 +261,7 @@ It's possible to sort and order results with the --sort (-s) and --dir (-d) opti
289
261
  Examples:
290
262
  $ visor brief --query 'architecture=i386'
291
263
  $ visor brief --query 'architecture=i386&format=iso'
292
- $ visor detail --sort name --dir desc]
264
+ $ visor brief --sort name --dir desc]
293
265
  when :detail
294
266
  %q[Usage: visor detail [options]
295
267
 
@@ -12,3 +12,4 @@ logger.level = (log_level == 'INFO' ? 1 : 0)
12
12
  config['vms'] = Visor::Image::Meta.new(host: meta_host, port: meta_port)
13
13
  config['vas'] = Visor::Image::Auth.new(host: auth_host, port: auth_port)
14
14
  config['configs'] = conf[:visor_store]
15
+ config['address'] = "#{conf[:visor_image][:bind_host]}:#{conf[:visor_image][:bind_port]}"
@@ -1,99 +1,103 @@
1
- require 'net/http'
2
- require 'net/https'
1
+ require 'em-synchrony'
2
+ require 'em-synchrony/em-http'
3
3
  require 'uri'
4
4
  require 'json'
5
5
 
6
6
  module Visor
7
7
  module Image
8
8
 
9
- # The Client API for the VISoR Auth.
9
+ # The API for the VISOR Auth System (VAS) server. This class supports all user's manipulation operations.
10
10
  #
11
- # After Instantiate a Client object its possible to directly interact with the auth server and its
11
+ # After Instantiate a VAS API client object its possible to directly interact with the VAS server and its
12
12
  # database backend.
13
13
  #
14
14
  class Auth
15
-
16
15
  include Visor::Common::Exception
17
16
 
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
17
+ attr_reader :host, :port
24
18
 
25
19
  def initialize(opts = {})
26
- @host = opts[:host] || DEFAULT_HOST
27
- @port = opts[:port] || DEFAULT_PORT
28
- @ssl = opts[:ssl] || false
20
+ @host = opts[:host]
21
+ @port = opts[:port]
29
22
  end
30
23
 
24
+ # Get information about all registered users.
25
+ #
26
+ # Options for filtering the returned results can be passed in.
27
+ #
28
+ # @option query [String] :<attribute_name> The user attribute value to filter returned results.
29
+ # @option query [String] :sort ("_id") The image attribute to sort returned results.
30
+ # @option query [String] :dir ("asc") The direction to sort results ("asc"/"desc").
31
+ #
32
+ # @return [Array] All user's accounts.
33
+ #
34
+ # @raise [NotFound] If there are no users registered on VAS.
35
+ #
31
36
  def get_users(query = {})
32
- str = build_query(query)
33
- request = Net::HTTP::Get.new("/users#{str}")
34
- do_request(request)
37
+ http = request.get path: '/users', query: query, head: get_headers
38
+ return_response(http)
35
39
  end
36
40
 
41
+ # Get information about a specific user.
42
+ #
43
+ # @param access_key [String] The user access key (username).
44
+ #
45
+ # @return [Hash] The requested user account information.
46
+ #
47
+ # @raise [NotFound] If user was not found.
48
+ #
37
49
  def get_user(access_key)
38
- request = Net::HTTP::Get.new("/users/#{access_key}")
39
- do_request(request)
50
+ http = request.get path: "/users/#{access_key}", head: get_headers
51
+ return_response(http)
40
52
  end
41
53
 
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.
54
+ # Register a new user account on VAS and return its data.
55
+ #
56
+ # @param info [Hash] The user information.
63
57
  #
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.
58
+ # @return [Hash] The already created user detailed information.
66
59
  #
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.
60
+ # @raise [Invalid] If user data validation fails.
61
+ # @raise [NotFound] If user was not found after registered.
62
+ # @raise [ConflictError] access_key was already taken.
69
63
  #
70
- def parse(key=nil, response)
71
- parsed = JSON.parse(response.body, symbolize_names: true)
72
- key ? parsed[key] : parsed
64
+ def post_user(info)
65
+ body = prepare_body(info)
66
+ http = request.post path: '/users', body: body, head: post_headers
67
+ return_response(http)
73
68
  end
74
69
 
75
- # Generate a valid URI query string from key/value pairs of the given hash.
70
+ # Update an existing user information and return it.
71
+ #
72
+ # @param access_key [String] The wanted user access_key.
73
+ # @param info [Hash] The user information.
76
74
  #
77
- # @param opts [Hash] The hash with the key/value pairs to generate query from.
75
+ # @return [Hash] The already updated user detailed information.
78
76
  #
79
- # @return [String] The generated query in the form of "?k=v&k1=v1".
77
+ # @raise [Invalid] If user data validation fails.
78
+ # @raise [NotFound] If user was not found.
80
79
  #
81
- def build_query(opts)
82
- opts.empty? ? '' : '?' + URI.encode_www_form(opts)
80
+ def put_user(access_key, info)
81
+ body = prepare_body(info)
82
+ http = request.put path: "/users/#{access_key}", body: body, head: put_headers
83
+ return_response(http)
83
84
  end
84
85
 
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.
86
+ # Delete an user and returns its information.
88
87
  #
89
- # @param request [Net::HTTPResponse] The request which will be modified in its headers.
88
+ # @param access_key [String] The wanted user access_key.
90
89
  #
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)
90
+ # @return [Hash] The already deleted user detailed information.
91
+ #
92
+ # @raise [NotFound] If user was not found.
93
+ #
94
+ def delete_user(access_key)
95
+ http = request.delete path: "/users/#{access_key}", access_key: delete_headers
96
+ return_response(http)
95
97
  end
96
98
 
99
+ private
100
+
97
101
  # Generate a valid JSON request body for POST and PUT requests.
98
102
  # It generates a JSON object encapsulated inside a :image key and then returns it.
99
103
  #
@@ -108,7 +112,7 @@ module Visor
108
112
 
109
113
  # Process requests by preparing its headers, launch them and assert or raise their response.
110
114
  #
111
- # @param request [Net::HTTPResponse] The request which will be launched.
115
+ # @param http [EventMachine::HttpRequest] The request which will be launched.
112
116
  #
113
117
  # @return [String, Hash] If an error is raised, then it parses and returns its message,
114
118
  # otherwise it properly parse and return the response body.
@@ -116,32 +120,59 @@ module Visor
116
120
  # @raise [NotFound] If required image was not found (on a GET, PUT or DELETE request).
117
121
  # @raise [Invalid] If image meta validation fails (on a POST or PUT request).
118
122
  #
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)
123
+ def return_response(http)
124
+ body = http.response
125
+ status = http.response_header.status.to_i
126
+
127
+ case status
128
+ when 0 then
129
+ raise InternalError, "VISOR Auth System server not found. Is it running?"
130
+ when 404 then
131
+ raise NotFound, parse(body)
132
+ when 400 then
133
+ raise Invalid, parse(body)
134
+ when 500 then
135
+ raise InternalError, parse(body)
127
136
  else
128
- parse(:user, response) or parse(:users, response)
137
+ parse(body)
129
138
  end
130
139
  end
131
140
 
141
+ def parse(body)
142
+ parsed = JSON.parse(body, symbolize_names: true)
143
+ parsed[:user] || parsed[:users] || parsed[:message]
144
+ end
145
+
132
146
  # Generate a new HTTP or HTTPS connection based on initialization parameters.
133
147
  #
134
- # @return [Net::HTTP] A HTTP or HTTPS (not done yet) connection ready to use.
148
+ # @return [EventMachine::HttpRequest] A HTTP or HTTPS (not done yet) connection ready to use.
135
149
  #
136
- def http_or_https
137
- if @ssl
150
+ def request
151
+ #if @ssl
138
152
  #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
153
+ #else
154
+ EventMachine::HttpRequest.new("http://#{@host}:#{@port}")
155
+ #end
156
+ end
157
+
158
+ # Fill common header keys before each request. This sets the 'User-Agent' and 'Accept'
159
+ # headers for every request and additionally sets the 'content-type' header
160
+ # for POST and PUT requests.
161
+ #
162
+ def get_headers
163
+ {'User-Agent' => 'VISOR Image System',
164
+ 'Accept' => 'application/json'}
143
165
  end
144
166
 
167
+ def post_headers
168
+ {'User-Agent' => 'VISOR Image System',
169
+ 'Accept' => 'application/json',
170
+ 'content-type' => 'application/json'}
171
+ end
172
+
173
+ alias :delete_headers :get_headers
174
+ alias :put_headers :post_headers
145
175
  end
146
176
  end
147
177
  end
178
+