Merge "Restructures authentication for resource providers"
This commit is contained in:
		@@ -12,185 +12,73 @@ class Puppet::Provider::Openstack < Puppet::Provider
 | 
			
		||||
  initvars # so commands will work
 | 
			
		||||
  commands :openstack => 'openstack'
 | 
			
		||||
 | 
			
		||||
  def request(service, action, object, credentials, *properties)
 | 
			
		||||
    if password_credentials_set?(credentials)
 | 
			
		||||
      auth_args = password_auth_args(credentials)
 | 
			
		||||
    elsif openrc_set?(credentials)
 | 
			
		||||
      credentials = get_credentials_from_openrc(credentials['openrc'])
 | 
			
		||||
      auth_args = password_auth_args(credentials)
 | 
			
		||||
    elsif service_credentials_set?(credentials)
 | 
			
		||||
      auth_args = token_auth_args(credentials)
 | 
			
		||||
    elsif env_vars_set?
 | 
			
		||||
      # noop; auth needs no extra arguments
 | 
			
		||||
      auth_args = nil
 | 
			
		||||
    else  # All authentication efforts failed
 | 
			
		||||
      raise(Puppet::Error::OpenstackAuthInputError, 'No credentials provided.')
 | 
			
		||||
    end
 | 
			
		||||
    args = [object, properties, auth_args].flatten.compact
 | 
			
		||||
    authenticate_request(service, action, args)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.request(service, action, object, *properties)
 | 
			
		||||
    if env_vars_set?
 | 
			
		||||
      # noop; auth needs no extra arguments
 | 
			
		||||
      auth_args = nil
 | 
			
		||||
    else  # All authentication efforts failed
 | 
			
		||||
      raise(Puppet::Error::OpenstackAuthInputError, 'No credentials provided.')
 | 
			
		||||
    end
 | 
			
		||||
    args = [object, properties, auth_args].flatten.compact
 | 
			
		||||
    authenticate_request(service, action, args)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Returns an array of hashes, where the keys are the downcased CSV headers
 | 
			
		||||
  # with underscores instead of spaces
 | 
			
		||||
  def self.authenticate_request(service, action, *args)
 | 
			
		||||
    rv = nil
 | 
			
		||||
    timeout = 10
 | 
			
		||||
    end_time = Time.now.to_i + timeout
 | 
			
		||||
    loop do
 | 
			
		||||
      begin
 | 
			
		||||
        if(action == 'list')
 | 
			
		||||
          response = openstack(service, action, '--quiet', '--format', 'csv', args)
 | 
			
		||||
          response = parse_csv(response)
 | 
			
		||||
          keys = response.delete_at(0) # ID,Name,Description,Enabled
 | 
			
		||||
          rv = response.collect do |line|
 | 
			
		||||
            hash = {}
 | 
			
		||||
            keys.each_index do |index|
 | 
			
		||||
              key = keys[index].downcase.gsub(/ /, '_').to_sym
 | 
			
		||||
              hash[key] = line[index]
 | 
			
		||||
  def self.request(service, action, properties, credentials=nil)
 | 
			
		||||
    env = credentials ? credentials.to_env : {}
 | 
			
		||||
    Puppet::Util.withenv(env) do
 | 
			
		||||
      rv = nil
 | 
			
		||||
      timeout = 10
 | 
			
		||||
      end_time = Time.now.to_i + timeout
 | 
			
		||||
      loop do
 | 
			
		||||
        begin
 | 
			
		||||
          if(action == 'list')
 | 
			
		||||
            response = openstack(service, action, '--quiet', '--format', 'csv', properties)
 | 
			
		||||
            response = parse_csv(response)
 | 
			
		||||
            keys = response.delete_at(0) # ID,Name,Description,Enabled
 | 
			
		||||
            rv = response.collect do |line|
 | 
			
		||||
              hash = {}
 | 
			
		||||
              keys.each_index do |index|
 | 
			
		||||
                key = keys[index].downcase.gsub(/ /, '_').to_sym
 | 
			
		||||
                hash[key] = line[index]
 | 
			
		||||
              end
 | 
			
		||||
              hash
 | 
			
		||||
            end
 | 
			
		||||
          elsif(action == 'show' || action == 'create')
 | 
			
		||||
            rv = {}
 | 
			
		||||
            # shell output is name="value"\nid="value2"\ndescription="value3" etc.
 | 
			
		||||
            openstack(service, action, '--format', 'shell', properties).split("\n").each do |line|
 | 
			
		||||
              # key is everything before the first "="
 | 
			
		||||
              key, val = line.split("=", 2)
 | 
			
		||||
              next unless val # Ignore warnings
 | 
			
		||||
              # value is everything after the first "=", with leading and trailing double quotes stripped
 | 
			
		||||
              val = val.gsub(/\A"|"\Z/, '')
 | 
			
		||||
              rv[key.downcase.to_sym] = val
 | 
			
		||||
            end
 | 
			
		||||
            hash
 | 
			
		||||
          end
 | 
			
		||||
        elsif(action == 'show' || action == 'create')
 | 
			
		||||
          rv = {}
 | 
			
		||||
          # shell output is name="value"\nid="value2"\ndescription="value3" etc.
 | 
			
		||||
          openstack(service, action, '--format', 'shell', args).split("\n").each do |line|
 | 
			
		||||
            # key is everything before the first "="
 | 
			
		||||
            key, val = line.split("=", 2)
 | 
			
		||||
            next unless val # Ignore warnings
 | 
			
		||||
            # value is everything after the first "=", with leading and trailing double quotes stripped
 | 
			
		||||
            val = val.gsub(/\A"|"\Z/, '')
 | 
			
		||||
            rv[key.downcase.to_sym] = val
 | 
			
		||||
          end
 | 
			
		||||
        else
 | 
			
		||||
          rv = openstack(service, action, args)
 | 
			
		||||
        end
 | 
			
		||||
        break
 | 
			
		||||
      rescue Puppet::ExecutionFailure => e
 | 
			
		||||
        if e.message =~ /HTTP 401/
 | 
			
		||||
          raise(Puppet::Error::OpenstackUnauthorizedError, 'Could not authenticate.')
 | 
			
		||||
        elsif e.message =~ /Unable to establish connection/
 | 
			
		||||
          current_time = Time.now.to_i
 | 
			
		||||
          if current_time > end_time
 | 
			
		||||
            break
 | 
			
		||||
          else
 | 
			
		||||
            wait = end_time - current_time
 | 
			
		||||
            Puppet::debug("Non-fatal error: \"#{e.message}\"; retrying for #{wait} more seconds.")
 | 
			
		||||
            if wait > timeout - 2 # Only notice the first time
 | 
			
		||||
              notice("#{service} service is unavailable. Will retry for up to #{wait} seconds.")
 | 
			
		||||
            end
 | 
			
		||||
            rv = openstack(service, action, properties)
 | 
			
		||||
          end
 | 
			
		||||
          break
 | 
			
		||||
        rescue Puppet::ExecutionFailure => e
 | 
			
		||||
          if e.message =~ /HTTP 401/
 | 
			
		||||
            raise(Puppet::Error::OpenstackUnauthorizedError, 'Could not authenticate.')
 | 
			
		||||
          elsif e.message =~ /Unable to establish connection/
 | 
			
		||||
            current_time = Time.now.to_i
 | 
			
		||||
            if current_time > end_time
 | 
			
		||||
              break
 | 
			
		||||
            else
 | 
			
		||||
              wait = end_time - current_time
 | 
			
		||||
              Puppet::debug("Non-fatal error: \"#{e.message}\"; retrying for #{wait} more seconds.")
 | 
			
		||||
              if wait > timeout - 2 # Only notice the first time
 | 
			
		||||
                notice("#{service} service is unavailable. Will retry for up to #{wait} seconds.")
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
            sleep(2)
 | 
			
		||||
          else
 | 
			
		||||
            raise e
 | 
			
		||||
          end
 | 
			
		||||
          sleep(2)
 | 
			
		||||
        else
 | 
			
		||||
          raise e
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      return rv
 | 
			
		||||
    end
 | 
			
		||||
    return rv
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def authenticate_request(service, action, *args)
 | 
			
		||||
    self.class.authenticate_request(service, action, *args)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def password_credentials_set?(auth_params)
 | 
			
		||||
    auth_params && auth_params['username'] && auth_params['password'] && auth_params['project_name'] && auth_params['auth_url']
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def openrc_set?(auth_params)
 | 
			
		||||
    auth_params && auth_params['openrc']
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def service_credentials_set?(auth_params)
 | 
			
		||||
    auth_params && auth_params['token'] && auth_params['url']
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.env_vars_set?
 | 
			
		||||
    ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['OS_PROJECT_NAME'] && ENV['OS_AUTH_URL']
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def env_vars_set?
 | 
			
		||||
    self.class.env_vars_set?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.password_auth_args(credentials)
 | 
			
		||||
    creds = [ '--os-username',     credentials['username'],
 | 
			
		||||
              '--os-password',     credentials['password'],
 | 
			
		||||
              '--os-project-name', credentials['project_name'],
 | 
			
		||||
              '--os-auth-url',     credentials['auth_url'] ]
 | 
			
		||||
 | 
			
		||||
    if credentials.include?('project_domain_name')
 | 
			
		||||
      creds << '--os-project-domain-name'
 | 
			
		||||
      creds << credentials['project_domain_name']
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if credentials.include?('user_domain_name')
 | 
			
		||||
      creds << '--os-user-domain-name'
 | 
			
		||||
      creds << credentials['user_domain_name']
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    creds
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def password_auth_args(credentials)
 | 
			
		||||
    self.class.password_auth_args(credentials)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.token_auth_args(credentials)
 | 
			
		||||
    args = [ '--os-token', credentials['token'],
 | 
			
		||||
             '--os-url',   credentials['url'] ]
 | 
			
		||||
    # Add the api version only if requested by the caller
 | 
			
		||||
    if credentials['version']
 | 
			
		||||
      args << [ '--os-identity-api-version', credentials['version'] ]
 | 
			
		||||
    end
 | 
			
		||||
    args
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def token_auth_args(credentials)
 | 
			
		||||
    self.class.token_auth_args(credentials)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def get_credentials_from_openrc(file)
 | 
			
		||||
    creds = {}
 | 
			
		||||
    File.open(file).readlines.delete_if{|l| l=~ /^#/}.each do |line|
 | 
			
		||||
      key, value = line.split('=')
 | 
			
		||||
      key = key.split(' ').last.downcase.sub(/^os_/, '')
 | 
			
		||||
      value = value.chomp.gsub(/'/, '')
 | 
			
		||||
      creds[key] = value
 | 
			
		||||
    end
 | 
			
		||||
    return creds
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.get_credentials_from_env
 | 
			
		||||
    env = ENV.to_hash.dup.delete_if { |key, _| ! (key =~ /^OS_/) }
 | 
			
		||||
    credentials = {}
 | 
			
		||||
    env.each do |name, value|
 | 
			
		||||
      credentials[name.downcase.sub(/^os_/, '')] = value
 | 
			
		||||
    end
 | 
			
		||||
    credentials
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def get_credentials_from_env
 | 
			
		||||
    self.class.get_credentials_from_env
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.parse_csv(text)
 | 
			
		||||
    # Ignore warnings - assume legitimate output starts with a double quoted
 | 
			
		||||
    # string.  Errors will be caught and raised prior to this
 | 
			
		||||
    text = text.split("\n").drop_while { |line| line !~ /^\".*\"/ }.join("\n")
 | 
			
		||||
    return CSV.parse(text + "\n")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								lib/puppet/provider/openstack/auth.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/puppet/provider/openstack/auth.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
require 'puppet/provider/openstack/credentials'
 | 
			
		||||
 | 
			
		||||
module Puppet::Provider::Openstack::Auth
 | 
			
		||||
 | 
			
		||||
  RCFILENAME = "#{ENV['HOME']}/openrc"
 | 
			
		||||
 | 
			
		||||
  def get_os_vars_from_env
 | 
			
		||||
    env = {}
 | 
			
		||||
    ENV.each { |k,v| env.merge!(k => v) if k =~ /^OS_/ }
 | 
			
		||||
    return env
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def get_os_vars_from_rcfile(filename)
 | 
			
		||||
    env = {}
 | 
			
		||||
    if File.exists?(filename)
 | 
			
		||||
      File.open(filename).readlines.delete_if{|l| l=~ /^#|^$/ }.each do |line|
 | 
			
		||||
        key, value = line.split('=')
 | 
			
		||||
        key = key.split(' ').last
 | 
			
		||||
        value = value.chomp.gsub(/'/, '')
 | 
			
		||||
        env.merge!(key => value) if key =~ /OS_/
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    return env
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def rc_filename
 | 
			
		||||
    RCFILENAME
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def request(service, action, properties=nil)
 | 
			
		||||
    properties ||= []
 | 
			
		||||
    set_credentials(@credentials, get_os_vars_from_env)
 | 
			
		||||
    unless @credentials.set?
 | 
			
		||||
      @credentials.unset
 | 
			
		||||
      set_credentials(@credentials, get_os_vars_from_rcfile(rc_filename))
 | 
			
		||||
    end
 | 
			
		||||
    unless @credentials.set?
 | 
			
		||||
      raise(Puppet::Error::OpenstackAuthInputError, 'Insufficient credentials to authenticate')
 | 
			
		||||
    end
 | 
			
		||||
    super(service, action, properties, @credentials)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_credentials(creds, env)
 | 
			
		||||
    env.each do |key, val|
 | 
			
		||||
      var = key.sub(/^OS_/,'').downcase
 | 
			
		||||
      creds.set(var, val)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										56
									
								
								lib/puppet/provider/openstack/credentials.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								lib/puppet/provider/openstack/credentials.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
require 'puppet'
 | 
			
		||||
require 'puppet/provider/openstack'
 | 
			
		||||
 | 
			
		||||
class Puppet::Provider::Openstack::Credentials
 | 
			
		||||
 | 
			
		||||
  KEYS = [
 | 
			
		||||
    :auth_url, :password, :project_name, :username,
 | 
			
		||||
    :token, :url,
 | 
			
		||||
    :identity_api_version
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
  KEYS.each { |var| attr_accessor var }
 | 
			
		||||
 | 
			
		||||
  def self.defined?(name)
 | 
			
		||||
    KEYS.include?(name.to_sym)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set(key, val)
 | 
			
		||||
    if self.class.defined?(key.to_sym)
 | 
			
		||||
      self.instance_variable_set("@#{key}".to_sym, val)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set?
 | 
			
		||||
    return true if user_password_set? || service_token_set?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def service_token_set?
 | 
			
		||||
    return true if @token && @url
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def to_env
 | 
			
		||||
    env = {}
 | 
			
		||||
    self.instance_variables.each do |var|
 | 
			
		||||
      name = var.to_s.sub(/^@/,'OS_').upcase
 | 
			
		||||
      env.merge!(name => self.instance_variable_get(var))
 | 
			
		||||
    end
 | 
			
		||||
    env
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def user_password_set?
 | 
			
		||||
    return true if @username && @password && @project_name && @auth_url
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unset
 | 
			
		||||
    list = KEYS.delete_if { |key, val| key == :identity_api_version }
 | 
			
		||||
    list.each { |key, val| self.set(key, '') if self.class.defined?("@#{key}".to_sym) }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def version
 | 
			
		||||
    self.class.to_s.sub(/.*V/,'').sub('_','.')
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Puppet::Provider::Openstack::CredentialsV2_0 < Puppet::Provider::Openstack::Credentials
 | 
			
		||||
end
 | 
			
		||||
@@ -1,66 +0,0 @@
 | 
			
		||||
# Add the auth parameter to whatever type is given
 | 
			
		||||
module Puppet::Util::Openstack
 | 
			
		||||
  def self.add_openstack_type_methods(type, comment)
 | 
			
		||||
 | 
			
		||||
    type.newparam(:auth) do
 | 
			
		||||
 | 
			
		||||
      desc <<EOT
 | 
			
		||||
Hash of authentication credentials. Credentials can be specified as either :
 | 
			
		||||
 | 
			
		||||
1. Using a project/user with a password
 | 
			
		||||
 | 
			
		||||
For Keystone API V2:
 | 
			
		||||
auth => {
 | 
			
		||||
  'username'     => 'test',
 | 
			
		||||
  'password'     => 'changeme',
 | 
			
		||||
  'project_name' => 'test',
 | 
			
		||||
  'auth_url'     => 'http://localhost:35357/v2.0'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
or altenatively for Keystone API V3:
 | 
			
		||||
auth => {
 | 
			
		||||
  'username'            => 'test',
 | 
			
		||||
  'password'            => 'changeme',
 | 
			
		||||
  'project_name'        => 'test',
 | 
			
		||||
  'project_domain_name' => 'domain1',
 | 
			
		||||
  'user_domain_name'    => 'domain1',
 | 
			
		||||
  'auth_url'            => 'http://localhost:35357/v3'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
2. Using a path to an openrc file containing these credentials
 | 
			
		||||
 | 
			
		||||
auth => {
 | 
			
		||||
  'openrc' => '/root/openrc'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
3. Using a service token
 | 
			
		||||
 | 
			
		||||
For Keystone API V2:
 | 
			
		||||
auth => {
 | 
			
		||||
  'token' => 'example',
 | 
			
		||||
  'url'   => 'http://localhost:35357/v2.0'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Alternatively for Keystone API V3:
 | 
			
		||||
auth => {
 | 
			
		||||
  'token' => 'example',
 | 
			
		||||
  'url'   => 'http://localhost:35357/v3.0'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
If not present, the provider will look for environment variables for
 | 
			
		||||
password credentials.
 | 
			
		||||
 | 
			
		||||
#{comment}
 | 
			
		||||
EOT
 | 
			
		||||
 | 
			
		||||
      validate do |value|
 | 
			
		||||
        raise(Puppet::Error, 'This property must be a hash') unless value.is_a?(Hash)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    type.autorequire(:package) do
 | 
			
		||||
      'python-openstackclient'
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										181
									
								
								spec/unit/provider/openstack/auth_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								spec/unit/provider/openstack/auth_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
			
		||||
require 'puppet'
 | 
			
		||||
require 'spec_helper'
 | 
			
		||||
require 'puppet/provider/openstack'
 | 
			
		||||
require 'puppet/provider/openstack/auth'
 | 
			
		||||
require 'tempfile'
 | 
			
		||||
 | 
			
		||||
class Puppet::Provider::Openstack::AuthTester < Puppet::Provider::Openstack
 | 
			
		||||
  extend Puppet::Provider::Openstack::Auth
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
klass = Puppet::Provider::Openstack::AuthTester
 | 
			
		||||
 | 
			
		||||
describe Puppet::Provider::Openstack::Auth do
 | 
			
		||||
 | 
			
		||||
  let(:type) do
 | 
			
		||||
    Puppet::Type.newtype(:test_resource) do
 | 
			
		||||
      newparam(:name, :namevar => true)
 | 
			
		||||
      newparam(:log_file)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:resource_attrs) do
 | 
			
		||||
    {
 | 
			
		||||
      :name => 'stubresource'
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  let(:provider) do
 | 
			
		||||
    klass.new(type.new(resource_attrs))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  before(:each) do
 | 
			
		||||
    ENV['OS_USERNAME']     = nil
 | 
			
		||||
    ENV['OS_PASSWORD']     = nil
 | 
			
		||||
    ENV['OS_PROJECT_NAME'] = nil
 | 
			
		||||
    ENV['OS_AUTH_URL']     = nil
 | 
			
		||||
    ENV['OS_TOKEN']        = nil
 | 
			
		||||
    ENV['OS_URL']          = nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#set_credentials' do
 | 
			
		||||
    it 'adds keys to the object' do
 | 
			
		||||
      credentials =  Puppet::Provider::Openstack::CredentialsV2_0.new
 | 
			
		||||
      set = { 'OS_USERNAME' => 'user', 'OS_PASSWORD' => 'secret',
 | 
			
		||||
        'OS_PROJECT_NAME' => 'tenant',
 | 
			
		||||
        'OS_AUTH_URL' => 'http://127.0.0.1:5000',
 | 
			
		||||
        'OS_TOKEN' => 'token',
 | 
			
		||||
        'OS_URL' => 'http://127.0.0.1:35357',
 | 
			
		||||
        'OS_IDENTITY_API_VERSION' => '2.0'
 | 
			
		||||
        }
 | 
			
		||||
      klass.set_credentials(credentials, set)
 | 
			
		||||
      expect(credentials.to_env).to eq("OS_AUTH_URL" => "http://127.0.0.1:5000",
 | 
			
		||||
          "OS_IDENTITY_API_VERSION" => '2.0',
 | 
			
		||||
          "OS_PASSWORD" => "secret",
 | 
			
		||||
          "OS_PROJECT_NAME" => "tenant",
 | 
			
		||||
          "OS_TOKEN" => "token",
 | 
			
		||||
          "OS_URL" => "http://127.0.0.1:35357",
 | 
			
		||||
          "OS_USERNAME" => "user")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#rc_filename' do
 | 
			
		||||
    it 'returns RCFILENAME' do
 | 
			
		||||
      expect(klass.rc_filename).to eq("#{ENV['HOME']}/openrc")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#get_os_from_env' do
 | 
			
		||||
    context 'with Openstack environment variables set' do
 | 
			
		||||
      it 'provides a hash' do
 | 
			
		||||
        ENV['OS_AUTH_URL']     = 'http://127.0.0.1:5000'
 | 
			
		||||
        ENV['OS_PASSWORD']     = 'abc123'
 | 
			
		||||
        ENV['OS_PROJECT_NAME'] = 'test'
 | 
			
		||||
        ENV['OS_USERNAME']     = 'test'
 | 
			
		||||
        response = klass.get_os_vars_from_env
 | 
			
		||||
        expect(response).to eq({"OS_AUTH_URL" => "http://127.0.0.1:5000","OS_PASSWORD" => "abc123","OS_PROJECT_NAME" => "test","OS_USERNAME" => "test"})
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#get_os_vars_from_rcfile' do
 | 
			
		||||
    context 'with a valid RC file' do
 | 
			
		||||
      it 'provides a hash' do
 | 
			
		||||
        mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_PROJECT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000'"
 | 
			
		||||
        filename = 'file'
 | 
			
		||||
        File.expects(:exists?).with('file').returns(true)
 | 
			
		||||
        File.expects(:open).with('file').returns(StringIO.new(mock))
 | 
			
		||||
 | 
			
		||||
        response = klass.get_os_vars_from_rcfile(filename)
 | 
			
		||||
        expect(response).to eq({"OS_AUTH_URL" => "http://127.0.0.1:5000","OS_PASSWORD" => "abc123","OS_PROJECT_NAME" => "test","OS_USERNAME" => "test"})
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with an empty file' do
 | 
			
		||||
      it 'provides an empty hash' do
 | 
			
		||||
        filename = 'file'
 | 
			
		||||
        File.expects(:exists?).with(filename).returns(true)
 | 
			
		||||
        File.expects(:open).with(filename).returns(StringIO.new(""))
 | 
			
		||||
 | 
			
		||||
        response = klass.get_os_vars_from_rcfile(filename)
 | 
			
		||||
        expect(response).to eq({})
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  before(:each) do
 | 
			
		||||
    class Puppet::Provider::Openstack::AuthTester
 | 
			
		||||
      @credentials =  Puppet::Provider::Openstack::CredentialsV2_0.new
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#request' do
 | 
			
		||||
    context 'with no valid credentials' do
 | 
			
		||||
      it 'fails to authenticate' do
 | 
			
		||||
        expect { klass.request('project', 'list', ['--long']) }.to raise_error(Puppet::Error::OpenstackAuthInputError, "Insufficient credentials to authenticate")
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with user credentials in env' do
 | 
			
		||||
      it 'is successful' do
 | 
			
		||||
        klass.expects(:get_os_vars_from_env)
 | 
			
		||||
             .returns({ 'OS_USERNAME' => 'test',
 | 
			
		||||
                        'OS_PASSWORD' => 'abc123',
 | 
			
		||||
                        'OS_PROJECT_NAME' => 'test',
 | 
			
		||||
                        'OS_AUTH_URL' => 'http://127.0.0.1:5000' })
 | 
			
		||||
        klass.expects(:openstack)
 | 
			
		||||
             .with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
 | 
			
		||||
             .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = klass.request('project', 'list', ['--long'])
 | 
			
		||||
        expect(response.first[:description]).to eq("Test tenant")
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with service token credentials in env' do
 | 
			
		||||
      it 'is successful' do
 | 
			
		||||
        klass.expects(:get_os_vars_from_env)
 | 
			
		||||
             .returns({ 'OS_TOKEN' => 'test',
 | 
			
		||||
                        'OS_URL' => 'http://127.0.0.1:5000' })
 | 
			
		||||
        klass.expects(:openstack)
 | 
			
		||||
             .with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
 | 
			
		||||
             .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = klass.request('project', 'list', ['--long'])
 | 
			
		||||
        expect(response.first[:description]).to eq("Test tenant")
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with a RC file containing user credentials' do
 | 
			
		||||
      it 'is successful' do
 | 
			
		||||
        mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_PROJECT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000'"
 | 
			
		||||
        File.expects(:exists?).with("#{ENV['HOME']}/openrc").returns(true)
 | 
			
		||||
        File.expects(:open).with("#{ENV['HOME']}/openrc").returns(StringIO.new(mock))
 | 
			
		||||
        klass.expects(:openstack)
 | 
			
		||||
             .with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
 | 
			
		||||
             .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = provider.class.request('project', 'list', ['--long'])
 | 
			
		||||
        expect(response.first[:description]).to eq("Test tenant")
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with a RC file containing service token credentials' do
 | 
			
		||||
      it 'is successful' do
 | 
			
		||||
        mock = "export OS_TOKEN='test'\nexport OS_URL='abc123'\n"
 | 
			
		||||
        File.expects(:exists?).with("#{ENV['HOME']}/openrc").returns(true)
 | 
			
		||||
        File.expects(:open).with("#{ENV['HOME']}/openrc").returns(StringIO.new(mock))
 | 
			
		||||
        klass.expects(:openstack)
 | 
			
		||||
             .with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
 | 
			
		||||
             .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = klass.request('project', 'list', ['--long'])
 | 
			
		||||
        expect(response.first[:description]).to eq("Test tenant")
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										63
									
								
								spec/unit/provider/openstack/credentials_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								spec/unit/provider/openstack/credentials_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
require 'puppet'
 | 
			
		||||
require 'spec_helper'
 | 
			
		||||
require 'puppet/provider/openstack'
 | 
			
		||||
require 'puppet/provider/openstack/credentials'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe Puppet::Provider::Openstack::Credentials do
 | 
			
		||||
 | 
			
		||||
  let(:creds) do
 | 
			
		||||
    creds = Puppet::Provider::Openstack::CredentialsV2_0.new
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#service_token_set?' do
 | 
			
		||||
    context "with service credentials" do
 | 
			
		||||
      it 'is successful' do
 | 
			
		||||
        creds.token = 'token'
 | 
			
		||||
        creds.url = 'url'
 | 
			
		||||
        expect(creds.service_token_set?).to be_truthy
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#password_set?' do
 | 
			
		||||
    context "with user credentials" do
 | 
			
		||||
      it 'is successful' do
 | 
			
		||||
        creds.auth_url = 'auth_url'
 | 
			
		||||
        creds.password = 'password'
 | 
			
		||||
        creds.project_name = 'project_name'
 | 
			
		||||
        creds.username = 'username'
 | 
			
		||||
        expect(creds.user_password_set?).to be_truthy
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#set?' do
 | 
			
		||||
    context "without any credential" do
 | 
			
		||||
      it 'fails' do
 | 
			
		||||
        expect(creds.set?).to be_falsey
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#to_env' do
 | 
			
		||||
    context "with an exhaustive data set" do
 | 
			
		||||
      it 'successfully returns content' do
 | 
			
		||||
        creds.auth_url = 'auth_url'
 | 
			
		||||
        creds.password = 'password'
 | 
			
		||||
        creds.project_name = 'project_name'
 | 
			
		||||
        creds.username = 'username'
 | 
			
		||||
        creds.token = 'token'
 | 
			
		||||
        creds.url = 'url'
 | 
			
		||||
        creds.identity_api_version = 'identity_api_version'
 | 
			
		||||
        expect(creds.auth_url).to eq("auth_url")
 | 
			
		||||
        expect(creds.password).to eq("password")
 | 
			
		||||
        expect(creds.project_name).to eq("project_name")
 | 
			
		||||
        expect(creds.username).to eq("username")
 | 
			
		||||
        expect(creds.token).to eq('token')
 | 
			
		||||
        expect(creds.url).to eq('url')
 | 
			
		||||
        expect(creds.identity_api_version).to eq('identity_api_version')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,12 +1,8 @@
 | 
			
		||||
# Load libraries from aviator here to simulate how they live together in a real puppet run
 | 
			
		||||
$LOAD_PATH.push(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'modules', 'aviator', 'lib'))
 | 
			
		||||
require 'puppet'
 | 
			
		||||
require 'spec_helper'
 | 
			
		||||
require 'puppet/provider/openstack'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe Puppet::Provider::Openstack do
 | 
			
		||||
 | 
			
		||||
  before(:each) do
 | 
			
		||||
    ENV['OS_USERNAME']     = nil
 | 
			
		||||
    ENV['OS_PASSWORD']     = nil
 | 
			
		||||
@@ -17,239 +13,48 @@ describe Puppet::Provider::Openstack do
 | 
			
		||||
  let(:type) do
 | 
			
		||||
    Puppet::Type.newtype(:test_resource) do
 | 
			
		||||
      newparam(:name, :namevar => true)
 | 
			
		||||
      newparam(:auth)
 | 
			
		||||
      newparam(:log_file)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  shared_examples 'authenticating with environment variables using API v2' do
 | 
			
		||||
    it 'makes a successful request' do
 | 
			
		||||
      ENV['OS_USERNAME']     = 'test'
 | 
			
		||||
      ENV['OS_PASSWORD']     = 'abc123'
 | 
			
		||||
      ENV['OS_PROJECT_NAME'] = 'test'
 | 
			
		||||
      ENV['OS_AUTH_URL']     = 'http://127.0.0.1:35357/v2.0'
 | 
			
		||||
      if provider.class == Class
 | 
			
		||||
        provider.stubs(:openstack)
 | 
			
		||||
                .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]])
 | 
			
		||||
                .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
      else
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                    .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]])
 | 
			
		||||
                    .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
      end
 | 
			
		||||
      response = provider.request('project', 'list', nil, nil, '--long' )
 | 
			
		||||
      expect(response.first[:description]).to match /Test tenant/
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  shared_examples 'authenticating with environment variables using API v3' do
 | 
			
		||||
    it 'makes a successful request' do
 | 
			
		||||
      ENV['OS_USERNAME']     = 'test'
 | 
			
		||||
      ENV['OS_PASSWORD']     = 'abc123'
 | 
			
		||||
      ENV['OS_PROJECT_NAME'] = 'test'
 | 
			
		||||
      ENV['OS_AUTH_URL']     = 'http://127.0.0.1:35357/v3'
 | 
			
		||||
      if provider.class == Class
 | 
			
		||||
        provider.stubs(:openstack)
 | 
			
		||||
                .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]])
 | 
			
		||||
                .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
      else
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                    .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]])
 | 
			
		||||
                    .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
      end
 | 
			
		||||
      response = provider.request('project', 'list', nil, nil, '--long' )
 | 
			
		||||
      expect(response.first[:description]).to match /Test tenant/
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  shared_examples 'it has no credentials' do
 | 
			
		||||
    it 'fails to authenticate' do
 | 
			
		||||
      expect{ provider.request('project', 'list', nil, nil, '--long') }.to raise_error(Puppet::Error::OpenstackAuthInputError, /No credentials provided/)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#request' do
 | 
			
		||||
    let(:resource_attrs) do
 | 
			
		||||
      {
 | 
			
		||||
        :name => 'stubresource',
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with valid password credentials in parameters' do
 | 
			
		||||
      let(:resource_attrs) do
 | 
			
		||||
        {
 | 
			
		||||
          :name         => 'stubresource',
 | 
			
		||||
          :auth         => {
 | 
			
		||||
            'username'     => 'test',
 | 
			
		||||
            'password'     => 'abc123',
 | 
			
		||||
            'project_name' => 'test',
 | 
			
		||||
            'auth_url'     => 'http://127.0.0.1:5000/v2.0',
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
      let(:provider) do
 | 
			
		||||
        Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
      end
 | 
			
		||||
    let(:provider) do
 | 
			
		||||
      Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
      it 'makes a successful request' do
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                      .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-project-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']])
 | 
			
		||||
                      .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
    it 'makes a successful request' do
 | 
			
		||||
      provider.class.stubs(:openstack)
 | 
			
		||||
                    .with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
 | 
			
		||||
                    .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = provider.request('project', 'list', nil, resource_attrs[:auth], '--long')
 | 
			
		||||
        expect(response.first[:description]).to match /Test tenant/
 | 
			
		||||
      end
 | 
			
		||||
      response = Puppet::Provider::Openstack.request('project', 'list', ['--long'])
 | 
			
		||||
      expect(response.first[:description]).to eq("Test tenant")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with valid openrc file in parameters' do
 | 
			
		||||
      mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_PROJECT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000/v2.0'"
 | 
			
		||||
      let(:resource_attrs) do
 | 
			
		||||
        {
 | 
			
		||||
          :name         => 'stubresource',
 | 
			
		||||
          :auth         => {
 | 
			
		||||
            'openrc' => '/root/openrc'
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
      let(:provider) do
 | 
			
		||||
        Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'makes a successful request' do
 | 
			
		||||
        File.expects(:open).with('/root/openrc').returns(StringIO.new(mock))
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                      .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-project-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']])
 | 
			
		||||
                      .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = provider.request('project', 'list', nil, resource_attrs[:auth], '--long')
 | 
			
		||||
        expect(response.first[:description]).to match /Test tenant/
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with valid service token in parameters' do
 | 
			
		||||
      let(:resource_attrs) do
 | 
			
		||||
        {
 | 
			
		||||
          :name         => 'stubresource',
 | 
			
		||||
          :auth         => {
 | 
			
		||||
            'token'   => 'secrettoken',
 | 
			
		||||
            'url'     => 'http://127.0.0.1:5000/v3',
 | 
			
		||||
            'version' => '3'
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
      let(:provider) do
 | 
			
		||||
        Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'makes a successful request' do
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                      .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-token', 'secrettoken', '--os-url', 'http://127.0.0.1:5000/v3', '--os-identity-api-version', '3']])
 | 
			
		||||
                      .returns('"ID","Name","Description","Enabled"
 | 
			
		||||
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
 | 
			
		||||
')
 | 
			
		||||
        response = provider.request('project', 'list', nil, resource_attrs[:auth], '--long')
 | 
			
		||||
        expect(response.first[:description]).to match /Test tenant/
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'makes a successful show request' do
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                      .with('project', 'show', '--format', 'shell', [['test', '--os-token', 'secrettoken', '--os-url', 'http://127.0.0.1:5000/v3', '--os-identity-api-version', '3']])
 | 
			
		||||
                      .returns('ID="1cb05cfed7c24279be884ba4f6520262"
 | 
			
		||||
Name="test"
 | 
			
		||||
Description="Test Tenant"
 | 
			
		||||
Enabled="True"
 | 
			
		||||
')
 | 
			
		||||
        response = provider.request('project', 'show', 'test', resource_attrs[:auth])
 | 
			
		||||
        expect(response[:description]).to match /Test Tenant/
 | 
			
		||||
        expect(response[:id]).to match /1cb05cfed7c24279be884ba4f6520262/
 | 
			
		||||
        expect(response[:name]).to match /test/
 | 
			
		||||
        expect(response[:enabled]).to match /True/
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with valid password credentials in environment variables' do
 | 
			
		||||
      it_behaves_like 'authenticating with environment variables using API v2' do
 | 
			
		||||
        let(:resource_attrs) do
 | 
			
		||||
          {
 | 
			
		||||
            :name => 'stubresource',
 | 
			
		||||
          }
 | 
			
		||||
        end
 | 
			
		||||
        let(:provider) do
 | 
			
		||||
          Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with no valid credentials' do
 | 
			
		||||
      it_behaves_like 'it has no credentials' do
 | 
			
		||||
        let(:resource_attrs) do
 | 
			
		||||
          {
 | 
			
		||||
            :name => 'stubresource',
 | 
			
		||||
          }
 | 
			
		||||
        end
 | 
			
		||||
        let(:provider) do
 | 
			
		||||
          Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'it retries on connection errors' do
 | 
			
		||||
      let(:resource_attrs) do
 | 
			
		||||
        {
 | 
			
		||||
          :name         => 'stubresource',
 | 
			
		||||
          :auth         => {
 | 
			
		||||
            'username'     => 'test',
 | 
			
		||||
            'password'     => 'abc123',
 | 
			
		||||
            'project_name' => 'test',
 | 
			
		||||
            'auth_url'     => 'http://127.0.0.1:5000/v2.0',
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
      let(:provider) do
 | 
			
		||||
        Puppet::Provider::Openstack.new(type.new(resource_attrs))
 | 
			
		||||
      end
 | 
			
		||||
    context 'on connection errors' do
 | 
			
		||||
      it 'retries' do
 | 
			
		||||
        ENV['OS_USERNAME']     = 'test'
 | 
			
		||||
        ENV['OS_PASSWORD']     = 'abc123'
 | 
			
		||||
        ENV['OS_PROJECT_NAME'] = 'test'
 | 
			
		||||
        ENV['OS_AUTH_URL']     = 'http://127.0.0.1:5000'
 | 
			
		||||
        provider.class.stubs(:openstack)
 | 
			
		||||
                      .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-project-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']])
 | 
			
		||||
                      .with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
 | 
			
		||||
                      .raises(Puppet::ExecutionFailure, 'Unable to establish connection')
 | 
			
		||||
                      .then
 | 
			
		||||
                      .returns('')
 | 
			
		||||
        provider.class.expects(:sleep).with(2).returns(nil)
 | 
			
		||||
        provider.request('project', 'list', nil, resource_attrs[:auth], '--long')
 | 
			
		||||
        Puppet::Provider::Openstack.request('project', 'list', ['--long'])
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  describe '::request' do
 | 
			
		||||
 | 
			
		||||
    context 'with valid password credentials in environment variables' do
 | 
			
		||||
      it_behaves_like 'authenticating with environment variables using API v2' do
 | 
			
		||||
        let(:resource_attrs) do
 | 
			
		||||
          {
 | 
			
		||||
            :name => 'stubresource',
 | 
			
		||||
          }
 | 
			
		||||
        end
 | 
			
		||||
        let(:provider) do
 | 
			
		||||
          Puppet::Provider::Openstack.dup
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'with no valid credentials' do
 | 
			
		||||
      it_behaves_like 'it has no credentials' do
 | 
			
		||||
        let(:provider) { Puppet::Provider::Openstack.dup }
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'parse_csv' do
 | 
			
		||||
    context 'with mixed stderr' do
 | 
			
		||||
      text = "ERROR: Testing\n\"field\",\"test\",1,2,3\n"
 | 
			
		||||
@@ -281,5 +86,4 @@ Enabled="True"
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user