visor-image 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
+