aviator 0.0.9 → 0.1.0

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