aviator 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9501d71ef567ef9bfe2a511a7122a23f8faca48
4
- data.tar.gz: 9ae86b5df08bbceafb3481b6a4693ab2783996c1
3
+ metadata.gz: f56372f2f4fea06121bebdef1c2bccfef12c1f56
4
+ data.tar.gz: a17fe8facfb17ba357ceb772d9087e89d53a59ca
5
5
  SHA512:
6
- metadata.gz: aed3dc71e256f15c8e314f4d781e369d23b6648fa7406c2e84a34f3326457e5c93fc1c2d2b073a0f71904947081da6191412e346d76ff8a5806184ffed1fd9af
7
- data.tar.gz: 435d8acd62fcbf0fc8ed08f2ee9fabd6baf87f17b8258eea76f7fae30faaee19b3b36f6e74239bab1914b3603a2f10ca3cf474ca22a19e69c70edde26f0b72d1
6
+ metadata.gz: 00aa0e3555582535cfac8d3699259d31140cc88f920f2d162c064d64e2b483ccc8bb484aa29f59f99be99cda355d57a450de29c08714d14e07d23978a9c3a116
7
+ data.tar.gz: 7a0155396260c67ab9c7f1c0d03504f7a1e81f8e36463b347303198375c0486d415acd5e97d265036a0ac7a7be9878c9d304ee2743d98dd40b6733f594611cef
data/Gemfile CHANGED
@@ -18,5 +18,6 @@ group :test do
18
18
  gem 'json', '~> 1.7.0'
19
19
  gem 'minitest', '~> 4.7.0'
20
20
  gem 'minitest-reporters', '~> 0.14.20'
21
+ gem 'mocha', '~> 1.1.0'
21
22
  gem 'vcr', '~> 2.8.0'
22
23
  end
@@ -22,24 +22,24 @@ Gem::Specification.new do |spec|
22
22
  spec.require_paths = ["lib"]
23
23
 
24
24
  # Runtime dependencies
25
- spec.add_dependency 'faraday', '>= 0.8.8'
26
- spec.add_dependency 'thor', '>= 0.18.1'
27
- spec.add_dependency 'terminal-table', '>= 1.4.5'
25
+ spec.add_runtime_dependency 'faraday', '~> 0.8', '>= 0.8.8'
26
+ spec.add_runtime_dependency 'thor', '~> 0.18', '>= 0.18.1'
27
+ spec.add_runtime_dependency 'terminal-table', '~> 1.4', '>= 1.4.5'
28
28
 
29
29
  # Development dependencies. See Gemfile for more development deps.
30
- spec.add_development_dependency "bundler", ">= 1.0"
31
- spec.add_development_dependency 'rb-fsevent', '~> 0.9.0'
32
- spec.add_development_dependency 'guard', '~> 1.8.0'
33
- spec.add_development_dependency 'guard-rake', '~> 0.0.0'
34
- spec.add_development_dependency 'guard-minitest', '~> 0.5.0'
30
+ spec.add_development_dependency 'bundler', '~> 1.0'
31
+ spec.add_development_dependency 'rb-fsevent', '~> 0.9', '>= 0.9.0'
32
+ spec.add_development_dependency 'guard', '~> 1.8', '>= 1.8.0'
33
+ spec.add_development_dependency 'guard-rake', '~> 0.0', '>= 0.0.0'
34
+ spec.add_development_dependency 'guard-minitest', '~> 0.5', '>= 0.5.0'
35
35
 
36
36
  if /darwin|mac os/ === RbConfig::CONFIG['host_os']
37
- spec.add_development_dependency 'terminal-notifier-guard', '~> 1.5.3'
37
+ spec.add_development_dependency 'terminal-notifier-guard', '~> 1.5', '>= 1.5.3'
38
38
  else
39
- spec.add_development_dependency 'ruby_gntp', '~> 0.3.0'
39
+ spec.add_development_dependency 'ruby_gntp', '~> 0.3', '>= 0.3.0'
40
40
  end
41
41
 
42
- spec.add_development_dependency 'pry', '~> 0.9.0'
43
- spec.add_development_dependency 'yard', '~> 0.8.0'
44
- spec.add_development_dependency 'redcarpet', '~> 2.3.0'
42
+ spec.add_development_dependency 'pry', '~> 0.9', '>= 0.9.0'
43
+ spec.add_development_dependency 'yard', '~> 0.8', '>= 0.8.0'
44
+ spec.add_development_dependency 'redcarpet', '~> 2.3', '>= 2.3.0'
45
45
  end
@@ -1,5 +1,13 @@
1
1
  module Aviator
2
2
 
3
+ #
4
+ # Manages a provider (e.g. OpenStack) session.
5
+ #
6
+ # Author:: Mark Maglana (mmaglana@gmail.com)
7
+ # Copyright:: Copyright (c) 2014 Mark Maglana
8
+ # License:: Distributed under the MIT license
9
+ # Homepage:: http://aviator.github.io/www/
10
+ #
3
11
  class Session
4
12
 
5
13
  class AuthenticationError < StandardError
@@ -42,7 +50,91 @@ module Aviator
42
50
  end
43
51
  end
44
52
 
45
-
53
+ #
54
+ # Create a new Session instance with options provided in <tt>opts</tt> which can
55
+ # have many forms discussed below.
56
+ #
57
+ # <b>Initialize with a config file</b>
58
+ #
59
+ # Aviator::Session.new(:config_file => 'path/to/aviator.yml', :environment => :production)
60
+ #
61
+ # In the above example, the config file must have the following form:
62
+ #
63
+ # production:
64
+ # provider: openstack
65
+ # auth_service:
66
+ # name: identity
67
+ # host_uri: 'http://my.openstackenv.org:5000'
68
+ # request: create_token
69
+ # validator: list_tenants
70
+ # api_version: v2
71
+ # auth_credentials:
72
+ # username: myusername
73
+ # password: mypassword
74
+ # tenant_name: myproject
75
+ #
76
+ # Once the session has been instantiated, you may authenticate against the
77
+ # provider as follows:
78
+ #
79
+ # session.authenticate
80
+ #
81
+ # Note that the required items under <tt>auth_credentials</tt> in the config
82
+ # file depends on the required parameters of the request class declared under
83
+ # <tt>auth_service</tt>. If writing the <tt>auth_credentials</tt> in the config
84
+ # file is not acceptable, you may omit it and just supply the credentials at
85
+ # runtime. For instance, assume that the <tt>auth_credentials</tt> section in the
86
+ # config file above is missing. You would then authenticate as follows:
87
+ #
88
+ # session.authenticate do |params|
89
+ # params.username = ARGV[0]
90
+ # params.password = ARGV[1]
91
+ # params.tenant_name = ARGV[2]
92
+ # end
93
+ #
94
+ # Please see Session#authenticate for more info.
95
+ #
96
+ # Note that while the example config file above only has one environment (production),
97
+ # you can declare an arbitrary number of environments in your config file. Shifting
98
+ # between environments is as simple as changing the <tt>:environment</tt> to refer to that.
99
+ #
100
+ #
101
+ # <b>Initialize with an in-memory hash</b>
102
+ #
103
+ # You can create an in-memory hash which is similar in structure to the config file except
104
+ # that you don't need to specify an environment name. For example:
105
+ #
106
+ # configuration = {
107
+ # :provider => 'openstack',
108
+ # :auth_service => {
109
+ # :name => 'identity',
110
+ # :host_uri => 'http://devstack:5000/v2.0',
111
+ # :request => 'create_token',
112
+ # :validator => 'list_tenants'
113
+ # }
114
+ # }
115
+ #
116
+ # Supply this to the initializer using the <tt>:config</tt> option. For example:
117
+ #
118
+ # Aviator::Session.new(:config => configuration)
119
+ #
120
+ #
121
+ # <b>Initialize with a session dump</b>
122
+ #
123
+ # You can create a new Session instance using a dump from another instance. For example:
124
+ #
125
+ # session_dump = session1.dump
126
+ # session2 = Aviator::Session.new(:session_dump => session_dump)
127
+ #
128
+ # However, Session.load is cleaner and recommended over this method.
129
+ #
130
+ #
131
+ # <b>Optionally supply a log file</b>
132
+ #
133
+ # In all forms above, you may optionally add a <tt>:log_file</tt> option to make
134
+ # Aviator write all HTTP calls to the given path. For example:
135
+ #
136
+ # Aviator::Session.new(:config_file => 'path/to/aviator.yml', :environment => :production, :log_file => 'path/to/log')
137
+ #
46
138
  def initialize(opts={})
47
139
  if opts.has_key? :session_dump
48
140
  initialize_with_dump(opts[:session_dump])
@@ -57,15 +149,47 @@ module Aviator
57
149
  @log_file = opts[:log_file]
58
150
  end
59
151
 
60
-
152
+ #
153
+ # Authenticates against the auth_service request class declared in the session's
154
+ # configuration during initialization. Please see Session.new for more information
155
+ # on declaring the request class to use for authentication.
156
+ #
157
+ # If the auth_service request class accepts a parameter block, you may also supply that
158
+ # when calling this method and it will be directly passed to the request. For example:
159
+ #
160
+ # session = Aviator::Session.new(:config => config)
161
+ # session.authenticate do |params|
162
+ # params.username = username
163
+ # params.password = password
164
+ # params.tenant_name = project
165
+ # end
166
+ #
167
+ # Expects an HTTP status 200 or 201. Any other status is treated as a failure.
168
+ #
169
+ # Note that you can also treat the block's argument like a hash with the attribute
170
+ # names as the keys. For example, we can rewrite the above as:
171
+ #
172
+ # session = Aviator::Session.new(:config => config)
173
+ # session.authenticate do |params|
174
+ # params[:username] = username
175
+ # params[:password] = password
176
+ # params[:tenant_name] = project
177
+ # end
178
+ #
179
+ # Keys can be symbols or strings.
180
+ #
61
181
  def authenticate(&block)
62
182
  block ||= lambda do |params|
63
- environment[:auth_credentials].each do |key, value|
64
- params[key] = value
183
+ config[:auth_credentials].each do |key, value|
184
+ begin
185
+ params[key] = value
186
+ rescue NameError => e
187
+ raise NameError.new("Unknown param name '#{key}'")
188
+ end
65
189
  end
66
190
  end
67
191
 
68
- response = auth_service.request environment[:auth_service][:request].to_sym, &block
192
+ response = auth_service.request config[:auth_service][:request].to_sym, &block
69
193
 
70
194
  if [200, 201].include? response.status
71
195
  @auth_response = Hashish.new({
@@ -79,20 +203,43 @@ module Aviator
79
203
  self
80
204
  end
81
205
 
82
-
206
+ #
207
+ # Returns true if the session has been authenticated.
208
+ #
83
209
  def authenticated?
84
210
  !auth_response.nil?
85
211
  end
86
212
 
213
+ #
214
+ # Returns its configuration.
215
+ #
216
+ def config
217
+ @config
218
+ end
87
219
 
220
+ #
221
+ # Returns a JSON string of its configuration and auth_data. This string can be streamed
222
+ # or stored and later re-loaded in another Session instance. For example:
223
+ #
224
+ # session = Aviator::Session.new(:config => configuration)
225
+ # str = session.dump
226
+ #
227
+ # # time passes...
228
+ #
229
+ # session = Aviator::Session.load(str)
230
+ #
88
231
  def dump
89
232
  JSON.generate({
90
- :environment => environment,
233
+ :config => config,
91
234
  :auth_response => auth_response
92
235
  })
93
236
  end
94
237
 
95
238
 
239
+ #
240
+ # Same as Session::load but re-uses the Session instance this method is
241
+ # called on instead of creating a new one.
242
+ #
96
243
  def load(session_dump)
97
244
  initialize_with_dump(session_dump)
98
245
  update_services_session_data
@@ -111,6 +258,14 @@ module Aviator
111
258
  end
112
259
 
113
260
 
261
+ #
262
+ # Creates a new Session object from a previous session's dump. See Session#dump for
263
+ # more information.
264
+ #
265
+ # If you want the newly deserialized session to log its output, make sure to indicate it on load
266
+ #
267
+ # Aviator::Session.load(session_dump_str, :log_file => 'path/to/aviator.log')
268
+ #
114
269
  def self.load(session_dump, opts={})
115
270
  opts[:session_dump] = session_dump
116
271
 
@@ -118,13 +273,72 @@ module Aviator
118
273
  end
119
274
 
120
275
 
276
+ #
277
+ # Returns the log file path. May be nil if none was provided during initialization.
278
+ #
279
+ def log_file
280
+ @log_file
281
+ end
282
+
283
+
284
+ #
285
+ # Calls the given request of the given service. An example call might look like:
286
+ #
287
+ # session.request :compute_service, :create_server do |p|
288
+ # p.name = "My Server"
289
+ # p.image_ref = "7cae8c8e-fb01-4a88-bba3-ae0fcb1dbe29"
290
+ # p.flavor_ref = "fa283da1-59a5-4245-8569-b6eadf69f10b"
291
+ # end
292
+ #
293
+ # Note that you can also treat the block's argument like a hash with the attribute
294
+ # names as the keys. For example, we can rewrite the above as:
295
+ #
296
+ # session.request :compute_service, :create_server do |p|
297
+ # p[:name] = "My Server"
298
+ # p[:image_ref] = "7cae8c8e-fb01-4a88-bba3-ae0fcb1dbe29"
299
+ # p[:flavor_ref] = "fa283da1-59a5-4245-8569-b6eadf69f10b"
300
+ # end
301
+ #
302
+ # Keys can be symbols or strings.
303
+ #
304
+ # ---
305
+ #
306
+ # <b>Request Options</b>
307
+ #
308
+ # You can further customize how the request is fulfilled by providing one or more
309
+ # options to the method call. For example, the following ensures that the request
310
+ # will call the :create_server request for the v1 API.
311
+ #
312
+ # session.request :compute_service, :create_server, :api_version => v1
313
+ #
314
+ # The available options vary depending on the provider. See the documentation
315
+ # on the provider's Provider class for more information (e.g. Aviator::OpenStack::Provider)
316
+ #
317
+ def request(service_name, request_name, opts={}, &params)
318
+ service = send("#{service_name.to_s}_service")
319
+ service.request(request_name, opts, &params)
320
+ end
321
+
322
+
323
+ #
324
+ # Returns true if the session is still valid in the underlying provider. This method does this
325
+ # by calling the validator request class declared declared under <tt>auth_service</tt> in the
326
+ # configuration. The validator can be any request class as long as:
327
+ #
328
+ # * The request class exists!
329
+ # * Does not require any parameters
330
+ # * It returns an HTTP status 200 or 203 to indicate auth info validity.
331
+ # * It returns any other HTTP status to indicate that the auth info is invalid.
332
+ #
333
+ # See Session::new for an example on how to specify the request class to use for session validation.
334
+ #
121
335
  def validate
122
336
  raise NotAuthenticatedError.new unless authenticated?
123
- raise ValidatorNotDefinedError.new unless environment[:auth_service][:validator]
337
+ raise ValidatorNotDefinedError.new unless config[:auth_service][:validator]
124
338
 
125
- auth_with_bootstrap = auth_response.merge({ :auth_service => environment[:auth_service] })
339
+ auth_with_bootstrap = auth_response.merge({ :auth_service => config[:auth_service] })
126
340
 
127
- response = auth_service.request environment[:auth_service][:validator].to_sym, :session_data => auth_with_bootstrap
341
+ response = auth_service.request config[:auth_service][:validator].to_sym, :session_data => auth_with_bootstrap
128
342
  response.status == 200 || response.status == 203
129
343
  end
130
344
 
@@ -139,27 +353,22 @@ module Aviator
139
353
 
140
354
  def auth_service
141
355
  @auth_service ||= Service.new(
142
- :provider => environment[:provider],
143
- :service => environment[:auth_service][:name],
144
- :default_session_data => { :auth_service => environment[:auth_service] },
356
+ :provider => config[:provider],
357
+ :service => config[:auth_service][:name],
358
+ :default_session_data => { :auth_service => config[:auth_service] },
145
359
  :log_file => log_file
146
360
  )
147
361
  end
148
362
 
149
363
 
150
- def environment
151
- @environment
152
- end
153
-
154
-
155
364
  def get_service_obj(service_name)
156
365
  @services ||= {}
157
366
 
158
367
  if @services[service_name].nil?
159
- default_options = environment["#{ service_name }_service"]
368
+ default_options = config["#{ service_name }_service"]
160
369
 
161
370
  @services[service_name] = Service.new(
162
- :provider => environment[:provider],
371
+ :provider => config[:provider],
163
372
  :service => service_name,
164
373
  :default_session_data => auth_response,
165
374
  :default_options => default_options,
@@ -174,28 +383,23 @@ module Aviator
174
383
  def initialize_with_config(config_path, environment)
175
384
  raise InvalidConfigFilePathError.new(config_path) unless Pathname.new(config_path).file?
176
385
 
177
- config = Hashish.new(YAML.load_file(config_path))
386
+ all_config = Hashish.new(YAML.load_file(config_path))
178
387
 
179
- raise EnvironmentNotDefinedError.new(config_path, environment) unless config[environment]
388
+ raise EnvironmentNotDefinedError.new(config_path, environment) unless all_config[environment]
180
389
 
181
- @environment = config[environment]
390
+ @config = all_config[environment]
182
391
  end
183
392
 
184
393
 
185
394
  def initialize_with_dump(session_dump)
186
395
  session_info = Hashish.new(JSON.parse(session_dump))
187
- @environment = session_info[:environment]
396
+ @config = session_info[:config]
188
397
  @auth_response = session_info[:auth_response]
189
398
  end
190
399
 
191
400
 
192
401
  def initialize_with_hash(hash_obj)
193
- @environment = Hashish.new(hash_obj)
194
- end
195
-
196
-
197
- def log_file
198
- @log_file
402
+ @config = Hashish.new(hash_obj)
199
403
  end
200
404
 
201
405
 
@@ -24,6 +24,7 @@ module Aviator
24
24
  param :email, :required => false
25
25
  param :enabled, :required => false
26
26
  param :tenantId, :required => false, :alias => :tenant_id
27
+ param :extras, :required => false
27
28
 
28
29
 
29
30
  def body
@@ -20,6 +20,7 @@ module Aviator
20
20
  param :email, :required => false
21
21
  param :enabled, :required => false
22
22
  param :tenantId, :required => false, :alias => :tenant_id
23
+ param :extras, :required => false
23
24
 
24
25
 
25
26
  def body
@@ -1,6 +1,68 @@
1
1
  module Aviator
2
2
  module Openstack
3
3
 
4
+ #
5
+ # Manages a provider (e.g. OpenStack) session.
6
+ #
7
+ # Author:: Mark Maglana (mmaglana@gmail.com)
8
+ # Copyright:: Copyright (c) 2014 Mark Maglana
9
+ # License:: Distributed under the MIT license
10
+ # Homepage:: http://aviator.github.io/www/
11
+ #
12
+ # <b>Request Options</b>
13
+ #
14
+ # The following options may be used in combination with each other when calling
15
+ # an OpenStack request class
16
+ #
17
+ # :api_version => :v2::
18
+ # Forces Aviator to use the request class for the v2 API. For any other
19
+ # version, replace Note that this may throw an error if no such request
20
+ # class exists. If you want to globally specify the API version to use for
21
+ # a specific service, declare it in your config file under the correct
22
+ # environment. For example:
23
+ #
24
+ # production:
25
+ # provider: openstack
26
+ # ...
27
+ # compute_service:
28
+ # api_version: v2
29
+ #
30
+ # Note that the <tt>:api_version</tt> option overrides whatever is declared in the
31
+ # configuration.
32
+ #
33
+ # :endpoint_type => (:public|:admin)::
34
+ # This allows you to be specific about the endpoint type in cases where two
35
+ # request classes under admin and public endpoints of the same service share
36
+ # the same name. This is true, for example, for the :list_tenants request of
37
+ # the identity service's v2 API. Its public endpoint will return only the tenants
38
+ # the user is a member of whereas the admin endpoint will return all tenants
39
+ # in the system.
40
+ #
41
+ # :session_data => Hash::
42
+ # Under normal situations, you wouldn't need to use this as it is automatically populated
43
+ # by the Session object provided it is authenticated. The specific use case when you'd
44
+ # need to set thsi optin is when you want to use Aviator to seed your OpenStack installation.
45
+ # In such a scenario, you would need to use a service token since no usernames and tenants
46
+ # would exist yet in the environment. To use a service token with Aviator, you will need to
47
+ # write something similar to the following example:
48
+ #
49
+ # openstack = Aviator::Session.new(:config => { :provider => 'openstack'})
50
+ #
51
+ # session_data = {:base_url => 'http://example.com',
52
+ # :service_token => 'service-token-created-at-openstack-install-time'}
53
+ #
54
+ # openstack.request :identity, :create_tenant, :api_version => :v2, :session_data => session_data) do |params|
55
+ # params.name = 'Tenant A'
56
+ # params.description = 'First Tenant!'
57
+ # params.enabled = true
58
+ # end
59
+ #
60
+ # Notice how the above code skips authentication. This is because the service token is
61
+ # pre-validated and ready for use with any request. Also note how we're providing a <tt>:base_url</tt>
62
+ # member in our session data. This is necessary since we normally get the service endpoints from
63
+ # Keystone when we authenticate. Now since we are not authenticating against Keystone, we don't have
64
+ # that catalogue to begin with. Thus the need to hardcode it in the request.
65
+ #
4
66
  module Provider
5
67
 
6
68
  class MultipleServiceApisError < StandardError
@@ -35,6 +97,7 @@ EOF
35
97
 
36
98
  class << self
37
99
 
100
+ #:nodoc:
38
101
  def find_request(service, name, session_data, options)
39
102
  service = service.to_s
40
103
  endpoint_type = options[:endpoint_type]