Move openstackclient parent provider to openstacklib
This patch abandons the use of the Aviator library for interacting with the openstack APIs in favor of the universal openstack client[1]. This work has already been implemented in the keystone module. This patch moves that work into openstacklib in order to make it available for the other modules. [1] https://wiki.openstack.org/wiki/OpenStackClient Change-Id: I68705c28955a54e26d98f1de718016691c64e4b1
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
fixtures:
|
fixtures:
|
||||||
repositories:
|
repositories:
|
||||||
apache: git://github.com/puppetlabs/puppetlabs-apache.git
|
apache: git://github.com/puppetlabs/puppetlabs-apache.git
|
||||||
aviator: git://github.com/aimonb/puppet_aviator.git
|
|
||||||
concat: git://github.com/puppetlabs/puppetlabs-concat.git
|
concat: git://github.com/puppetlabs/puppetlabs-concat.git
|
||||||
mysql: git://github.com/puppetlabs/puppetlabs-mysql.git
|
mysql: git://github.com/puppetlabs/puppetlabs-mysql.git
|
||||||
postgresql: git://github.com/puppetlabs/puppetlabs-postgresql.git
|
postgresql: git://github.com/puppetlabs/puppetlabs-postgresql.git
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
*.swp
|
*.swp
|
||||||
spec/fixtures/*
|
spec/fixtures/*
|
||||||
!spec/fixtures/vcr/
|
|
||||||
pkg
|
pkg
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
|
|||||||
2
Gemfile
2
Gemfile
@@ -8,8 +8,6 @@ group :development, :test do
|
|||||||
gem 'rspec'
|
gem 'rspec'
|
||||||
gem 'mocha'
|
gem 'mocha'
|
||||||
gem 'json'
|
gem 'json'
|
||||||
gem 'faraday', '0.8.8', :require => false
|
|
||||||
gem 'vcr', :require => false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if puppetversion = ENV['PUPPET_GEM_VERSION']
|
if puppetversion = ENV['PUPPET_GEM_VERSION']
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
require 'puppet'
|
|
||||||
require 'puppet/feature/aviator'
|
|
||||||
require 'puppet/util/inifile'
|
|
||||||
|
|
||||||
class Puppet::Provider::Aviator < Puppet::Provider
|
|
||||||
|
|
||||||
def session
|
|
||||||
@session ||= authenticate(resource[:auth], resource[:log_file])
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.session
|
|
||||||
@session ||= authenticate(nil, nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
def request(service, request, &block)
|
|
||||||
self.class.make_request(service, request, session_data, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.request(service, request, &block)
|
|
||||||
self.make_request(service, request, session_data, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# needed for tests
|
|
||||||
def session_data
|
|
||||||
@session_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.session_data
|
|
||||||
@session_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def session_data=(data)
|
|
||||||
@session_data=data
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.session_data=(data)
|
|
||||||
@session_data=data
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Attempt to find credentials in this order:
|
|
||||||
# 1. username,password,tenant,host set in type parameters
|
|
||||||
# 2. openrc file path set in type parameters
|
|
||||||
# 3. service token and host set in type parameters
|
|
||||||
# 4. username,password,tenant,host set in environment variables
|
|
||||||
# 5. service token and host set in keystone.conf (backwards compatible version)
|
|
||||||
def authenticate(auth_params, log_file)
|
|
||||||
auth_params ||= {}
|
|
||||||
if password_credentials_set?(auth_params)
|
|
||||||
@session = get_authenticated_session(auth_params, log_file)
|
|
||||||
|
|
||||||
elsif openrc_set?(auth_params)
|
|
||||||
credentials = get_credentials_from_openrc(auth_params['openrc'])
|
|
||||||
@session = get_authenticated_session(credentials, log_file)
|
|
||||||
|
|
||||||
elsif service_credentials_set?(auth_params)
|
|
||||||
session_hash = get_unauthenticated_session(auth_params, log_file)
|
|
||||||
@session_data = session_hash[:data]
|
|
||||||
@session = session_hash[:session]
|
|
||||||
|
|
||||||
elsif env_vars_set?
|
|
||||||
credentials = get_credentials_from_env
|
|
||||||
@session = get_authenticated_session(credentials, log_file)
|
|
||||||
|
|
||||||
else # Last effort: try to get the token from keystone.conf
|
|
||||||
session_hash = self.class.try_auth_with_token(keystone_file, log_file)
|
|
||||||
@session_data = session_hash[:data]
|
|
||||||
@session = session_hash[:session]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.authenticate(auth_params, log_file)
|
|
||||||
auth_params = {} unless auth_params
|
|
||||||
if env_vars_set?
|
|
||||||
credentials = get_credentials_from_env
|
|
||||||
@session = get_authenticated_session(credentials, log_file)
|
|
||||||
|
|
||||||
else # Last effort: try to get the token from keystone.conf
|
|
||||||
session_hash = try_auth_with_token(keystone_file, log_file)
|
|
||||||
@session_data = session_hash[:data]
|
|
||||||
@session = session_hash[:session]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.try_auth_with_token(conf_file, log_file)
|
|
||||||
service_token = get_admin_token_from_keystone_file(conf_file)
|
|
||||||
auth_url = get_auth_url_from_keystone_file(conf_file)
|
|
||||||
session_hash = {}
|
|
||||||
if service_token
|
|
||||||
credentials = {
|
|
||||||
'service_token' => service_token,
|
|
||||||
'host_uri' => auth_url,
|
|
||||||
}
|
|
||||||
session_hash = get_unauthenticated_session(credentials, log_file)
|
|
||||||
else # All authentication efforts failed
|
|
||||||
raise(Puppet::Error, 'No credentials provided.')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.make_request(service, request, session_data, &block)
|
|
||||||
response = nil
|
|
||||||
if service && service.default_session_data
|
|
||||||
response = service.request(request, :endpoint_type => 'admin') do |params|
|
|
||||||
yield(params) if block
|
|
||||||
end
|
|
||||||
elsif session_data
|
|
||||||
response = service.request(request, :endpoint_type => 'admin',
|
|
||||||
:session_data => session_data) do |params|
|
|
||||||
yield(params) if block
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise(Puppet::Error, 'Cannot make a request with no session data.')
|
|
||||||
end
|
|
||||||
if response.body.hash['error']
|
|
||||||
raise(Puppet::Error, "Error making request: #{response.body.hash['error']['code']} #{response.body.hash['error']['title']}")
|
|
||||||
end
|
|
||||||
response
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def password_credentials_set?(auth_params)
|
|
||||||
auth_params['username'] && auth_params['password'] && auth_params['tenant_name'] && auth_params['host_uri']
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def openrc_set?(auth_params)
|
|
||||||
auth_params['openrc']
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def service_credentials_set?(auth_params)
|
|
||||||
auth_params['service_token'] && auth_params['host_uri']
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.env_vars_set?
|
|
||||||
ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['OS_TENANT_NAME'] && ENV['OS_AUTH_URL']
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def env_vars_set?
|
|
||||||
self.class.env_vars_set?
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def get_credentials_from_openrc(file)
|
|
||||||
creds = {}
|
|
||||||
begin
|
|
||||||
File.open(file).readlines.delete_if{|l| l=~ /^#/}.each do |line|
|
|
||||||
key, value = line.split('=')
|
|
||||||
key = key.split(' ').last
|
|
||||||
value = value.chomp.gsub(/'/, '')
|
|
||||||
creds[key] = value
|
|
||||||
end
|
|
||||||
return creds
|
|
||||||
rescue Exception => error
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.get_credentials_from_env
|
|
||||||
ENV.to_hash.dup.delete_if { |key, _| ! (key =~ /^OS/) } # Ruby 1.8.7
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_credentials_from_env
|
|
||||||
self.class.get_credentials_from_env
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.keystone_file
|
|
||||||
keystone_file = Puppet::Util::IniConfig::File.new
|
|
||||||
keystone_file.read('/etc/keystone/keystone.conf')
|
|
||||||
keystone_file
|
|
||||||
end
|
|
||||||
|
|
||||||
def keystone_file
|
|
||||||
return @keystone_file if @keystone_file
|
|
||||||
@keystone_file = Puppet::Util::IniConfig::File.new
|
|
||||||
@keystone_file.read('/etc/keystone/keystone.conf')
|
|
||||||
@keystone_file
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.get_admin_token_from_keystone_file(conf_file)
|
|
||||||
if conf_file and conf_file['DEFAULT'] and conf_file['DEFAULT']['admin_token']
|
|
||||||
return "#{conf_file['DEFAULT']['admin_token'].strip}"
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_admin_token_from_keystone_file
|
|
||||||
conf_file = keystone_file
|
|
||||||
self.class.get_admin_token_from_keystone_file(conf_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.get_auth_url_from_keystone_file(conf_file)
|
|
||||||
if conf_file
|
|
||||||
if conf_file['DEFAULT']
|
|
||||||
if conf_file['DEFAULT']['admin_endpoint']
|
|
||||||
auth_url = conf_file['DEFAULT']['admin_endpoint'].strip
|
|
||||||
return versioned_endpoint(auth_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
if conf_file['DEFAULT']['admin_port']
|
|
||||||
admin_port = conf_file['DEFAULT']['admin_port'].strip
|
|
||||||
else
|
|
||||||
admin_port = '35357'
|
|
||||||
end
|
|
||||||
|
|
||||||
if conf_file['DEFAULT']['admin_bind_host']
|
|
||||||
host = conf_file['DEFAULT']['admin_bind_host'].strip
|
|
||||||
if host == "0.0.0.0"
|
|
||||||
host = "127.0.0.1"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
host = "127.0.0.1"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if conf_file['ssl'] && conf_file['ssl']['enable'] && conf_file['ssl']['enable'].strip.downcase == 'true'
|
|
||||||
protocol = 'https'
|
|
||||||
else
|
|
||||||
protocol = 'http'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
"#{protocol}://#{host}:#{admin_port}/v2.0/"
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_auth_url_from_keystone_file
|
|
||||||
self.class.get_auth_url_from_keystone_file(keystone_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.make_configuration(credentials)
|
|
||||||
host_uri = versioned_endpoint(credentials['host_uri'] || credentials['OS_AUTH_URL'], credentials['api_version'])
|
|
||||||
{
|
|
||||||
:provider => 'openstack',
|
|
||||||
:auth_service => {
|
|
||||||
:name => 'identity',
|
|
||||||
:host_uri => host_uri,
|
|
||||||
:request => 'create_token',
|
|
||||||
:validator => 'list_tenants',
|
|
||||||
},
|
|
||||||
:auth_credentials => {
|
|
||||||
:username => credentials['username'] || credentials['OS_USERNAME'],
|
|
||||||
:password => credentials['password'] || credentials['OS_PASSWORD'],
|
|
||||||
:tenant_name => credentials['tenant_name'] || credentials['OS_TENANT_NAME']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.get_authenticated_session(credentials, log_file)
|
|
||||||
configuration = make_configuration(credentials)
|
|
||||||
session = ::Aviator::Session.new(:config => configuration, :log_file => log_file)
|
|
||||||
session.authenticate
|
|
||||||
session
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_authenticated_session(credentials, log_file)
|
|
||||||
self.class.get_authenticated_session(credentials, log_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.get_unauthenticated_session(credentials, log_file)
|
|
||||||
configuration = {
|
|
||||||
:provider => 'openstack',
|
|
||||||
}
|
|
||||||
session_data = {
|
|
||||||
:base_url => credentials['host_uri'],
|
|
||||||
:service_token => credentials['service_token']
|
|
||||||
}
|
|
||||||
session = ::Aviator::Session.new(:config => configuration, :log_file => log_file)
|
|
||||||
{ :session => session, :data => session_data }
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_unauthenticated_session(credentials, log_file)
|
|
||||||
self.class.get_unauthenticated_session(credentials, log_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.versioned_endpoint(endpoint, version = 'v2.0')
|
|
||||||
version = 'v2.0' if version.nil?
|
|
||||||
if endpoint =~ /\/#{version}\/?$/ || endpoint =~ /\/v2.0\/?$/ || endpoint =~ /\/v3\/?$/
|
|
||||||
endpoint
|
|
||||||
else
|
|
||||||
"#{endpoint.chomp('/')}/#{version}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
183
lib/puppet/provider/openstack.rb
Normal file
183
lib/puppet/provider/openstack.rb
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
require 'csv'
|
||||||
|
require 'puppet'
|
||||||
|
|
||||||
|
class Puppet::Error::OpenstackAuthInputError < Puppet::Error
|
||||||
|
end
|
||||||
|
|
||||||
|
class Puppet::Error::OpenstackUnauthorizedError < Puppet::Error
|
||||||
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
# Ignore warnings - assume legitimate output starts with a double quote
|
||||||
|
# Errors will be caught and raised prior to this
|
||||||
|
response = response.split("\n").select { |line| line =~ /^\".*\",\".*\"/ }.join("\n")
|
||||||
|
response = CSV.parse(response.to_s)
|
||||||
|
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', 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
|
||||||
|
end
|
||||||
|
sleep(2)
|
||||||
|
else
|
||||||
|
raise e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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['tenant_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['auth_url']
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.env_vars_set?
|
||||||
|
ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['OS_TENANT_NAME'] && ENV['OS_AUTH_URL']
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def env_vars_set?
|
||||||
|
self.class.env_vars_set?
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def self.password_auth_args(credentials)
|
||||||
|
['--os-username', credentials['username'],
|
||||||
|
'--os-password', credentials['password'],
|
||||||
|
'--os-tenant-name', credentials['tenant_name'],
|
||||||
|
'--os-auth-url', credentials['auth_url']]
|
||||||
|
end
|
||||||
|
|
||||||
|
def password_auth_args(credentials)
|
||||||
|
self.class.password_auth_args(credentials)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def self.token_auth_args(credentials)
|
||||||
|
['--os-token', credentials['token'],
|
||||||
|
'--os-url', credentials['auth_url']]
|
||||||
|
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
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Add the auth parameter to whatever type is given
|
# Add the auth parameter to whatever type is given
|
||||||
module Puppet::Util::Aviator
|
module Puppet::Util::Openstack
|
||||||
def self.add_aviator_params(type)
|
def self.add_openstack_type_methods(type, comment)
|
||||||
|
|
||||||
type.newparam(:auth) do
|
type.newparam(:auth) do
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ auth => {
|
|||||||
'username' => 'test',
|
'username' => 'test',
|
||||||
'password' => 'passw0rd',
|
'password' => 'passw0rd',
|
||||||
'tenant_name' => 'test',
|
'tenant_name' => 'test',
|
||||||
'host_uri' => 'http://localhost:35357/v2.0',
|
'auth_url' => 'http://localhost:35357/v2.0',
|
||||||
}
|
}
|
||||||
|
|
||||||
or a path to an openrc file containing these credentials, e.g.:
|
or a path to an openrc file containing these credentials, e.g.:
|
||||||
@@ -25,12 +25,13 @@ or a service token and host, e.g.:
|
|||||||
|
|
||||||
auth => {
|
auth => {
|
||||||
'service_token' => 'ADMIN',
|
'service_token' => 'ADMIN',
|
||||||
'host_uri' => 'http://localhost:35357/v2.0',
|
'auth_url' => 'http://localhost:35357/v2.0',
|
||||||
}
|
}
|
||||||
|
|
||||||
If not present, the provider will first look for environment variables
|
If not present, the provider will look for environment variables for
|
||||||
for password credentials and then to /etc/keystone/keystone.conf for a
|
password credentials.
|
||||||
service token.
|
|
||||||
|
#{comment}
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
validate do |value|
|
validate do |value|
|
||||||
@@ -38,9 +39,9 @@ EOT
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
type.newparam(:log_file) do
|
type.autorequire(:package) do
|
||||||
desc 'Log file. Defaults to no logging.'
|
'python-openstackclient'
|
||||||
defaultto('/dev/null')
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
18
manifests/openstackclient.pp
Normal file
18
manifests/openstackclient.pp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# == Class: openstacklib::openstackclient
|
||||||
|
#
|
||||||
|
# Installs the openstackclient
|
||||||
|
#
|
||||||
|
# == Parameters
|
||||||
|
#
|
||||||
|
# [package_ensure]
|
||||||
|
# Ensure state of the openstackclient package.
|
||||||
|
# Optional. Defaults to 'present'.
|
||||||
|
#
|
||||||
|
class openstacklib::openstackclient(
|
||||||
|
$package_ensure = 'present',
|
||||||
|
){
|
||||||
|
package { 'python-openstackclient':
|
||||||
|
ensure => $package_ensure,
|
||||||
|
tag => 'openstack',
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,6 @@
|
|||||||
],
|
],
|
||||||
"description": "Puppet module library to expose common functionality between OpenStack modules.",
|
"description": "Puppet module library to expose common functionality between OpenStack modules.",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
{ "name": "aimonb/aviator", "version_requirement": ">=0.4.2 <1.0.0" },
|
|
||||||
{ "name": "puppetlabs/apache", "version_requirement": ">=1.0.0 <2.0.0" },
|
{ "name": "puppetlabs/apache", "version_requirement": ">=1.0.0 <2.0.0" },
|
||||||
{ "name": "puppetlabs/mysql", "version_requirement": ">=3.0.0 <4.0.0" },
|
{ "name": "puppetlabs/mysql", "version_requirement": ">=3.0.0 <4.0.0" },
|
||||||
{ "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" },
|
{ "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" },
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
http_interactions:
|
|
||||||
- request:
|
|
||||||
method: get
|
|
||||||
uri: "http://192.168.11.4:35357/v2.0/tenants"
|
|
||||||
body:
|
|
||||||
encoding: US-ASCII
|
|
||||||
string: ""
|
|
||||||
headers:
|
|
||||||
Content-Type:
|
|
||||||
- application/json
|
|
||||||
User-Agent:
|
|
||||||
- "Faraday v0.8.8"
|
|
||||||
X-Auth-Token:
|
|
||||||
- sosp-kyl
|
|
||||||
response:
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message:
|
|
||||||
headers:
|
|
||||||
vary:
|
|
||||||
- X-Auth-Token
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
content-length:
|
|
||||||
- "491"
|
|
||||||
date:
|
|
||||||
- "Tue, 30 Sep 2014 06:59:48 GMT"
|
|
||||||
connection:
|
|
||||||
- close
|
|
||||||
body:
|
|
||||||
encoding: UTF-8
|
|
||||||
string: "{\x22tenants_links\x22: [], \x22tenants\x22: [{\x22description\x22: \x22Test tenant\x22, \x22enabled\x22: true, \x22id\x22: \x2234e463e2bab24f78990ca864e4a28ba2\x22, \x22name\x22: \x22test2\x22}, {\x22description\x22: \x22Tenant for the openstack services\x22, \x22enabled\x22: true, \x22id\x22: \x2268c8fcf77aff4b409cc158c0f6cbff7b\x22, \x22name\x22: \x22services\x22}, {\x22description\x22: \x22Test tenant\x22, \x22enabled\x22: true, \x22id\x22: \x22c330f1bc663648df9c1e7835a1e7a955\x22, \x22name\x22: \x22test\x22}, {\x22description\x22: \x22admin tenant\x22, \x22enabled\x22: true, \x22id\x22: \x22c518b36fa220499b85ba9a71014ce2a5\x22, \x22name\x22: \x22admin\x22}]}"
|
|
||||||
http_version:
|
|
||||||
recorded_at: "Tue, 30 Sep 2014 06:59:48 GMT"
|
|
||||||
recorded_with: "VCR 2.9.3"
|
|
||||||
File diff suppressed because one or more lines are too long
36
spec/fixtures/vcr/aviator/session/with_token.yml
vendored
36
spec/fixtures/vcr/aviator/session/with_token.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
http_interactions:
|
|
||||||
- request:
|
|
||||||
method: get
|
|
||||||
uri: "http://192.168.11.4:35357/v2.0/tenants"
|
|
||||||
body:
|
|
||||||
encoding: US-ASCII
|
|
||||||
string: ""
|
|
||||||
headers:
|
|
||||||
Content-Type:
|
|
||||||
- application/json
|
|
||||||
User-Agent:
|
|
||||||
- "Faraday v0.8.8"
|
|
||||||
X-Auth-Token:
|
|
||||||
- sosp-kyl
|
|
||||||
response:
|
|
||||||
status:
|
|
||||||
code: 200
|
|
||||||
message:
|
|
||||||
headers:
|
|
||||||
vary:
|
|
||||||
- X-Auth-Token
|
|
||||||
content-type:
|
|
||||||
- application/json
|
|
||||||
content-length:
|
|
||||||
- "491"
|
|
||||||
date:
|
|
||||||
- "Tue, 30 Sep 2014 06:59:48 GMT"
|
|
||||||
connection:
|
|
||||||
- close
|
|
||||||
body:
|
|
||||||
encoding: UTF-8
|
|
||||||
string: "{\x22tenants_links\x22: [], \x22tenants\x22: [{\x22description\x22: \x22Test tenant\x22, \x22enabled\x22: true, \x22id\x22: \x2234e463e2bab24f78990ca864e4a28ba2\x22, \x22name\x22: \x22test2\x22}, {\x22description\x22: \x22Tenant for the openstack services\x22, \x22enabled\x22: true, \x22id\x22: \x2268c8fcf77aff4b409cc158c0f6cbff7b\x22, \x22name\x22: \x22services\x22}, {\x22description\x22: \x22Test tenant\x22, \x22enabled\x22: true, \x22id\x22: \x22c330f1bc663648df9c1e7835a1e7a955\x22, \x22name\x22: \x22test\x22}, {\x22description\x22: \x22admin tenant\x22, \x22enabled\x22: true, \x22id\x22: \x22c518b36fa220499b85ba9a71014ce2a5\x22, \x22name\x22: \x22admin\x22}]}"
|
|
||||||
http_version:
|
|
||||||
recorded_at: "Tue, 30 Sep 2014 06:59:48 GMT"
|
|
||||||
recorded_with: "VCR 2.9.3"
|
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
require 'puppetlabs_spec_helper/module_spec_helper'
|
require 'puppetlabs_spec_helper/module_spec_helper'
|
||||||
require 'shared_examples'
|
require 'shared_examples'
|
||||||
require 'vcr'
|
|
||||||
|
|
||||||
|
|
||||||
RSpec.configure do |c|
|
RSpec.configure do |c|
|
||||||
c.alias_it_should_behave_like_to :it_configures, 'configures'
|
c.alias_it_should_behave_like_to :it_configures, 'configures'
|
||||||
c.alias_it_should_behave_like_to :it_raises, 'raises'
|
c.alias_it_should_behave_like_to :it_raises, 'raises'
|
||||||
end
|
end
|
||||||
|
|
||||||
VCR.configure do |c|
|
|
||||||
c.cassette_library_dir = 'spec/fixtures/vcr'
|
|
||||||
c.hook_into :faraday
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,320 +0,0 @@
|
|||||||
# 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 'vcr'
|
|
||||||
require 'spec_helper'
|
|
||||||
require 'puppet/provider/aviator'
|
|
||||||
|
|
||||||
|
|
||||||
describe Puppet::Provider::Aviator do
|
|
||||||
|
|
||||||
before(:each) do
|
|
||||||
ENV['OS_USERNAME'] = nil
|
|
||||||
ENV['OS_PASSWORD'] = nil
|
|
||||||
ENV['OS_TENANT_NAME'] = nil
|
|
||||||
ENV['OS_AUTH_URL'] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:log_file) { '/tmp/aviator_spec.log' }
|
|
||||||
|
|
||||||
let(:type) do
|
|
||||||
Puppet::Type.newtype(:test_resource) do
|
|
||||||
newparam(:name, :namevar => true)
|
|
||||||
newparam(:auth)
|
|
||||||
newparam(:log_file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
shared_examples 'creating a session using environment variables' do
|
|
||||||
it 'creates an authenticated session' do
|
|
||||||
ENV['OS_USERNAME'] = 'admin'
|
|
||||||
ENV['OS_PASSWORD'] = 'fyby-tet'
|
|
||||||
ENV['OS_TENANT_NAME'] = 'admin'
|
|
||||||
ENV['OS_AUTH_URL'] = 'http://192.168.11.4:35357/v2.0'
|
|
||||||
response = nil
|
|
||||||
VCR.use_cassette('aviator/session/with_password') do
|
|
||||||
session = provider.session
|
|
||||||
response = session.identity_service.request(:list_tenants, :session_data => provider.session_data)
|
|
||||||
end
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'creating a session using a service token from keystone.conf' do
|
|
||||||
it 'creates an unauthenticated session' do
|
|
||||||
data = "[DEFAULT]\nadmin_token=sosp-kyl\nadmin_endpoint=http://192.168.11.4:35357/v2.0"
|
|
||||||
response = nil
|
|
||||||
VCR.use_cassette('aviator/session/with_token') do
|
|
||||||
# Stubbing File.read produces inconsistent results because of how IniConfig
|
|
||||||
# overrides the File class in some versions of Puppet.
|
|
||||||
# Stubbing FileType.filetype(:flat) simplifies working with IniConfig
|
|
||||||
Puppet::Util::FileType.filetype(:flat).any_instance.expects(:read).returns(StringIO.new(data).read)
|
|
||||||
session = provider.session
|
|
||||||
Puppet::Util::FileType.filetype(:flat).any_instance.unstub(:read)
|
|
||||||
response = session.identity_service.request(:list_tenants, :session_data => provider.session_data)
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'it has no credentials' do
|
|
||||||
it 'fails to authenticate' do
|
|
||||||
expect{ provider.session }.to raise_error(Puppet::Error, /No credentials provided/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'making request with an existing session' do
|
|
||||||
it 'makes a successful request' do
|
|
||||||
VCR.use_cassette('aviator/request/with_session') do
|
|
||||||
session = provider.session
|
|
||||||
response = provider.request(session.identity_service, :list_tenants)
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'making request with injected session data' do
|
|
||||||
it 'makes a successful request' do
|
|
||||||
VCR.use_cassette('aviator/request/without_session') do
|
|
||||||
session = provider.session
|
|
||||||
response = provider.request(session.identity_service, :list_tenants)
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'making request with no session or session data' do
|
|
||||||
it 'fails to make a request' do
|
|
||||||
expect{ provider.request(nil, :list_tenants) }.to raise_error(Puppet::Error, /Cannot make a request/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#session' do
|
|
||||||
|
|
||||||
context 'with valid password credentials in parameters' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
:auth => {
|
|
||||||
'username' => 'admin',
|
|
||||||
'password' => 'fyby-tet',
|
|
||||||
'tenant_name' => 'admin',
|
|
||||||
'host_uri' => 'http://192.168.11.4:35357/v2.0',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a session' do
|
|
||||||
provider = Puppet::Provider::Aviator.new(type.new(resource_attrs))
|
|
||||||
response = nil
|
|
||||||
VCR.use_cassette('aviator/session/with_password') do
|
|
||||||
session = provider.session
|
|
||||||
response = session.identity_service.request(:list_tenants)
|
|
||||||
end
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with valid openrc file in parameters' do
|
|
||||||
data = "export OS_USERNAME='admin'\nexport OS_PASSWORD='fyby-tet'\nexport OS_TENANT_NAME='admin'\nexport OS_AUTH_URL='http://192.168.11.4:35357/v2.0'"
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
:auth => {
|
|
||||||
'openrc' => '/root/openrc'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a session' do
|
|
||||||
provider = Puppet::Provider::Aviator.new(type.new(resource_attrs))
|
|
||||||
response = nil
|
|
||||||
VCR.use_cassette('aviator/session/with_password') do
|
|
||||||
File.expects(:open).with('/root/openrc').returns(StringIO.new(data))
|
|
||||||
session = provider.session
|
|
||||||
File.unstub(:open) # Ignore File.open calls to cassette file
|
|
||||||
response = session.identity_service.request(:list_tenants)
|
|
||||||
end
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with valid service token in parameters' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
:auth => {
|
|
||||||
'service_token' => 'sosp-kyl',
|
|
||||||
'host_uri' => 'http://192.168.11.4:35357/v2.0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
subject(:session) do
|
|
||||||
provider = Puppet::Provider::Aviator.new(type.new(resource_attrs))
|
|
||||||
VCR.use_cassette('aviator/session/with_token') do
|
|
||||||
session = provider.session
|
|
||||||
response = session.identity_service.request(:list_tenants, :session_data => provider.session_data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a session' do
|
|
||||||
expect(session.status).to eq(200)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with valid password credentials in environment variables' do
|
|
||||||
it_behaves_like 'creating a session using environment variables' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:provider) do
|
|
||||||
Puppet::Provider::Aviator.new(type.new(resource_attrs))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with valid service token in keystone.conf' do
|
|
||||||
it_behaves_like 'creating a session using a service token from keystone.conf' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:provider) do
|
|
||||||
Puppet::Provider::Aviator.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) { Puppet::Provider::Aviator.new(type.new(resource_attrs)) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
describe '::session' do
|
|
||||||
|
|
||||||
context 'with valid password credentials in environment variables' do
|
|
||||||
it_behaves_like 'creating a session using environment variables' do
|
|
||||||
let(:provider) { Puppet::Provider::Aviator.dup }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with valid service token in keystone.conf' do
|
|
||||||
it_behaves_like 'creating a session using a service token from keystone.conf' do
|
|
||||||
let(:provider) { Puppet::Provider::Aviator.dup }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with no valid credentials' do
|
|
||||||
it_behaves_like 'it has no credentials' do
|
|
||||||
let(:provider) { Puppet::Provider::Aviator.dup }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#request' do
|
|
||||||
context 'when a session exists' do
|
|
||||||
it_behaves_like 'making request with an existing session' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
:auth => {
|
|
||||||
'username' => 'admin',
|
|
||||||
'password' => 'fyby-tet',
|
|
||||||
'tenant_name' => 'admin',
|
|
||||||
'host_uri' => 'http://192.168.11.4:35357/v2.0',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let (:provider) { Puppet::Provider::Aviator.new(type.new(resource_attrs)) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when injecting session data' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
:auth => {
|
|
||||||
'service_token' => 'sosp-kyl',
|
|
||||||
'host_uri' => 'http://192.168.11.4:35357/v2.0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:provider) { Puppet::Provider::Aviator.new(type.new(resource_attrs)) }
|
|
||||||
it 'makes a successful request' do
|
|
||||||
provider = Puppet::Provider::Aviator.new(type.new(resource_attrs))
|
|
||||||
VCR.use_cassette('aviator/request/without_session') do
|
|
||||||
session = provider.session
|
|
||||||
response = provider.request(session.identity_service, :list_tenants)
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there is no session or session data' do
|
|
||||||
it_behaves_like 'making request with no session or session data' do
|
|
||||||
let(:resource_attrs) do
|
|
||||||
{
|
|
||||||
:name => 'stubresource',
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:provider) {Puppet::Provider::Aviator.new(type.new(resource_attrs)) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '::request' do
|
|
||||||
context 'when a session exists' do
|
|
||||||
|
|
||||||
it_behaves_like 'making request with an existing session' do
|
|
||||||
let(:provider) { provider = Puppet::Provider::Aviator.dup }
|
|
||||||
before(:each) do
|
|
||||||
ENV['OS_USERNAME'] = 'admin'
|
|
||||||
ENV['OS_PASSWORD'] = 'fyby-tet'
|
|
||||||
ENV['OS_TENANT_NAME'] = 'admin'
|
|
||||||
ENV['OS_AUTH_URL'] = 'http://192.168.11.4:35357/v2.0'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when injecting session data' do
|
|
||||||
let(:session_data) do
|
|
||||||
{
|
|
||||||
:base_url => 'http://192.168.11.4:35357/v2.0',
|
|
||||||
:service_token => 'sosp-kyl'
|
|
||||||
}
|
|
||||||
end
|
|
||||||
it 'makes a successful request' do
|
|
||||||
provider = Puppet::Provider::Aviator.dup
|
|
||||||
VCR.use_cassette('aviator/request/without_session') do
|
|
||||||
session = ::Aviator::Session.new(:config => { :provider => 'openstack' }, :log_file => log_file)
|
|
||||||
provider.session_data = session_data
|
|
||||||
response = provider.request(session.identity_service, :list_tenants)
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there is no session or session data' do
|
|
||||||
it_behaves_like 'making request with no session or session data' do
|
|
||||||
let(:provider) { Puppet::Provider::Aviator.dup }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
228
spec/unit/provider/openstack_spec.rb
Normal file
228
spec/unit/provider/openstack_spec.rb
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# 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
|
||||||
|
ENV['OS_TENANT_NAME'] = nil
|
||||||
|
ENV['OS_AUTH_URL'] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
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' do
|
||||||
|
it 'makes a successful request' do
|
||||||
|
ENV['OS_USERNAME'] = 'test'
|
||||||
|
ENV['OS_PASSWORD'] = 'abc123'
|
||||||
|
ENV['OS_TENANT_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 '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
|
||||||
|
|
||||||
|
context 'with valid password credentials in parameters' do
|
||||||
|
let(:resource_attrs) do
|
||||||
|
{
|
||||||
|
:name => 'stubresource',
|
||||||
|
:auth => {
|
||||||
|
'username' => 'test',
|
||||||
|
'password' => 'abc123',
|
||||||
|
'tenant_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
|
||||||
|
|
||||||
|
it 'makes a successful request' do
|
||||||
|
provider.class.stubs(:openstack)
|
||||||
|
.with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-tenant-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 openrc file in parameters' do
|
||||||
|
mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_TENANT_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-tenant-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',
|
||||||
|
'auth_url' => 'http://127.0.0.1:5000/v2.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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/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
|
||||||
|
|
||||||
|
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/v2.0']])
|
||||||
|
.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' 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',
|
||||||
|
'tenant_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
|
||||||
|
it 'retries' do
|
||||||
|
provider.class.stubs(:openstack)
|
||||||
|
.with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-tenant-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']])
|
||||||
|
.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')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
describe '::request' do
|
||||||
|
|
||||||
|
context 'with valid password credentials in environment variables' do
|
||||||
|
it_behaves_like 'authenticating with environment variables' 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
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user