Class: Aviator::Session

Inherits:
Object show all
Defined in:
lib/puppet/feature/aviator/core/session.rb

Overview

Manages a provider (e.g. OpenStack) session.

Author

Mark Maglana (mmaglana@gmail.com)

Copyright

Copyright © 2014 Mark Maglana

License

Distributed under the MIT license

Homepage

aviator.github.io/www/

Defined Under Namespace

Classes: AuthenticationError, EnvironmentNotDefinedError, InitializationError, InvalidConfigFilePathError, NotAuthenticatedError, ValidatorNotDefinedError

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Session

Create a new Session instance with options provided in opts which can have many forms discussed below.

Initialize with a config file

Aviator::Session.new(:config_file => 'path/to/aviator.yml', :environment => :production)

In the above example, the config file must have the following form:

production:
  provider: openstack
  auth_service:
    name: identity
    host_uri: 'http://my.openstackenv.org:5000'
    request: create_token
    validator: list_tenants
    api_version: v2
  auth_credentials:
    username: myusername
    password: mypassword
    tenant_name: myproject

Once the session has been instantiated, you may authenticate against the provider as follows:

session.authenticate

Note that the required items under auth_credentials in the config file depends on the required parameters of the request class declared under auth_service. If writing the auth_credentials in the config file is not acceptable, you may omit it and just supply the credentials at runtime. For instance, assume that the auth_credentials section in the config file above is missing. You would then authenticate as follows:

session.authenticate do |params|
  params.username    = ARGV[0]
  params.password    = ARGV[1]
  params.tenant_name = ARGV[2]
end

Please see Session#authenticate for more info.

Note that while the example config file above only has one environment (production), you can declare an arbitrary number of environments in your config file. Shifting between environments is as simple as changing the :environment to refer to that.

Initialize with an in-memory hash

You can create an in-memory hash which is similar in structure to the config file except that you don’t need to specify an environment name. For example:

configuration = {
  :provider => 'openstack',
  :auth_service => {
    :name      => 'identity',
    :host_uri  => 'http://devstack:5000/v2.0',
    :request   => 'create_token',
    :validator => 'list_tenants'
  }
}

Supply this to the initializer using the :config option. For example:

Aviator::Session.new(:config => configuration)

Initialize with a session dump

You can create a new Session instance using a dump from another instance. For example:

session_dump = session1.dump
session2 = Aviator::Session.new(:session_dump => session_dump)

However, Session.load is cleaner and recommended over this method.

Optionally supply a log file

In all forms above, you may optionally add a :log_file option to make Aviator write all HTTP calls to the given path. For example:

Aviator::Session.new(:config_file => 'path/to/aviator.yml', :environment => :production, :log_file => 'path/to/log')


138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/puppet/feature/aviator/core/session.rb', line 138

def initialize(opts={})
  if opts.has_key? :session_dump
    initialize_with_dump(opts[:session_dump])
  elsif opts.has_key? :config_file
    initialize_with_config(opts[:config_file], opts[:environment])
  elsif opts.has_key? :config
    initialize_with_hash(opts[:config])
  else
    raise InitializationError.new
  end

  @log_file = opts[:log_file]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



250
251
252
253
254
255
256
257
258
# File 'lib/puppet/feature/aviator/core/session.rb', line 250

def method_missing(name, *args, &block)
  service_name_parts = name.to_s.match(/^(\w+)_service$/)

  if service_name_parts
    get_service_obj(service_name_parts[1])
  else
    super name, *args, &block
  end
end

Class Method Details

.load(session_dump, opts = {}) ⇒ Object

Creates a new Session object from a previous session’s dump. See Session#dump for more information.

If you want the newly deserialized session to log its output, make sure to indicate it on load

Aviator::Session.load(session_dump_str, :log_file => 'path/to/aviator.log')


269
270
271
272
273
# File 'lib/puppet/feature/aviator/core/session.rb', line 269

def self.load(session_dump, opts={})
  opts[:session_dump] = session_dump

  new(opts)
end

Instance Method Details

#authenticate(&block) ⇒ Object

Authenticates against the auth_service request class declared in the session’s configuration during initialization. Please see Session.new for more information on declaring the request class to use for authentication.

If the auth_service request class accepts a parameter block, you may also supply that when calling this method and it will be directly passed to the request. For example:

session = Aviator::Session.new(:config => config)
session.authenticate do |params|
  params.username    = username
  params.password    = password
  params.tenant_name = project
end

Expects an HTTP status 200 or 201. Any other status is treated as a failure.

Note that you can also treat the block’s argument like a hash with the attribute names as the keys. For example, we can rewrite the above as:

session = Aviator::Session.new(:config => config)
session.authenticate do |params|
  params[:username]    = username
  params[:password]    = password
  params[:tenant_name] = project
end

Keys can be symbols or strings.



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/puppet/feature/aviator/core/session.rb', line 181

def authenticate(&block)
  block ||= lambda do |params|
    config[:auth_credentials].each do |key, value|
      begin
        params[key] = value
      rescue NameError => e
        raise NameError.new("Unknown param name '#{key}'")
      end
    end
  end

  response = auth_service.request config[:auth_service][:request].to_sym, &block

  if [200, 201].include? response.status
    @auth_response = Hashish.new({
      :headers => response.headers,
      :body    => response.body
    })
    update_services_session_data
  else
    raise AuthenticationError.new(response.body)
  end
  self
end

#authenticated?Boolean

Returns true if the session has been authenticated.

Returns:

  • (Boolean)


209
210
211
# File 'lib/puppet/feature/aviator/core/session.rb', line 209

def authenticated?
  !auth_response.nil?
end

#configObject

Returns its configuration.



216
217
218
# File 'lib/puppet/feature/aviator/core/session.rb', line 216

def config
  @config
end

#dumpObject

Returns a JSON string of its configuration and auth_data. This string can be streamed or stored and later re-loaded in another Session instance. For example:

session = Aviator::Session.new(:config => configuration)
str = session.dump

# time passes...

session = Aviator::Session.load(str)


231
232
233
234
235
236
# File 'lib/puppet/feature/aviator/core/session.rb', line 231

def dump
  JSON.generate({
    :config        => config,
    :auth_response => auth_response
  })
end

#load(session_dump) ⇒ Object

Same as Session::load but re-uses the Session instance this method is called on instead of creating a new one.



243
244
245
246
247
# File 'lib/puppet/feature/aviator/core/session.rb', line 243

def load(session_dump)
  initialize_with_dump(session_dump)
  update_services_session_data
  self
end

#log_fileObject

Returns the log file path. May be nil if none was provided during initialization.



279
280
281
# File 'lib/puppet/feature/aviator/core/session.rb', line 279

def log_file
  @log_file
end

#request(service_name, request_name, opts = {}, &params) ⇒ Object

Calls the given request of the given service. An example call might look like:

session.request :compute_service, :create_server do |p|
  p.name       = "My Server"
  p.image_ref  = "7cae8c8e-fb01-4a88-bba3-ae0fcb1dbe29"
  p.flavor_ref = "fa283da1-59a5-4245-8569-b6eadf69f10b"
end

Note that you can also treat the block’s argument like a hash with the attribute names as the keys. For example, we can rewrite the above as:

session.request :compute_service, :create_server do |p|
  p[:name]       = "My Server"
  p[:image_ref]  = "7cae8c8e-fb01-4a88-bba3-ae0fcb1dbe29"
  p[:flavor_ref] = "fa283da1-59a5-4245-8569-b6eadf69f10b"
end

Keys can be symbols or strings.


Request Options

You can further customize how the request is fulfilled by providing one or more options to the method call. For example, the following ensures that the request will call the :create_server request for the v1 API.

session.request :compute_service, :create_server, :api_version => v1

The available options vary depending on the provider. See the documentation on the provider’s Provider class for more information (e.g. Aviator::OpenStack::Provider)



317
318
319
320
# File 'lib/puppet/feature/aviator/core/session.rb', line 317

def request(service_name, request_name, opts={}, &params)
  service = send("#{service_name.to_s}_service")
  service.request(request_name, opts, &params)
end

#validateObject

Returns true if the session is still valid in the underlying provider. This method does this by calling the validator request class declared declared under auth_service in the configuration. The validator can be any request class as long as:

  • The request class exists!

  • Does not require any parameters

  • It returns an HTTP status 200 or 203 to indicate auth info validity.

  • It returns any other HTTP status to indicate that the auth info is invalid.

See Session::new for an example on how to specify the request class to use for session validation.



335
336
337
338
339
340
341
342
343
# File 'lib/puppet/feature/aviator/core/session.rb', line 335

def validate
  raise NotAuthenticatedError.new unless authenticated?
  raise ValidatorNotDefinedError.new unless config[:auth_service][:validator]

  auth_with_bootstrap = auth_response.merge({ :auth_service  => config[:auth_service] })

  response = auth_service.request config[:auth_service][:validator].to_sym, :session_data => auth_with_bootstrap
  response.status == 200 || response.status == 203
end