Add composite namevar for tenant, user, user_role.
There are two sides on this patch, the user facing one, and the developer's one. It gives more flexibility for the interface used by the user for the Keystone_tenant, Keystone_user and Keystone_user_roles resources. For instance to specify a user and give the admin role, currently you have to: keystone_user { 'new_admin::admin_domain': ensure => present, enabled => true, tenant => 'openstackv3::admin_domain', email => 'test@example.tld', password => 'a_big_secret', } keystone_user_role { 'new_admin::admin_domain@openstackv3::admin_domain': ensure => present, roles => ['admin'], } Now you can specify it like this: keystone_user { 'new_admin': ensure => present, enabled => true, domain => 'admin_domain', tenant => 'openstackv3::admin_domain', email => 'test@example.tld', password => 'a_big_secret', } keystone_user_role { 'new_admin@openstackv3': ensure => present, user_domain => 'admin_domain', project_domain => 'admin_domain', roles => ['admin'], } For the developer this simplify the code. Puppet is using composite namevar to make all the resources unique. So guessing what pattern is used in the title is no longer required. For instance this : keystone_tenant { 'project_one': ensure => present } keystone_tenant { 'meaningless': name => 'project_one', domain => 'Default', ensure => present } is detected as the same tenant by puppet. The same is true for dependencies. This is working correctly: keystone_tenant { 'meaningless': name => 'project_one', domain => 'domain_one', ensure => present } file {'/tmp/needed': ensure => present, require => Keystone_tenant['project_one::domain_one'] } In autorequire term in type definition, you just have to pass the fully qualified name (with the domain suffix for user and tenant) of the resource and puppet will do the matching, whatever the original title is. See the examples in user and tenant in keystone_user_role type. Change-Id: I4deb27dc6f71fb7a7ec6a9c72bd0e1412c2e9a30
This commit is contained in:
parent
b195a13918
commit
74799f9e34
@ -11,6 +11,7 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
INI_FILENAME = '/etc/keystone/keystone.conf'
|
||||
|
||||
@@default_domain_id = nil
|
||||
@@default_domain = nil
|
||||
|
||||
def self.admin_endpoint
|
||||
@admin_endpoint ||= get_admin_endpoint
|
||||
@ -32,10 +33,6 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
end
|
||||
end
|
||||
|
||||
def self.default_domain
|
||||
domain_name_from_id(default_domain_id)
|
||||
end
|
||||
|
||||
def self.default_domain_id
|
||||
if @@default_domain_id
|
||||
@@default_domain_id
|
||||
@ -50,18 +47,40 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
@@default_domain_id = id
|
||||
end
|
||||
|
||||
def self.default_domain_set?
|
||||
true unless default_domain_id == 'default'
|
||||
def self.resource_to_name(domain, name, check_for_default=true)
|
||||
raise Puppet::Error, "Domain cannot be nil for project '#{name}'. " \
|
||||
'Please report a bug.' if domain.nil?
|
||||
join_str = '::'
|
||||
name_display = [name]
|
||||
unless check_for_default && domain == default_domain
|
||||
name_display << domain
|
||||
end
|
||||
name_display.join(join_str)
|
||||
end
|
||||
|
||||
def self.domain_check(name, domain)
|
||||
# Ongoing deprecation warning ending after Mitaka
|
||||
# http://specs.openstack.org/openstack/puppet-openstack-specs/specs/kilo/api-v3-support.html
|
||||
if (domain.nil? || domain.empty?) && default_domain_set?
|
||||
warning('In Liberty, not providing a domain name (::domain) for a ' \
|
||||
"resource name (#{name}) is deprecated when the default_domain_id is " \
|
||||
"not 'default'")
|
||||
def self.name_to_resource(name)
|
||||
uniq = name.split('::')
|
||||
if uniq.count == 1
|
||||
uniq.insert(0, default_domain)
|
||||
else
|
||||
uniq.reverse!
|
||||
end
|
||||
uniq
|
||||
end
|
||||
|
||||
# Prefix with default domain if missing from the name.
|
||||
def self.make_full_name(name)
|
||||
resource_to_name(*name_to_resource(name), false)
|
||||
end
|
||||
|
||||
def self.default_domain
|
||||
# Default domain class variable is filled in by
|
||||
# user/tenant/user_role type or domain resource.
|
||||
@@default_domain
|
||||
end
|
||||
|
||||
def self.default_domain=(value)
|
||||
@@default_domain = value
|
||||
end
|
||||
|
||||
def self.domain_name_from_id(id)
|
||||
@ -160,31 +179,6 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
end
|
||||
end
|
||||
|
||||
# use the domain in this order:
|
||||
# 1 - the domain name specified in the resource definition - resource[:domain]
|
||||
# 2 - the domain name part of the resource name/title e.g. user_name::user_domain
|
||||
# if passed in by name_and_domain above
|
||||
# 3 - use the specified default_domain_name
|
||||
# 4 - lookup the default domain
|
||||
# 5 - use 'Default' - the "default" default domain if no other one is configured
|
||||
# Usage: name_and_domain(resource[:name], resource[:domain], default_domain_name)
|
||||
def self.name_and_domain(namedomstr, domain_from_resource=nil, default_domain_name=nil)
|
||||
name, domain = Util.split_domain(namedomstr)
|
||||
ret = [name]
|
||||
if domain_from_resource
|
||||
ret << domain_from_resource
|
||||
elsif domain
|
||||
ret << domain
|
||||
elsif default_domain_name
|
||||
ret << default_domain_name
|
||||
elsif default_domain
|
||||
ret << default_domain
|
||||
else
|
||||
ret << 'Default'
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
def self.request(service, action, properties=nil)
|
||||
super
|
||||
rescue Puppet::Error::OpenstackAuthInputError => error
|
||||
|
@ -9,15 +9,15 @@ Puppet::Type.type(:keystone_tenant).provide(
|
||||
|
||||
@credentials = Puppet::Provider::Openstack::CredentialsV3.new
|
||||
|
||||
include PuppetX::Keystone::CompositeNamevar::Helpers
|
||||
|
||||
def initialize(value={})
|
||||
super(value)
|
||||
@property_flush = {}
|
||||
end
|
||||
|
||||
def create
|
||||
# see if resource[:domain], or project_name::project_domain
|
||||
project_name, project_domain = self.class.name_and_domain(resource[:name], resource[:domain])
|
||||
properties = [project_name]
|
||||
properties = [resource[:name]]
|
||||
if resource[:enabled] == :true
|
||||
properties << '--enable'
|
||||
elsif resource[:enabled] == :false
|
||||
@ -27,14 +27,22 @@ Puppet::Type.type(:keystone_tenant).provide(
|
||||
properties << '--description'
|
||||
properties << resource[:description]
|
||||
end
|
||||
if project_domain
|
||||
properties << '--domain'
|
||||
properties << project_domain
|
||||
end
|
||||
properties << resource[:domain]
|
||||
|
||||
@property_hash = self.class.request('project', 'create', properties)
|
||||
@property_hash[:name] = resource[:name]
|
||||
@property_hash[:domain] = resource[:domain]
|
||||
@property_hash[:ensure] = :present
|
||||
rescue Puppet::ExecutionFailure => e
|
||||
if e.message =~ /No domain with a name or ID of/
|
||||
raise(Puppet::Error, "No project #{resource[:name]} with domain #{resource[:domain]} found")
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
mk_resource_methods
|
||||
|
||||
def exists?
|
||||
@property_hash[:ensure] == :present
|
||||
@ -57,25 +65,12 @@ Puppet::Type.type(:keystone_tenant).provide(
|
||||
@property_flush[:description] = value
|
||||
end
|
||||
|
||||
def description
|
||||
@property_hash[:description]
|
||||
end
|
||||
|
||||
def domain
|
||||
@property_hash[:domain]
|
||||
end
|
||||
|
||||
def id
|
||||
@property_hash[:id]
|
||||
end
|
||||
|
||||
def self.instances
|
||||
projects = request('project', 'list', '--long')
|
||||
projects.collect do |project|
|
||||
domain_name = domain_name_from_id(project[:domain_id])
|
||||
project_name = set_domain_for_name(project[:name], domain_name)
|
||||
new(
|
||||
:name => project_name,
|
||||
:name => resource_to_name(domain_name, project[:name]),
|
||||
:ensure => :present,
|
||||
:enabled => project[:enabled].downcase.chomp == 'true' ? true : false,
|
||||
:description => project[:description],
|
||||
@ -87,20 +82,10 @@ Puppet::Type.type(:keystone_tenant).provide(
|
||||
end
|
||||
|
||||
def self.prefetch(resources)
|
||||
project_hash = {}
|
||||
projects = instances
|
||||
resources.each do |resname, resource|
|
||||
# resname may be specified as just "name" or "name::domain"
|
||||
name, resdomain = name_and_domain(resname, resource[:domain])
|
||||
provider = projects.find do |project|
|
||||
# have a match if the full instance name matches the full resource name, OR
|
||||
# the base resource name matches the base instance name, and the
|
||||
# resource domain matches the instance domain
|
||||
project_name, project_domain = name_and_domain(project.name, project.domain)
|
||||
(project.name == resname) ||
|
||||
((project_name == name) && (project_domain == resdomain))
|
||||
end
|
||||
resource.provider = provider if provider
|
||||
prefetch_composite(resources) do |sorted_namevars|
|
||||
domain = sorted_namevars[0]
|
||||
name = sorted_namevars[1]
|
||||
resource_to_name(domain, name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -9,14 +9,15 @@ Puppet::Type.type(:keystone_user).provide(
|
||||
|
||||
@credentials = Puppet::Provider::Openstack::CredentialsV3.new
|
||||
|
||||
include PuppetX::Keystone::CompositeNamevar::Helpers
|
||||
|
||||
def initialize(value={})
|
||||
super(value)
|
||||
@property_flush = {}
|
||||
end
|
||||
|
||||
def create
|
||||
# see if resource[:domain], or user specified as user::domain
|
||||
user_name, user_domain = self.class.name_and_domain(resource[:name], resource[:domain])
|
||||
user_name, user_domain = resource[:name], resource[:domain]
|
||||
properties = [user_name]
|
||||
if resource[:enabled] == :true
|
||||
properties << '--enable'
|
||||
@ -54,13 +55,15 @@ Puppet::Type.type(:keystone_user).provide(
|
||||
options << '--email' << resource[:email] if @property_flush[:email]
|
||||
# project handled in tenant= separately
|
||||
unless options.empty?
|
||||
options << @property_hash[:id]
|
||||
options << id
|
||||
self.class.request('user', 'set', options)
|
||||
end
|
||||
@property_flush.clear
|
||||
end
|
||||
end
|
||||
|
||||
mk_resource_methods
|
||||
|
||||
def exists?
|
||||
@property_hash[:ensure] == :present
|
||||
end
|
||||
@ -74,18 +77,10 @@ Puppet::Type.type(:keystone_user).provide(
|
||||
@property_flush[:enabled] = value
|
||||
end
|
||||
|
||||
def email
|
||||
@property_hash[:email]
|
||||
end
|
||||
|
||||
def email=(value)
|
||||
@property_flush[:email] = value
|
||||
end
|
||||
|
||||
def id
|
||||
@property_hash[:id]
|
||||
end
|
||||
|
||||
def password
|
||||
passwd = nil
|
||||
return passwd if resource[:password] == nil
|
||||
@ -104,7 +99,7 @@ Puppet::Type.type(:keystone_user).provide(
|
||||
# NOTE: The only reason we use username is so that the openstack provider
|
||||
# will know we are doing v3password auth - otherwise, it is not used. The
|
||||
# user_id uniquely identifies the user including domain.
|
||||
credentials.username, unused = self.class.name_and_domain(resource[:name], domain)
|
||||
credentials.username = resource[:name]
|
||||
# Need to specify a project id to get a project scoped token. List
|
||||
# all of the projects for the user, and use the id from the first one.
|
||||
projects = self.class.request('project', 'list', ['--user', id, '--long'])
|
||||
@ -149,9 +144,8 @@ Puppet::Type.type(:keystone_user).provide(
|
||||
users = request('user', 'list', ['--long'])
|
||||
users.collect do |user|
|
||||
domain_name = domain_name_from_id(user[:domain])
|
||||
user_name = set_domain_for_name(user[:name], domain_name)
|
||||
new(
|
||||
:name => user_name,
|
||||
:name => resource_to_name(domain_name, user[:name]),
|
||||
:ensure => :present,
|
||||
:enabled => user[:enabled].downcase.chomp == 'true' ? true : false,
|
||||
:password => user[:password],
|
||||
@ -165,19 +159,10 @@ Puppet::Type.type(:keystone_user).provide(
|
||||
end
|
||||
|
||||
def self.prefetch(resources)
|
||||
users = instances
|
||||
resources.each do |resname, resource|
|
||||
# resname may be specified as just "name" or "name::domain"
|
||||
name, resdomain = name_and_domain(resname, resource[:domain])
|
||||
provider = users.find do |user|
|
||||
# have a match if the full instance name matches the full resource name, OR
|
||||
# the base resource name matches the base instance name, and the
|
||||
# resource domain matches the instance domain
|
||||
username, user_domain = name_and_domain(user.name, user.domain)
|
||||
(user.name == resname) ||
|
||||
((username == name) && (user_domain == resdomain))
|
||||
end
|
||||
resource.provider = provider if provider
|
||||
prefetch_composite(resources) do |sorted_namevars|
|
||||
domain = sorted_namevars[0]
|
||||
name = sorted_namevars[1]
|
||||
resource_to_name(domain, name)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
require 'puppet/provider/keystone'
|
||||
require 'puppet/provider/keystone/util'
|
||||
require 'puppet_x/keystone/composite_namevar'
|
||||
|
||||
Puppet::Type.type(:keystone_user_role).provide(
|
||||
:openstack,
|
||||
:parent => Puppet::Provider::Keystone
|
||||
) do
|
||||
|
||||
desc "Provider to manage keystone role assignments to users."
|
||||
|
||||
include PuppetX::Keystone::CompositeNamevar::Helpers
|
||||
|
||||
@credentials = Puppet::Provider::Openstack::CredentialsV3.new
|
||||
|
||||
def initialize(value={})
|
||||
@ -36,17 +38,17 @@ Puppet::Type.type(:keystone_user_role).provide(
|
||||
|
||||
def exists?
|
||||
if self.class.user_role_hash.nil? || self.class.user_role_hash.empty?
|
||||
roles = self.class.request('role', 'list', properties)
|
||||
roles_db = self.class.request('role', 'list', properties)
|
||||
# Since requesting every combination of users, roles, and
|
||||
# projects is so expensive, construct the property hash here
|
||||
# instead of in self.instances so it can be used in the role
|
||||
# and destroy methods
|
||||
@property_hash[:name] = resource[:name]
|
||||
if roles.empty?
|
||||
if roles_db.empty?
|
||||
@property_hash[:ensure] = :absent
|
||||
else
|
||||
@property_hash[:ensure] = :present
|
||||
@property_hash[:roles] = roles.collect do |role|
|
||||
@property_hash[:roles] = roles_db.collect do |role|
|
||||
role[:name]
|
||||
end
|
||||
end
|
||||
@ -54,8 +56,13 @@ Puppet::Type.type(:keystone_user_role).provide(
|
||||
return @property_hash[:ensure] == :present
|
||||
end
|
||||
|
||||
def roles
|
||||
@property_hash[:roles]
|
||||
mk_resource_methods
|
||||
|
||||
# Don't want :absent
|
||||
[:user, :user_domain, :project, :project_domain, :domain].each do |attr|
|
||||
define_method(attr) do
|
||||
@property_hash[attr] ||= resource[attr]
|
||||
end
|
||||
end
|
||||
|
||||
def roles=(value)
|
||||
@ -74,110 +81,42 @@ Puppet::Type.type(:keystone_user_role).provide(
|
||||
def self.instances
|
||||
instances = build_user_role_hash
|
||||
instances.collect do |title, roles|
|
||||
new(
|
||||
new({
|
||||
:name => title,
|
||||
:ensure => :present,
|
||||
:roles => roles
|
||||
)
|
||||
}.merge(@user_role_parameters[title]))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def properties
|
||||
return @properties if @properties
|
||||
properties = []
|
||||
if get_project_id
|
||||
if set?(:project)
|
||||
properties << '--project' << get_project_id
|
||||
elsif get_domain
|
||||
properties << '--domain' << get_domain
|
||||
elsif set?(:domain)
|
||||
properties << '--domain' << domain
|
||||
else
|
||||
raise(Puppet::Error, 'No project or domain specified for role')
|
||||
end
|
||||
properties << '--user' << get_user_id
|
||||
properties
|
||||
end
|
||||
|
||||
def get_user
|
||||
resource[:name].rpartition('@').first
|
||||
end
|
||||
|
||||
def get_project
|
||||
resource[:name].rpartition('@').last
|
||||
end
|
||||
|
||||
# if the role is for a domain, it will be specified as
|
||||
# user@::domain - the "project" part will be empty
|
||||
def get_domain
|
||||
# use defined because @domain may be nil
|
||||
return @domain if defined?(@domain)
|
||||
projname, domname = Util.split_domain(get_project)
|
||||
if projname.nil?
|
||||
@domain = domname # no project specified, so must be a domain
|
||||
else
|
||||
@domain = nil # not a domain specific role
|
||||
end
|
||||
@domain
|
||||
@properties = properties
|
||||
end
|
||||
|
||||
def get_user_id
|
||||
return @user_id if defined?(@user_id)
|
||||
user_name, domain_name = Util.split_domain(get_user)
|
||||
if user_name.nil?
|
||||
@user_id = nil
|
||||
else
|
||||
user = self.class.fetch_user(user_name, domain_name)
|
||||
if user.nil?
|
||||
raise(Puppet::Error, "No user #{user_name} with domain #{domain_name} found")
|
||||
else
|
||||
@user_id = user[:id]
|
||||
end
|
||||
end
|
||||
@user_id
|
||||
user_db = self.class.fetch_user(user, user_domain)
|
||||
raise(Puppet::Error, "No user #{user} with domain #{user_domain} found") if user_db.nil?
|
||||
user_db[:id]
|
||||
end
|
||||
|
||||
def get_project_id
|
||||
return @project_id if defined?(@project_id)
|
||||
project_name, domain_name = Util.split_domain(get_project)
|
||||
if project_name.nil?
|
||||
@project_id = nil
|
||||
else
|
||||
project = self.class.fetch_project(project_name, domain_name)
|
||||
if project.nil?
|
||||
raise(Puppet::Error, "No project #{project_name} with domain #{domain_name} found")
|
||||
else
|
||||
@project_id = project[:id]
|
||||
end
|
||||
end
|
||||
@project_id
|
||||
end
|
||||
|
||||
def self.get_projects
|
||||
request('project', 'list', '--long').collect do |project|
|
||||
{
|
||||
:id => project[:id],
|
||||
:name => project[:name],
|
||||
:domain_id => project[:domain_id],
|
||||
:domain => domain_name_from_id(project[:domain_id])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_users(project_id=nil, domain_id=nil)
|
||||
properties = ['--long']
|
||||
if project_id
|
||||
properties << '--project' << project_id
|
||||
elsif domain_id
|
||||
properties << '--domain' << domain_id
|
||||
end
|
||||
request('user', 'list', properties).collect do |user|
|
||||
{
|
||||
:id => user[:id],
|
||||
:name => user[:name],
|
||||
# note - column is "Domain" but it is really the domain id
|
||||
:domain_id => user[:domain],
|
||||
:domain => domain_name_from_id(user[:domain])
|
||||
}
|
||||
project_db = self.class.fetch_project(project, project_domain)
|
||||
if project_db.nil?
|
||||
raise(Puppet::Error, "No project #{project} with domain #{project_domain} found")
|
||||
end
|
||||
project_db[:id]
|
||||
end
|
||||
|
||||
def self.user_role_hash
|
||||
@ -193,6 +132,7 @@ Puppet::Type.type(:keystone_user_role).provide(
|
||||
# given key does not exist, create it with an empty
|
||||
# array as the value for the hash key
|
||||
hash = @user_role_hash || Hash.new{|h,k| h[k] = []}
|
||||
@user_role_parameters = {}
|
||||
return hash unless hash.empty?
|
||||
# Need a mapping of project id to names.
|
||||
project_hash = {}
|
||||
@ -210,12 +150,21 @@ Puppet::Type.type(:keystone_user_role).provide(
|
||||
# now, get all role assignments
|
||||
request('role assignment', 'list').each do |assignment|
|
||||
if assignment[:user]
|
||||
if assignment[:project]
|
||||
hash["#{user_hash[assignment[:user]]}@#{project_hash[assignment[:project]]}"] << role_hash[assignment[:role]]
|
||||
user_str = user_hash[assignment[:user]]
|
||||
if assignment[:project] && !assignment[:project].empty?
|
||||
project_str = project_hash[assignment[:project]]
|
||||
name = "#{user_str}@#{project_str}"
|
||||
@user_role_parameters[name] = Hash[
|
||||
[:user_domain, :user, :project_domain, :project]
|
||||
.zip(name_to_resource(user_str) + name_to_resource(project_str))]
|
||||
else
|
||||
domainname = domain_id_to_name(assignment[:domain])
|
||||
hash["#{user_hash[assignment[:user]]}@::#{domainname}"] << role_hash[assignment[:role]]
|
||||
domainname = domain_name_from_id(assignment[:domain])
|
||||
name = "#{user_hash[assignment[:user]]}@::#{domainname}"
|
||||
@user_role_parameters[name] = Hash[
|
||||
[:user_domain, :user, :domain]
|
||||
.zip(name_to_resource(user_str) + [domainname])]
|
||||
end
|
||||
hash[name] << role_hash[assignment[:role]]
|
||||
end
|
||||
end
|
||||
set_user_role_hash(hash)
|
||||
|
@ -49,6 +49,4 @@ Puppet::Type.newtype(:keystone_domain) do
|
||||
autorequire(:anchor) do
|
||||
['keystone_started']
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
@ -2,6 +2,8 @@
|
||||
File.expand_path('../..', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
|
||||
File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
|
||||
require 'puppet/provider/keystone/util'
|
||||
require 'puppet_x/keystone/composite_namevar'
|
||||
require 'puppet_x/keystone/type'
|
||||
|
||||
Puppet::Type.newtype(:keystone_tenant) do
|
||||
|
||||
@ -35,18 +37,15 @@ Puppet::Type.newtype(:keystone_tenant) do
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:domain) do
|
||||
newparam(:domain) do
|
||||
desc 'Domain for tenant.'
|
||||
newvalues(nil, /\S+/)
|
||||
def insync?(is)
|
||||
raise(Puppet::Error, "[keystone_tenant]: The domain cannot be changed from #{self.should} to #{is}") unless self.should == is
|
||||
true
|
||||
end
|
||||
isnamevar
|
||||
include PuppetX::Keystone::Type::DefaultDomain
|
||||
end
|
||||
|
||||
autorequire(:keystone_domain) do
|
||||
# use the domain parameter if given, or the one from name if any
|
||||
self[:domain] || Util.split_domain(self[:name])[1]
|
||||
self[:domain]
|
||||
end
|
||||
|
||||
# This ensures the service is started and therefore the keystone
|
||||
@ -56,4 +55,8 @@ Puppet::Type.newtype(:keystone_tenant) do
|
||||
autorequire(:anchor) do
|
||||
['keystone_started', 'default_domain_created']
|
||||
end
|
||||
|
||||
def self.title_patterns
|
||||
PuppetX::Keystone::CompositeNamevar.basic_split_title_patterns(:name, :domain)
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,8 @@ File.expand_path('../..', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift
|
||||
File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
|
||||
|
||||
require 'puppet/provider/keystone/util'
|
||||
require 'puppet_x/keystone/composite_namevar'
|
||||
require 'puppet_x/keystone/type'
|
||||
|
||||
Puppet::Type.newtype(:keystone_user) do
|
||||
|
||||
@ -26,9 +28,9 @@ Puppet::Type.newtype(:keystone_user) do
|
||||
newvalues(/\S+/)
|
||||
def change_to_s(currentvalue, newvalue)
|
||||
if currentvalue == :absent
|
||||
return "created password"
|
||||
return 'created password'
|
||||
else
|
||||
return "changed password"
|
||||
return 'changed password'
|
||||
end
|
||||
end
|
||||
|
||||
@ -59,21 +61,22 @@ Puppet::Type.newtype(:keystone_user) do
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:domain) do
|
||||
newvalues(nil, /\S+/)
|
||||
def insync?(is)
|
||||
raise(Puppet::Error, "[keystone_user]: The domain cannot be changed from #{self.should} to #{is}") unless self.should == is
|
||||
true
|
||||
end
|
||||
newparam(:domain) do
|
||||
isnamevar
|
||||
include PuppetX::Keystone::Type::DefaultDomain
|
||||
end
|
||||
|
||||
autorequire(:keystone_domain) do
|
||||
# use the domain parameter if given, or the one from name if any
|
||||
self[:domain] or Util.split_domain(self[:name])[1]
|
||||
self[:domain]
|
||||
end
|
||||
|
||||
# we should not do anything until the keystone service is started
|
||||
autorequire(:anchor) do
|
||||
['keystone_started','default_domain_created']
|
||||
end
|
||||
|
||||
def self.title_patterns
|
||||
PuppetX::Keystone::CompositeNamevar.basic_split_title_patterns(:name, :domain)
|
||||
end
|
||||
end
|
||||
|
@ -3,40 +3,83 @@ File.expand_path('../..', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift
|
||||
File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
|
||||
|
||||
require 'puppet/provider/keystone/util'
|
||||
require 'puppet_x/keystone/composite_namevar'
|
||||
require 'puppet_x/keystone/type'
|
||||
|
||||
Puppet::Type.newtype(:keystone_user_role) do
|
||||
|
||||
desc <<-EOT
|
||||
This is currently used to model the creation of
|
||||
keystone users roles.
|
||||
This is currently used to model the creation of keystone users
|
||||
roles.
|
||||
|
||||
User roles are an assignment of a role to a user on a certain
|
||||
tenant. The combination of all of these attributes is unique.
|
||||
|
||||
The resource's name can be specified like this:
|
||||
|
||||
<user(::user_domain)?>@<project(::project_domain)?|::domain>
|
||||
|
||||
which means the user is required. Project and domain are mutually
|
||||
exclusive. User_domain and project_domain are optional.
|
||||
|
||||
"user_domain" and "project_domain" resources default to the name
|
||||
of the "Keystone_domain" resource which has the "is_default"
|
||||
property set to true in the current catalog, or to "Default" if
|
||||
such resource doesn't exist in the catalog.
|
||||
|
||||
User roles are an assignment of a role to a user on
|
||||
a certain tenant. The combination of all of these
|
||||
attributes is unique.
|
||||
EOT
|
||||
|
||||
include PuppetX::Keystone::CompositeNamevar::Helpers
|
||||
ensurable
|
||||
|
||||
newparam(:name, :namevar => true) do
|
||||
newparam(:name, :namevar => true)
|
||||
|
||||
[:user, :project].each do |p|
|
||||
newparam(p) do
|
||||
isnamevar
|
||||
defaultto PuppetX::Keystone::CompositeNamevar::Unset
|
||||
end
|
||||
end
|
||||
|
||||
[:user_domain, :project_domain].each do |p|
|
||||
newparam(p) do
|
||||
isnamevar
|
||||
include PuppetX::Keystone::Type::DefaultDomain
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:domain) do
|
||||
isnamevar
|
||||
defaultto PuppetX::Keystone::CompositeNamevar::Unset
|
||||
validate do |v|
|
||||
if !resource.parameters[:project].nil? &&
|
||||
resource.parameters[:project].value != PuppetX::Keystone::CompositeNamevar::Unset &&
|
||||
v != PuppetX::Keystone::CompositeNamevar::Unset
|
||||
raise(Puppet::ResourceError,
|
||||
'Cannot define both project and domain for a role.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:roles, :array_matching => :all) do
|
||||
def insync?(is)
|
||||
return false unless is.is_a? Array
|
||||
# order of roles does not matter
|
||||
is.sort == self.should.sort
|
||||
is.sort == should.sort
|
||||
end
|
||||
end
|
||||
|
||||
autorequire(:keystone_user) do
|
||||
self[:name].rpartition('@').first
|
||||
# Pass through title parsing for matching resource.
|
||||
[provider.class.resource_to_name(self[:user_domain], self[:user], false)]
|
||||
end
|
||||
|
||||
autorequire(:keystone_tenant) do
|
||||
proj, dom = Util.split_domain(self[:name].rpartition('@').last)
|
||||
rv = nil
|
||||
if proj # i.e. not ::domain
|
||||
rv = self[:name].rpartition('@').last
|
||||
rv = []
|
||||
unless parameter_set?(:domain)
|
||||
# Pass through title parsing for matching resource.
|
||||
rv << provider.class.resource_to_name(self[:project_domain],
|
||||
self[:project], false)
|
||||
end
|
||||
rv
|
||||
end
|
||||
@ -46,15 +89,9 @@ Puppet::Type.newtype(:keystone_user_role) do
|
||||
end
|
||||
|
||||
autorequire(:keystone_domain) do
|
||||
rv = []
|
||||
userdom = Util.split_domain(self[:name].rpartition('@').first)[1]
|
||||
if userdom
|
||||
rv << userdom
|
||||
end
|
||||
projectdom = Util.split_domain(self[:name].rpartition('@').last)[1]
|
||||
if projectdom
|
||||
rv << projectdom
|
||||
end
|
||||
rv = [self[:user_domain]]
|
||||
rv << self[:project_domain] if parameter_set?(:project_domain)
|
||||
rv << self[:domain] if parameter_set?(:domain)
|
||||
rv
|
||||
end
|
||||
|
||||
@ -62,4 +99,72 @@ Puppet::Type.newtype(:keystone_user_role) do
|
||||
autorequire(:anchor) do
|
||||
['keystone_started']
|
||||
end
|
||||
|
||||
def self.title_patterns
|
||||
user = PuppetX::Keystone::CompositeNamevar.not_two_colon_regex
|
||||
project_domain = user
|
||||
domain = user
|
||||
user_domain = Regexp.new(/(?:[^:@]|:[^:@])+/)
|
||||
project = user_domain
|
||||
unset = ->(_) { PuppetX::Keystone::CompositeNamevar::Unset }
|
||||
[
|
||||
[
|
||||
# fully qualified user with fully qualified project
|
||||
/^(#{user})::(#{user_domain})@(#{project})::(#{project_domain})$/,
|
||||
[
|
||||
[:user],
|
||||
[:user_domain],
|
||||
[:project],
|
||||
[:project_domain]
|
||||
]
|
||||
],
|
||||
# fully qualified user with domain
|
||||
[
|
||||
/^(#{user})::(#{user_domain})@::(#{domain})($)/,
|
||||
[
|
||||
[:user],
|
||||
[:user_domain],
|
||||
[:domain],
|
||||
# Don't want to have project_domain set to default, while
|
||||
# not used.
|
||||
[:project_domain, unset]
|
||||
]
|
||||
],
|
||||
# fully qualified user with project
|
||||
[
|
||||
/^(#{user})::(#{user_domain})@(#{project})$/,
|
||||
[
|
||||
[:user],
|
||||
[:user_domain],
|
||||
[:project]
|
||||
]
|
||||
],
|
||||
# user with fully qualified project
|
||||
[
|
||||
/^(#{user})@(#{project})::(#{project_domain})$/,
|
||||
[
|
||||
[:user],
|
||||
[:project],
|
||||
[:project_domain]
|
||||
]
|
||||
],
|
||||
# user with domain
|
||||
[
|
||||
/^(#{user})@::(#{domain})($)/,
|
||||
[
|
||||
[:user],
|
||||
[:domain],
|
||||
[:project_domain, unset]
|
||||
]
|
||||
],
|
||||
# user with project
|
||||
[
|
||||
/^(#{user})@(#{project})$/,
|
||||
[
|
||||
[:user],
|
||||
[:project]
|
||||
]
|
||||
]
|
||||
]
|
||||
end
|
||||
end
|
||||
|
71
lib/puppet_x/keystone/composite_namevar.rb
Normal file
71
lib/puppet_x/keystone/composite_namevar.rb
Normal file
@ -0,0 +1,71 @@
|
||||
# Cherry pick PUP-1073 from puppetlabs: support of composite namevar for alias.
|
||||
if Gem::Version.new(Puppet.version) < Gem::Version.new('4.0.0')
|
||||
Puppet::Resource::Catalog.class_eval do
|
||||
def create_resource_aliases(resource)
|
||||
# Skip creating aliases and checking collisions for non-isomorphic resources.
|
||||
return unless resource.respond_to?(:isomorphic?) and resource.isomorphic?
|
||||
# Add an alias if the uniqueness key is valid and not the
|
||||
# title, which has already been checked.
|
||||
ukey = resource.uniqueness_key
|
||||
if ukey.any? and ukey != [resource.title]
|
||||
self.alias(resource, ukey)
|
||||
end
|
||||
end
|
||||
end
|
||||
Puppet::Resource.class_eval do
|
||||
def uniqueness_key
|
||||
# Temporary kludge to deal with inconsistent use patterns; ensure we don't return nil for namevar/:name
|
||||
h = self.to_hash
|
||||
name = h[namevar] || h[:name] || self.name
|
||||
h[namevar] ||= name
|
||||
h[:name] ||= name
|
||||
h.values_at(*key_attributes.sort_by { |k| k.to_s })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'puppet_x/keystone/composite_namevar/helpers'
|
||||
|
||||
module PuppetX
|
||||
module Keystone
|
||||
module CompositeNamevar
|
||||
|
||||
class Unset; end
|
||||
|
||||
def self.not_two_colon_regex
|
||||
# Anything but 2 consecutive colons.
|
||||
Regexp.new(/(?:[^:]|:[^:])+/)
|
||||
end
|
||||
|
||||
def self.basic_split_title_patterns(prefix, suffix, separator = '::', *regexps)
|
||||
associated_regexps = []
|
||||
if regexps.empty? and separator == '::'
|
||||
associated_regexps += [not_two_colon_regex, not_two_colon_regex]
|
||||
else
|
||||
if regexps.count != 2
|
||||
raise(Puppet::DevError, 'You must provide two regexps')
|
||||
else
|
||||
associated_regexps += regexps
|
||||
end
|
||||
end
|
||||
prefix_re = associated_regexps[0]
|
||||
suffix_re = associated_regexps[1]
|
||||
[
|
||||
[
|
||||
/^(#{prefix_re})#{separator}(#{suffix_re})$/,
|
||||
[
|
||||
[prefix],
|
||||
[suffix]
|
||||
]
|
||||
],
|
||||
[
|
||||
/^(#{prefix_re})$/,
|
||||
[
|
||||
[prefix]
|
||||
]
|
||||
]
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
lib/puppet_x/keystone/composite_namevar/helpers.rb
Normal file
27
lib/puppet_x/keystone/composite_namevar/helpers.rb
Normal file
@ -0,0 +1,27 @@
|
||||
require 'puppet_x/keystone/composite_namevar/helpers/utilities'
|
||||
|
||||
module PuppetX
|
||||
module Keystone
|
||||
module CompositeNamevar
|
||||
module Helpers
|
||||
def set?(param, argument = nil)
|
||||
value = nil
|
||||
if argument.nil?
|
||||
value = send(param.to_sym)
|
||||
else
|
||||
value = send(param.to_sym, argument)
|
||||
end
|
||||
value != PuppetX::Keystone::CompositeNamevar::Unset
|
||||
end
|
||||
|
||||
def parameter_set?(key)
|
||||
set?(:'[]', key.to_sym)
|
||||
end
|
||||
|
||||
def self.included(klass)
|
||||
klass.extend Utilities if klass.to_s.match(/Provider/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
44
lib/puppet_x/keystone/composite_namevar/helpers/utilities.rb
Normal file
44
lib/puppet_x/keystone/composite_namevar/helpers/utilities.rb
Normal file
@ -0,0 +1,44 @@
|
||||
module PuppetX
|
||||
module Keystone
|
||||
module CompositeNamevar
|
||||
module Helpers
|
||||
module Utilities
|
||||
def prefetch_composite(resources)
|
||||
# cannot trust puppet for correct resources with semantic title
|
||||
res = resources.values.first
|
||||
catalog = res.catalog
|
||||
klass = res.class
|
||||
required_resources = catalog.resources.find_all do |e|
|
||||
e.class.to_s == klass.to_s
|
||||
end
|
||||
|
||||
# hash catalog resource by uniq key
|
||||
required_res = Hash[required_resources.map(&:uniqueness_key)
|
||||
.zip(required_resources)]
|
||||
# This is the sort order returned by uniqueness_key.
|
||||
namevars_ordered = resource_type.key_attributes.map(&:to_s).sort
|
||||
existings = instances
|
||||
# uniqueness_key sort by lexical order of the key attributes
|
||||
required_res.each do |res_key, resource|
|
||||
provider = existings.find do |existing|
|
||||
if block_given?
|
||||
# transformation is done on the name using namevar,
|
||||
# so we let the user transform it the correct way.
|
||||
res_transformed_namevar = yield(res_key)
|
||||
# name in self.instance is assumed to have the same
|
||||
# transformation than the one given by the user.
|
||||
exist_transformed_namevar = existing.name
|
||||
res_transformed_namevar == exist_transformed_namevar
|
||||
else
|
||||
res_key == namevars_ordered
|
||||
.map { |namevar| existing.send(namevar) }
|
||||
end
|
||||
end
|
||||
resource.provider = provider if provider
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1
lib/puppet_x/keystone/type.rb
Normal file
1
lib/puppet_x/keystone/type.rb
Normal file
@ -0,0 +1 @@
|
||||
require 'puppet_x/keystone/type/default_domain'
|
82
lib/puppet_x/keystone/type/default_domain.rb
Normal file
82
lib/puppet_x/keystone/type/default_domain.rb
Normal file
@ -0,0 +1,82 @@
|
||||
module PuppetX
|
||||
module Keystone
|
||||
module Type
|
||||
module DefaultDomain
|
||||
def self.included(klass)
|
||||
klass.class_eval do
|
||||
defaultto do
|
||||
default = PuppetX::Keystone::Type::DefaultDomain
|
||||
.calculate_using(resource)
|
||||
deprecation_msg = 'Support for a resource without the domain ' \
|
||||
'set is deprecated in Liberty cycle. ' \
|
||||
'It will be dropped in the M-cycle. ' \
|
||||
"Currently using '#{default}' as default domain."
|
||||
warning(deprecation_msg)
|
||||
default
|
||||
end
|
||||
|
||||
# This make sure that @@default_domain is filled no matter
|
||||
# what.
|
||||
validate do |_|
|
||||
PuppetX::Keystone::Type::DefaultDomain
|
||||
.calculate_using(resource)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
def self.calculate_using(resource)
|
||||
current_default = from_keystone_env
|
||||
return current_default unless current_default.nil?
|
||||
catalog = resource.catalog
|
||||
if catalog.nil?
|
||||
# Running "puppet resource" (resource.catalog is nil)).
|
||||
# We try to get the default from the keystone.conf file
|
||||
# and then, the name from the api.
|
||||
current_default = from_keystone_conf_and_api(resource)
|
||||
else
|
||||
current_default = from_catalog(catalog)
|
||||
end
|
||||
|
||||
resource.provider.class.default_domain = current_default
|
||||
current_default
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.from_keystone_env
|
||||
Puppet::Provider::Keystone.default_domain
|
||||
rescue Puppet::DevError => e
|
||||
raise e unless e.message.match(/The default domain should already be filled in/)
|
||||
end
|
||||
|
||||
def self.from_catalog(catalog)
|
||||
default_res_in_cat = catalog.resources.find do |r|
|
||||
r.class.to_s == 'Puppet::Type::Keystone_domain' &&
|
||||
r[:is_default] == :true &&
|
||||
r[:ensure] == :present
|
||||
end
|
||||
default_res_in_cat[:name] rescue 'Default'
|
||||
end
|
||||
|
||||
def self.from_keystone_conf_and_api(resource)
|
||||
current_default = nil
|
||||
default_domain_from_conf = Puppet::Resource.indirection
|
||||
.find('Keystone_config/identity/default_domain_id')
|
||||
|
||||
if default_domain_from_conf[:ensure] == :absent
|
||||
current_default = 'Default'
|
||||
else
|
||||
current_default = resource.provider.class
|
||||
.domain_name_from_id(default_domain_from_conf[:value])
|
||||
end
|
||||
current_default
|
||||
rescue
|
||||
raise(Puppet::DevError,
|
||||
'The default domain cannot be guessed from your ' \
|
||||
'current installation. Please check that keystone ' \
|
||||
'is working properly')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -67,7 +67,6 @@ describe 'basic keystone server with resources' do
|
||||
database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone',
|
||||
admin_token => 'admin_token',
|
||||
enabled => true,
|
||||
default_domain => 'default_domain',
|
||||
}
|
||||
# "v2" admin and service
|
||||
class { '::keystone::roles::admin':
|
||||
@ -99,15 +98,16 @@ describe 'basic keystone server with resources' do
|
||||
enabled => true,
|
||||
description => 'Domain for admin v3 users',
|
||||
}
|
||||
keystone_tenant { 'servicesv3::service_domain':
|
||||
keystone_tenant { 'servicesv3':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'Tenant for the openstack services',
|
||||
domain => 'service_domain',
|
||||
}
|
||||
keystone_tenant { 'openstackv3::admin_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'admin tenant',
|
||||
description => 'admin tenant'
|
||||
}
|
||||
keystone_user { 'adminv3::admin_domain':
|
||||
ensure => present,
|
||||
@ -115,7 +115,9 @@ describe 'basic keystone server with resources' do
|
||||
email => 'test@example.tld',
|
||||
password => 'a_big_secret',
|
||||
}
|
||||
keystone_user_role { 'adminv3::admin_domain@openstackv3::admin_domain':
|
||||
keystone_user_role { 'adminv3@openstackv3':
|
||||
project_domain => 'admin_domain',
|
||||
user_domain => 'admin_domain',
|
||||
ensure => present,
|
||||
roles => ['admin'],
|
||||
}
|
||||
@ -227,11 +229,11 @@ describe 'basic keystone server with resources' do
|
||||
end
|
||||
describe 'with v2 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name Default --os-project-domain-name Default'
|
||||
end
|
||||
describe "with v2 service with v3 credentials" do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name Default --os-project-domain-name Default'
|
||||
end
|
||||
describe 'with v3 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
@ -241,6 +243,33 @@ describe 'basic keystone server with resources' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username beaker-civ3 --os-password secret --os-project-name servicesv3 --os-user-domain-name service_domain --os-project-domain-name service_domain'
|
||||
end
|
||||
|
||||
end
|
||||
describe 'composite namevar quick test' do
|
||||
context 'similar resources different naming' do
|
||||
let(:pp) do
|
||||
<<-EOM
|
||||
keystone_tenant { 'openstackv3':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'admin tenant',
|
||||
domain => 'admin_domain'
|
||||
}
|
||||
keystone_user { 'adminv3::useless_when_the_domain_is_set':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
email => 'test@example.tld',
|
||||
password => 'a_big_secret',
|
||||
domain => 'admin_domain'
|
||||
}
|
||||
keystone_user_role { 'adminv3::admin_domain@openstackv3::admin_domain':
|
||||
ensure => present,
|
||||
roles => ['admin'],
|
||||
}
|
||||
EOM
|
||||
end
|
||||
it 'should not do any modification' do
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
161
spec/acceptance/default_domain_spec.rb
Normal file
161
spec/acceptance/default_domain_spec.rb
Normal file
@ -0,0 +1,161 @@
|
||||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'basic keystone server with changed domain id' do
|
||||
after(:context) do
|
||||
clean_up_manifest = <<-EOM
|
||||
class { '::keystone':
|
||||
verbose => true,
|
||||
debug => true,
|
||||
database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone',
|
||||
admin_token => 'admin_token',
|
||||
enabled => true,
|
||||
}
|
||||
|
||||
keystone_config { 'identity/default_domain_id': ensure => absent}
|
||||
EOM
|
||||
apply_manifest(clean_up_manifest, :catch_failures => true)
|
||||
end
|
||||
|
||||
context 'new domain id' do
|
||||
let(:pp) do
|
||||
<<-EOM
|
||||
Exec { logoutput => 'on_failure' }
|
||||
|
||||
# make sure apache is stopped before keystone eventlet
|
||||
# in case of wsgi was run before
|
||||
class { '::apache':
|
||||
service_ensure => 'stopped',
|
||||
}
|
||||
Service['httpd'] -> Service['keystone']
|
||||
|
||||
# Common resources
|
||||
case $::osfamily {
|
||||
'Debian': {
|
||||
include ::apt
|
||||
class { '::openstack_extras::repo::debian::ubuntu':
|
||||
release => 'liberty',
|
||||
repo => 'proposed',
|
||||
package_require => true,
|
||||
}
|
||||
}
|
||||
'RedHat': {
|
||||
class { '::openstack_extras::repo::redhat::redhat':
|
||||
manage_rdo => false,
|
||||
repo_hash => {
|
||||
'openstack-common-testing' => {
|
||||
'baseurl' => 'http://cbs.centos.org/repos/cloud7-openstack-common-testing/x86_64/os/',
|
||||
'descr' => 'openstack-common-testing',
|
||||
'gpgcheck' => 'no',
|
||||
},
|
||||
'openstack-liberty-testing' => {
|
||||
'baseurl' => 'http://cbs.centos.org/repos/cloud7-openstack-liberty-testing/x86_64/os/',
|
||||
'descr' => 'openstack-liberty-testing',
|
||||
'gpgcheck' => 'no',
|
||||
},
|
||||
'openstack-liberty-trunk' => {
|
||||
'baseurl' => 'http://trunk.rdoproject.org/centos7-liberty/current-passed-ci/',
|
||||
'descr' => 'openstack-liberty-trunk',
|
||||
'gpgcheck' => 'no',
|
||||
},
|
||||
},
|
||||
}
|
||||
package { 'openstack-selinux': ensure => 'latest' }
|
||||
}
|
||||
default: {
|
||||
fail("Unsupported osfamily (${::osfamily})")
|
||||
}
|
||||
}
|
||||
|
||||
class { '::mysql::server': }
|
||||
|
||||
# Keystone resources
|
||||
class { '::keystone::client': }
|
||||
class { '::keystone::cron::token_flush': }
|
||||
class { '::keystone::db::mysql':
|
||||
password => 'keystone',
|
||||
}
|
||||
class { '::keystone':
|
||||
verbose => true,
|
||||
debug => true,
|
||||
database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone',
|
||||
admin_token => 'admin_token',
|
||||
enabled => true,
|
||||
default_domain => 'my_default_domain'
|
||||
}
|
||||
keystone_tenant { 'project_in_my_default_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'Project in another default domain',
|
||||
}
|
||||
keystone_user { 'user_in_my_default_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
email => 'test@example.tld',
|
||||
password => 'a_big_secret',
|
||||
}
|
||||
keystone_user_role { 'user_in_my_default_domain@project_in_my_default_domain':
|
||||
ensure => present,
|
||||
roles => ['admin'],
|
||||
}
|
||||
keystone_domain { 'other_domain': ensure => present }
|
||||
keystone_user { 'user_in_my_default_domain::other_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
email => 'test@example.tld',
|
||||
password => 'a_big_secret',
|
||||
}
|
||||
keystone_tenant { 'project_in_my_default_domain::other_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'Project in other domain',
|
||||
}
|
||||
keystone_user_role { 'user_in_my_default_domain@::other_domain':
|
||||
ensure => present,
|
||||
user_domain => 'other_domain',
|
||||
roles => ['admin'],
|
||||
}
|
||||
EOM
|
||||
end
|
||||
|
||||
describe 'puppet apply' do
|
||||
it 'should work with no errors and catch deprecation warning' do
|
||||
apply_manifest(pp, :catch_failures => true) do |result|
|
||||
expect(result.stderr)
|
||||
.to include_regexp([/Keystone_tenant\[project_in_my_default_domain\]\/domain: Support for a resource without.*. Currently using 'my_default_domain' as default domain/,
|
||||
/Keystone_user\[user_in_my_default_domain\]\/domain/,
|
||||
/Keystone_user_role\[user_in_my_default_domain@project_in_my_default_domain\]\/user_domain/,
|
||||
/Keystone_user_role\[user_in_my_default_domain@project_in_my_default_domain\]\/project_domain/])
|
||||
end
|
||||
end
|
||||
it 'should be idempotent' do
|
||||
apply_manifest(pp, :catch_changes => true) do |result|
|
||||
expect(result.stderr)
|
||||
.to include_regexp(/Warning: \/Keystone_tenant.*Currently using 'my_default_domain'/)
|
||||
end
|
||||
end
|
||||
end
|
||||
describe 'puppet resources are successful created' do
|
||||
it 'for tenant' do
|
||||
shell('puppet resource keystone_tenant') do |result|
|
||||
expect(result.stdout)
|
||||
.to include_regexp([/keystone_tenant { 'project_in_my_default_domain::my_default_domain':/,
|
||||
/keystone_tenant { 'project_in_my_default_domain::other_domain':/])
|
||||
end
|
||||
end
|
||||
it 'for user' do
|
||||
shell('puppet resource keystone_user') do |result|
|
||||
expect(result.stdout)
|
||||
.to include_regexp([/keystone_user { 'user_in_my_default_domain::my_default_domain':/,
|
||||
/keystone_user { 'user_in_my_default_domain::other_domain':/])
|
||||
end
|
||||
end
|
||||
it 'for role' do
|
||||
shell('puppet resource keystone_user_role') do |result|
|
||||
expect(result.stdout)
|
||||
.to include_regexp([/keystone_user_role { 'user_in_my_default_domain::my_default_domain@project_in_my_default_domain::my_default_domain':/,
|
||||
/keystone_user_role { 'user_in_my_default_domain::other_domain@::other_domain':/])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -61,7 +61,6 @@ describe 'keystone server running with Apache/WSGI as Identity Provider' do
|
||||
admin_token => 'admin_token',
|
||||
enabled => true,
|
||||
service_name => 'httpd',
|
||||
default_domain => 'default_domain',
|
||||
}
|
||||
include ::apache
|
||||
class { '::keystone::wsgi::apache':
|
||||
@ -230,11 +229,11 @@ describe 'keystone server running with Apache/WSGI as Identity Provider' do
|
||||
end
|
||||
describe 'with v2 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name Default --os-project-domain-name Default'
|
||||
end
|
||||
describe "with v2 service with v3 credentials" do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name Default --os-project-domain-name Default'
|
||||
end
|
||||
describe 'with v3 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
|
@ -61,7 +61,6 @@ describe 'keystone server running with Apache/WSGI with resources' do
|
||||
admin_token => 'admin_token',
|
||||
enabled => true,
|
||||
service_name => 'httpd',
|
||||
default_domain => 'default_domain',
|
||||
}
|
||||
include ::apache
|
||||
class { '::keystone::wsgi::apache':
|
||||
@ -226,11 +225,11 @@ describe 'keystone server running with Apache/WSGI with resources' do
|
||||
end
|
||||
describe 'with v2 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name Default --os-project-domain-name Default'
|
||||
end
|
||||
describe "with v2 service with v3 credentials" do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name Default --os-project-domain-name Default'
|
||||
end
|
||||
describe 'with v3 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
|
@ -3,3 +3,134 @@ shared_examples_for "a Puppet::Error" do |description|
|
||||
expect { is_expected.to have_class_count(1) }.to raise_error(Puppet::Error, description)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'parse title correctly' do |result|
|
||||
let(:title) do |example|
|
||||
example.metadata[:example_group][:description]
|
||||
end
|
||||
let(:current_class) do |example|
|
||||
example.metadata[:described_class]
|
||||
end
|
||||
let(:resource) { current_class.new(:title => title) }
|
||||
it 'should parse this title correctly' do
|
||||
times = result.delete(:calling_default) || 0
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(times).returns('Default')
|
||||
expect(resource.to_hash).to include(result)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for 'croak on the title' do
|
||||
let(:title) do |example|
|
||||
example.metadata[:example_group][:description]
|
||||
end
|
||||
let(:current_class) do |example|
|
||||
example.metadata[:described_class]
|
||||
end
|
||||
let(:user) { current_class.new(:title => title) }
|
||||
it 'croak on the title' do
|
||||
expect { user }.to raise_error(Puppet::Error, /No set of title patterns matched the title/)
|
||||
end
|
||||
end
|
||||
|
||||
# Let resources to [<tested_resource>, <required>, <required>, ..., <not_required>]
|
||||
shared_examples_for 'autorequire the correct resources' do
|
||||
let(:catalog) { Puppet::Resource::Catalog.new }
|
||||
it 'should autorequire correctly' do
|
||||
resource = resources[0]
|
||||
resources_good = resources[1...resources.count-1]
|
||||
catalog.add_resource(*resources)
|
||||
|
||||
dependency = resource.autorequire
|
||||
expect(dependency.size).to eq(resources_good.count)
|
||||
resources_good.each_with_index do |good, idx|
|
||||
expect(dependency[idx].target).to eq(resource)
|
||||
expect(dependency[idx].source).to eq(good)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Let resources to [<existing>, <non_existing>]
|
||||
shared_examples_for 'prefetch the resources' do
|
||||
let(:current_class) do |example|
|
||||
example.metadata[:described_class]
|
||||
end
|
||||
it 'should correctly prefetch the existing resource' do
|
||||
existing = resources[0]
|
||||
non_existing = resources[1]
|
||||
resource = mock
|
||||
r = []
|
||||
r << existing
|
||||
|
||||
catalog = Puppet::Resource::Catalog.new
|
||||
r.each { |res| catalog.add_resource(res) }
|
||||
m_value = mock
|
||||
m_first = mock
|
||||
resource.expects(:values).returns(m_value)
|
||||
m_value.expects(:first).returns(m_first)
|
||||
m_first.expects(:catalog).returns(catalog)
|
||||
m_first.expects(:class).returns(current_class.resource_type)
|
||||
current_class.prefetch(resource)
|
||||
|
||||
# found and not found
|
||||
expect(existing.provider.ensure).to eq(:present)
|
||||
expect(non_existing.provider.ensure).to eq(:absent)
|
||||
end
|
||||
end
|
||||
|
||||
# attribute [Array[Hash]]
|
||||
# - the first hash are the expected result
|
||||
# - second are parameters to test default domain, required but can be empty
|
||||
# - the rest are the combination of attributes you want to test
|
||||
# see examples in user/user_role/tenant
|
||||
shared_examples_for 'create the correct resource' do |attributes|
|
||||
expected_results = attributes.shift['expected_results']
|
||||
default_domain = attributes.shift
|
||||
|
||||
context 'domain filled' do
|
||||
attributes.each do |attribute|
|
||||
context 'test' do
|
||||
let(:resource_attrs) { attribute.values[0] }
|
||||
it "should correctly create the resource when #{attribute.keys[0]}" do
|
||||
times = resource_attrs.delete(:default_domain)
|
||||
unless times.nil?
|
||||
Puppet::Provider::Keystone.expects(:default_domain)
|
||||
.times(times)
|
||||
.returns(default_domain[:name])
|
||||
end
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expected_results.each do |key, value|
|
||||
expect(provider.send(key)).to eq(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
context 'domain not passed, using default' do
|
||||
with_default_domain = default_domain[:attributes]
|
||||
if with_default_domain
|
||||
with_default_domain.each do |attribute|
|
||||
let(:resource_attrs) { attribute[1] }
|
||||
it 'should fall into the default domain' do
|
||||
Puppet::Provider::Keystone.expects(:default_domain)
|
||||
.times(default_domain[:times])
|
||||
.returns(default_domain[:name])
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expected_results.each do |key, value|
|
||||
expect(provider.send(key)).to eq(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Let resources to [<resource_1>, <duplicate>]
|
||||
shared_examples_for 'detect duplicate resource' do
|
||||
let(:catalog) { Puppet::Resource::Catalog.new }
|
||||
it 'should detect the duplicate' do
|
||||
expect { catalog.add_resource(resources[0]) }.not_to raise_error
|
||||
expect { catalog.add_resource(resources[1]) }.to raise_error(ArgumentError,/Cannot alias/)
|
||||
end
|
||||
end
|
||||
|
@ -19,7 +19,10 @@ def setup_provider_tests
|
||||
@admin_token = nil
|
||||
@keystone_file = nil
|
||||
Puppet::Provider::Keystone.default_domain_id = nil
|
||||
Puppet::Provider::Keystone.default_domain = nil
|
||||
@domain_hash = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f }
|
||||
|
@ -54,3 +54,5 @@ RSpec.configure do |c|
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f }
|
||||
|
20
spec/support/matchers/include_regexp.rb
Normal file
20
spec/support/matchers/include_regexp.rb
Normal file
@ -0,0 +1,20 @@
|
||||
RSpec::Matchers.define :include_regexp do |expected|
|
||||
regexps = expected.dup
|
||||
regexps = [regexps] unless regexps.is_a?(Array)
|
||||
expected_count = regexps.count
|
||||
count = 0
|
||||
match do |actual|
|
||||
output = actual
|
||||
output = output.split("\n") unless output.is_a?(Array)
|
||||
output.each do |line|
|
||||
regexps.each_with_index do |regex, regexp_idx|
|
||||
if line.match(regex)
|
||||
count += 1
|
||||
regexps.delete_at(regexp_idx)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
expected_count == count
|
||||
end
|
||||
end
|
@ -22,7 +22,7 @@ describe provider_class do
|
||||
:name => 'foo',
|
||||
:description => 'foo',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:enabled => 'True'
|
||||
}
|
||||
end
|
||||
|
||||
@ -65,8 +65,7 @@ describe provider_class do
|
||||
name="foo"
|
||||
description="foo"
|
||||
enabled=True
|
||||
'
|
||||
)
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
end
|
||||
@ -99,8 +98,7 @@ enabled=True
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Description","Enabled"
|
||||
"1cb05cfed7c24279be884ba4f6520262","foo","foo",True
|
||||
'
|
||||
)
|
||||
')
|
||||
instances = provider_class.instances
|
||||
expect(instances.count).to eq(1)
|
||||
end
|
||||
@ -131,8 +129,7 @@ enabled=True
|
||||
name="foo"
|
||||
description="foo"
|
||||
enabled=True
|
||||
'
|
||||
)
|
||||
')
|
||||
expect(provider.class.default_domain_id).to eq('default')
|
||||
expect(another_class.default_domain_id).to eq('default')
|
||||
provider.create
|
||||
|
@ -142,41 +142,6 @@ id="the_user_id"
|
||||
expect(klass.fetch_user('The User', 'Default')).to eq({:name=>"The User", :id=>"the_user_id"})
|
||||
end
|
||||
end
|
||||
describe '#default_domain_set?' do
|
||||
it 'should be false when default_domain_id is default' do
|
||||
mock = {'identity' => {'default_domain_id' => 'default'}}
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.default_domain_set?).to be_falsey
|
||||
end
|
||||
it 'should be true when default_domain_id is not default' do
|
||||
mock = {'identity' => {'default_domain_id' => 'not_default'}}
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.default_domain_set?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
describe '#domain_check' do
|
||||
it 'should not warn when domain name is provided' do
|
||||
klass.domain_check('name', 'domain')
|
||||
expect(klass.domain_check('name', 'domain')).to be_nil
|
||||
end
|
||||
it 'should not warn when domain is not provided and default_domain_id is not set' do
|
||||
klass.domain_check('name', '')
|
||||
expect(klass.domain_check('name', '')).to be_nil
|
||||
end
|
||||
it 'should warn when domain name is empty and default_domain_id is set' do
|
||||
mock = {'identity' => {'default_domain_id' => 'not_default'}}
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
klass.expects(:warning).with("In Liberty, not providing a domain name (::domain) for a resource name (name) is deprecated when the default_domain_id is not 'default'")
|
||||
klass.domain_check('name', '')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_admin_endpoint' do
|
||||
it 'should return nothing if there is no keystone config file' do
|
||||
@ -367,64 +332,6 @@ id="other_domain_id"
|
||||
set_env
|
||||
end
|
||||
|
||||
it 'name_and_domain should return the resource domain' do
|
||||
expect(klass.name_and_domain('foo::in_name', 'from_resource', 'default')).to eq(['foo', 'from_resource'])
|
||||
end
|
||||
it 'name_and_domain should return the default domain' do
|
||||
expect(klass.name_and_domain('foo', nil, 'default')).to eq(['foo', 'default'])
|
||||
end
|
||||
it 'name_and_domain should return the domain part of the name' do
|
||||
expect(klass.name_and_domain('foo::in_name', nil, 'default')).to eq(['foo', 'in_name'])
|
||||
end
|
||||
it 'should return the default domain name using the default_domain_id from keystone.conf' do
|
||||
mock = {
|
||||
'DEFAULT' => {
|
||||
'admin_endpoint' => 'http://127.0.0.1:35357',
|
||||
'admin_token' => 'admin_token'
|
||||
},
|
||||
'identity' => {'default_domain_id' => 'somename'}
|
||||
}
|
||||
File.expects(:exists?).with('/etc/keystone/keystone.conf').returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
klass.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"somename","SomeName",True,"default domain"
|
||||
')
|
||||
expect(klass.name_and_domain('foo')).to eq(['foo', 'SomeName'])
|
||||
end
|
||||
it 'should return the default_domain_id from one class set in another class' do
|
||||
klass.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default domain"
|
||||
"somename","SomeName",True,"some domain"
|
||||
')
|
||||
another_class.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default domain"
|
||||
"somename","SomeName",True,"some domain"
|
||||
')
|
||||
expect(klass.default_domain).to eq('Default')
|
||||
expect(another_class.default_domain).to eq('Default')
|
||||
klass.default_domain_id = 'somename'
|
||||
expect(klass.default_domain).to eq('SomeName')
|
||||
expect(another_class.default_domain).to eq('SomeName')
|
||||
end
|
||||
it 'should return Default if default_domain_id is not configured' do
|
||||
mock = {}
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
File.expects(:exists?).with('/etc/keystone/keystone.conf').returns(true)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
klass.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default domain"
|
||||
')
|
||||
expect(klass.name_and_domain('foo')).to eq(['foo', 'Default'])
|
||||
end
|
||||
it 'should list all domains when requesting a domain name from an ID' do
|
||||
klass.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
|
@ -12,44 +12,44 @@ describe provider_class do
|
||||
provider_class.reset
|
||||
end
|
||||
|
||||
let(:tenant_attrs) do
|
||||
let(:resource_attrs) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
{
|
||||
:name => 'foo',
|
||||
:description => 'foo',
|
||||
:name => 'project_one',
|
||||
:description => 'Project One',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:enabled => 'True'
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) do
|
||||
Puppet::Type::Keystone_tenant.new(tenant_attrs)
|
||||
Puppet::Type::Keystone_tenant.new(resource_attrs)
|
||||
end
|
||||
|
||||
let(:provider) do
|
||||
provider_class.new(resource)
|
||||
end
|
||||
|
||||
def before_hook(domainlist)
|
||||
def before_hook(domainlist, provider_class)
|
||||
if domainlist
|
||||
provider.class.expects(:openstack).once
|
||||
provider_class.expects(:openstack).once
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"foo_domain_id","foo_domain",True,"foo domain"
|
||||
"bar_domain_id","bar_domain",True,"bar domain"
|
||||
"domain_one_id","domain_one",True,"project_one domain"
|
||||
"domain_two_id","domain_two",True,"domain_two domain"
|
||||
"another_domain_id","another_domain",True,"another domain"
|
||||
"disabled_domain_id","disabled_domain",False,"disabled domain"
|
||||
"default","Default",True,"the default domain"
|
||||
'
|
||||
)
|
||||
')
|
||||
end
|
||||
end
|
||||
|
||||
before :each, :domainlist => true do
|
||||
before_hook(true)
|
||||
before_hook(true, provider_class)
|
||||
end
|
||||
|
||||
before :each, :domainlist => false do
|
||||
before_hook(false)
|
||||
before_hook(false, provider_class)
|
||||
end
|
||||
|
||||
let(:set_env) do
|
||||
@ -65,17 +65,16 @@ describe provider_class do
|
||||
|
||||
describe 'when managing a tenant' do
|
||||
|
||||
describe '#create', :domainlist => true do
|
||||
describe '#create', :domainlist => false do
|
||||
it 'creates a tenant' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'create', '--format', 'shell', ['foo', '--enable', '--description', 'foo', '--domain', 'Default'])
|
||||
.returns('description="foo"
|
||||
.with('project', 'create', '--format', 'shell', ['project_one', '--enable', '--description', 'Project One', '--domain', 'Default'])
|
||||
.returns('description="Project One"
|
||||
enabled="True"
|
||||
name="foo"
|
||||
id="foo"
|
||||
domain_id="foo_domain_id"
|
||||
'
|
||||
)
|
||||
name="project_one"
|
||||
id="project_one"
|
||||
domain_id="domain_one_id"
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
end
|
||||
@ -92,159 +91,203 @@ domain_id="foo_domain_id"
|
||||
end
|
||||
|
||||
context 'when tenant does not exist', :domainlist => false do
|
||||
subject(:response) do
|
||||
response = provider.exists?
|
||||
it 'exists? should be false' do
|
||||
expect(provider.exists?).to be_falsey
|
||||
end
|
||||
|
||||
it { expect(response).to be_falsey }
|
||||
end
|
||||
|
||||
describe '#instances', :domainlist => true do
|
||||
it 'finds every tenant' do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', '--long')
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
"1cb05cfed7c24279be884ba4f6520262","project_one","domain_one_id","Project One",True
|
||||
"2cb05cfed7c24279be884ba4f6520262","project_one","domain_two_id","Project One, domain Two",True
|
||||
')
|
||||
instances = provider.class.instances
|
||||
expect(instances[0].name).to eq('project_one::domain_one')
|
||||
expect(instances[0].domain).to eq('domain_one')
|
||||
expect(instances[1].name).to eq('project_one::domain_two')
|
||||
expect(instances[1].domain).to eq('domain_two')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#prefetch' do
|
||||
before(:each) do
|
||||
provider_class.expects(:domain_name_from_id).with('default').returns('Default')
|
||||
provider_class.expects(:domain_name_from_id).with('domain_two_id').returns('domain_two')
|
||||
# There are one for self.instance and one for each Puppet::Type.type calls.
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(3).returns('Default')
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', '--long')
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
"1cb05cfed7c24279be884ba4f6520262","foo","foo_domain_id","foo",True
|
||||
"2cb05cfed7c24279be884ba4f6520262","foo","bar_domain_id","foo",True
|
||||
"1cb05cfed7c24279be884ba4f6520262","project_one","default","A project",True
|
||||
"2cb05cfed7c24279be884ba4f6520262","project_one","domain_two_id","A domain_two",True
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'show', '--format', 'shell', 'foo_domain')
|
||||
.returns('description=""
|
||||
enabled="True"
|
||||
id="foo_domain_id"
|
||||
name="foo_domain"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'show', '--format', 'shell', 'bar_domain')
|
||||
.returns('description=""
|
||||
enabled="True"
|
||||
id="bar_domain_id"
|
||||
name="bar_domain"
|
||||
')
|
||||
instances = provider.class.instances
|
||||
expect(instances[0].name).to eq('foo::foo_domain')
|
||||
expect(instances[0].domain).to eq('foo_domain')
|
||||
expect(instances[1].name).to eq('foo::bar_domain')
|
||||
end
|
||||
let(:resources) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(4).returns('Default')
|
||||
[Puppet::Type.type(:keystone_tenant).new(:title => 'project_one', :ensure => :absent),
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'non_existant', :ensure => :absent)]
|
||||
end
|
||||
include_examples 'prefetch the resources'
|
||||
end
|
||||
|
||||
describe '#flush' do
|
||||
context '.enable' do
|
||||
describe '-> false' do
|
||||
it 'properly set enable to false' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'set', ['37b7086693ec482389799da5dc546fa4', '--disable'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.enabled = :false
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
describe '-> true' do
|
||||
it 'properly set enable to true' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'set', ['37b7086693ec482389799da5dc546fa4', '--enable'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.enabled = :true
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
end
|
||||
context '.description' do
|
||||
it 'change the description' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'set', ['37b7086693ec482389799da5dc546fa4',
|
||||
'--description=new description'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.expects(:resource).returns(:description => 'new description')
|
||||
provider.description = 'new description'
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
context '.enable/description' do
|
||||
it 'properly change the enable and the description' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'set', ['37b7086693ec482389799da5dc546fa4', '--disable',
|
||||
'--description=new description'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.expects(:resource).returns(:description => 'new description')
|
||||
provider.enabled = :false
|
||||
provider.description = 'new description'
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with no domain in resource', :domainlist => true do
|
||||
|
||||
let(:tenant_attrs) do
|
||||
context 'when managing a tenant using v3 domain' do
|
||||
describe '#create' do
|
||||
describe 'with domain in resource', :domainlist => false do
|
||||
before(:each) do
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'create', '--format', 'shell', ['project_one', '--enable', '--description', 'Project One', '--domain', 'domain_one'])
|
||||
.returns('description="Project One"
|
||||
enabled="True"
|
||||
name="project_one"
|
||||
id="project-id"
|
||||
domain_id="domain_one_id"
|
||||
')
|
||||
end
|
||||
include_examples 'create the correct resource', [
|
||||
{
|
||||
:name => 'foo',
|
||||
:description => 'foo',
|
||||
'expected_results' => {
|
||||
:domain => 'domain_one',
|
||||
:id => 'project-id',
|
||||
:name => 'project_one'
|
||||
}
|
||||
},
|
||||
{
|
||||
:name => 'domain_one',
|
||||
:times => 2,
|
||||
:attributes => {
|
||||
'Default' => {
|
||||
:title => 'project_one',
|
||||
:description => 'Project One',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True'
|
||||
}
|
||||
end
|
||||
|
||||
it 'adds default domain to commands' do
|
||||
mock = {
|
||||
'identity' => {'default_domain_id' => 'foo_domain_id'}
|
||||
}
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
File.expects(:exists?).with('/etc/keystone/keystone.conf').returns(true)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'create', '--format', 'shell', ['foo', '--enable', '--description', 'foo', '--domain', 'foo_domain'])
|
||||
.returns('description="foo"
|
||||
enabled="True"
|
||||
name="foo"
|
||||
id="project-id"
|
||||
domain_id="foo_domain_id"
|
||||
'
|
||||
)
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("project-id")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'v3 domains with domain in resource', :domainlist => false do
|
||||
|
||||
let(:tenant_attrs) do
|
||||
},
|
||||
{
|
||||
:name => 'foo',
|
||||
:description => 'foo',
|
||||
'domain in parameter' => {
|
||||
:name => 'project_one',
|
||||
:description => 'Project One',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:domain => 'foo_domain'
|
||||
:domain => 'domain_one',
|
||||
:default_domain => 1
|
||||
}
|
||||
end
|
||||
|
||||
it 'uses given domain in commands' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'create', '--format', 'shell', ['foo', '--enable', '--description', 'foo', '--domain', 'foo_domain'])
|
||||
.returns('description="foo"
|
||||
enabled="True"
|
||||
name="foo"
|
||||
id="project-id"
|
||||
domain_id="foo_domain_id"
|
||||
'
|
||||
)
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("project-id")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with domain in name/title', :domainlist => false do
|
||||
|
||||
let(:tenant_attrs) do
|
||||
},
|
||||
{
|
||||
:name => 'foo::foo_domain',
|
||||
:description => 'foo',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True'
|
||||
}
|
||||
end
|
||||
|
||||
it 'uses given domain in commands' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'create', '--format', 'shell', ['foo', '--enable', '--description', 'foo', '--domain', 'foo_domain'])
|
||||
.returns('description="foo"
|
||||
enabled="True"
|
||||
name="foo"
|
||||
id="project-id"
|
||||
domain_id="foo_domain_id"
|
||||
'
|
||||
)
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("project-id")
|
||||
expect(provider.name).to eq('foo::foo_domain')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with domain in name/title and in resource', :domainlist => false do
|
||||
|
||||
let(:tenant_attrs) do
|
||||
{
|
||||
:name => 'foo::bar_domain',
|
||||
:description => 'foo',
|
||||
'domain in title' => {
|
||||
:title => 'project_one::domain_one',
|
||||
:description => 'Project One',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:domain => 'foo_domain'
|
||||
:default_domain => 1
|
||||
}
|
||||
},
|
||||
{
|
||||
'domain in parameter override domain in title' => {
|
||||
:title => 'project_one::domain_two',
|
||||
:description => 'Project One',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:domain => 'domain_one',
|
||||
:default_domain => 1
|
||||
}
|
||||
},
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
it 'uses given domain in commands' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'create', '--format', 'shell', ['foo', '--enable', '--description', 'foo', '--domain', 'foo_domain'])
|
||||
.returns('description="foo"
|
||||
enabled="True"
|
||||
name="foo"
|
||||
id="project-id"
|
||||
domain_id="foo_domain_id"
|
||||
'
|
||||
)
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("project-id")
|
||||
expect(provider.name).to eq('foo::bar_domain')
|
||||
describe '#prefetch' do
|
||||
before(:each) do
|
||||
provider_class.expects(:domain_name_from_id)
|
||||
.with('domain_one_id').returns('domain_one')
|
||||
provider_class.expects(:domain_name_from_id)
|
||||
.with('domain_two_id').returns('domain_two')
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', '--long')
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
"1cb05cfed7c24279be884ba4f6520262","name","domain_one_id","A project_one",True
|
||||
"2cb05cfed7c24279be884ba4f6520262","project_one","domain_two_id","A domain_two",True
|
||||
')
|
||||
end
|
||||
let(:resources) do
|
||||
# 1 by resource + 3 in prefetch and instance list.
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(5).returns('Default')
|
||||
[
|
||||
Puppet::Type.type(:keystone_tenant)
|
||||
.new(:title => 'name::domain_one', :ensure => :absent),
|
||||
Puppet::Type.type(:keystone_tenant)
|
||||
.new(:title => 'noex::domain_one', :ensure => :absent)
|
||||
]
|
||||
end
|
||||
include_examples 'prefetch the resources'
|
||||
end
|
||||
|
||||
context 'different name, identical resource' do
|
||||
let(:resources) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(2).returns('Default')
|
||||
[
|
||||
Puppet::Type.type(:keystone_tenant)
|
||||
.new(:title => 'name::domain_one', :ensure => :present),
|
||||
Puppet::Type.type(:keystone_tenant)
|
||||
.new(:title => 'name', :domain => 'domain_one', :ensure => :present)
|
||||
]
|
||||
end
|
||||
include_examples 'detect duplicate resource'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -25,19 +25,20 @@ describe provider_class do
|
||||
project_class.reset
|
||||
end
|
||||
|
||||
let(:user_attrs) do
|
||||
let(:resource_attrs) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
{
|
||||
:name => 'user1',
|
||||
:ensure => :present,
|
||||
:enabled => 'True',
|
||||
:password => 'secret',
|
||||
:email => 'user1@example.com',
|
||||
:domain => 'domain1',
|
||||
:domain => 'domain1'
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) do
|
||||
Puppet::Type::Keystone_user.new(user_attrs)
|
||||
Puppet::Type::Keystone_user.new(resource_attrs)
|
||||
end
|
||||
|
||||
let(:provider) do
|
||||
@ -84,14 +85,14 @@ username="user1"
|
||||
|
||||
describe '#instances' do
|
||||
it 'finds every user' do
|
||||
provider.class.expects(:openstack)
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'list', '--quiet', '--format', 'csv', ['--long'])
|
||||
.returns('"ID","Name","Project Id","Domain","Description","Email","Enabled"
|
||||
"user1_id","user1","project1_id","domain1_id","user1 description","user1@example.com",True
|
||||
"user2_id","user2","project2_id","domain2_id","user2 description","user2@example.com",True
|
||||
"user3_id","user3","project3_id","domain3_id","user3 description","user3@example.com",True
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
provider_class.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default"
|
||||
@ -99,51 +100,95 @@ username="user1"
|
||||
"domain2_id","domain2",True,"domain2"
|
||||
"domain3_id","domain3",True,"domain3"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'show', '--format', 'shell', 'domain1')
|
||||
.returns('description=""
|
||||
enabled="True"
|
||||
id="domain1_id"
|
||||
name="domain1"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'show', '--format', 'shell', 'domain2')
|
||||
.returns('description=""
|
||||
enabled="True"
|
||||
id="domain2_id"
|
||||
name="domain2"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'show', '--format', 'shell', 'domain3')
|
||||
.returns('description=""
|
||||
enabled="True"
|
||||
id="domain3_id"
|
||||
name="domain3"
|
||||
')
|
||||
# for self.instances to create the name string in
|
||||
# resource_to_name
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(3).returns('Default')
|
||||
instances = provider.class.instances
|
||||
expect(instances.count).to eq(3)
|
||||
expect(instances[0].name).to eq('user1::domain1')
|
||||
expect(instances[0].domain).to eq('domain1')
|
||||
expect(instances[1].name).to eq('user2::domain2')
|
||||
expect(instances[1].domain).to eq('domain2')
|
||||
expect(instances[2].name).to eq('user3::domain3')
|
||||
expect(instances[2].domain).to eq('domain3')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#prefetch' do
|
||||
let(:resources) do
|
||||
[Puppet::Type.type(:keystone_user).new(:title => 'exists', :ensure => :present),
|
||||
Puppet::Type.type(:keystone_user).new(:title => 'non_exists', :ensure => :present)]
|
||||
end
|
||||
before(:each) do
|
||||
provider_class.expects(:domain_name_from_id).with('default')
|
||||
.returns('Default')
|
||||
provider_class.expects(:domain_name_from_id).with('domain2_id')
|
||||
.returns('bar')
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(7)
|
||||
.returns('Default')
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'list', '--quiet', '--format', 'csv', ['--long'])
|
||||
.returns('"ID","Name","Project Id","Domain","Description","Email","Enabled"
|
||||
"user1_id","exists","project1_id","default","user1 description","user1@example.com",True
|
||||
"user2_id","user2","project2_id","domain2_id","user2 description","user2@example.com",True
|
||||
')
|
||||
end
|
||||
include_examples 'prefetch the resources'
|
||||
end
|
||||
|
||||
describe '#flush' do
|
||||
context '.enable' do
|
||||
describe '-> false' do
|
||||
it 'properly set enable to false' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'set', ['--disable', '37b7086693ec482389799da5dc546fa4'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.enabled = :false
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
describe '-> true' do
|
||||
it 'properly set enable to true' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'set', ['--enable', '37b7086693ec482389799da5dc546fa4'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.enabled = :true
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
end
|
||||
context '.email' do
|
||||
it 'change the mail' do
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'set', ['--email', 'new email',
|
||||
'37b7086693ec482389799da5dc546fa4'])
|
||||
.returns('""')
|
||||
provider.expects(:id).returns('37b7086693ec482389799da5dc546fa4')
|
||||
provider.expects(:resource).returns(:email => 'new email')
|
||||
provider.email = 'new email'
|
||||
provider.flush
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#password" do
|
||||
let(:user_attrs) do
|
||||
describe '#password' do
|
||||
let(:resource_attrs) do
|
||||
{
|
||||
:name => 'foo',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:password => 'foo',
|
||||
:email => 'foo@example.com',
|
||||
:domain => 'domain1',
|
||||
:domain => 'domain1'
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) do
|
||||
Puppet::Type::Keystone_user.new(user_attrs)
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type::Keystone_user.new(resource_attrs)
|
||||
end
|
||||
|
||||
let :provider do
|
||||
@ -151,7 +196,6 @@ name="domain3"
|
||||
end
|
||||
|
||||
it 'checks the password' do
|
||||
provider.instance_variable_get('@property_hash')[:id] = 'user1_id'
|
||||
mock_creds = Puppet::Provider::Openstack::CredentialsV3.new
|
||||
mock_creds.auth_url='http://127.0.0.1:5000'
|
||||
mock_creds.password='foo'
|
||||
@ -160,21 +204,9 @@ name="domain3"
|
||||
mock_creds.project_id='project-id-1'
|
||||
Puppet::Provider::Openstack::CredentialsV3.expects(:new).returns(mock_creds)
|
||||
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default"
|
||||
"domain1_id","domain1",True,"domain1"
|
||||
"domain2_id","domain2",True,"domain2"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', ['--user', 'user1_id', '--long'])
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
"project2_id","project2","domain2_id","",True
|
||||
')
|
||||
Puppet::Provider::Openstack.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', ['--user', 'project1_id', '--long'])
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
"project-id-1","foo","domain1_id","foo",True
|
||||
')
|
||||
Puppet::Provider::Openstack.expects(:openstack)
|
||||
@ -184,20 +216,13 @@ e664a386befa4a30878dcef20e79f167
|
||||
8dce2ae9ecd34c199d2877bf319a3d06
|
||||
ac43ec53d5a74a0b9f51523ae41a29f0
|
||||
')
|
||||
provider.expects(:id).times(2).returns('user1_id')
|
||||
password = provider.password
|
||||
expect(password).to eq('foo')
|
||||
end
|
||||
|
||||
it 'fails the password check' do
|
||||
provider.instance_variable_get('@property_hash')[:id] = 'user1_id'
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default"
|
||||
"domain1_id","domain1",True,"domain1"
|
||||
"domain2_id","domain2",True,"domain2"
|
||||
')
|
||||
Puppet::Provider::Openstack.expects(:openstack)
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', ['--user', 'user1_id', '--long'])
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
"project-id-1","foo","domain1_id","foo",True
|
||||
@ -205,6 +230,7 @@ ac43ec53d5a74a0b9f51523ae41a29f0
|
||||
Puppet::Provider::Openstack.expects(:openstack)
|
||||
.with('token', 'issue', ['--format', 'value'])
|
||||
.raises(Puppet::ExecutionFailure, 'HTTP 401 invalid authentication')
|
||||
provider.expects(:id).times(2).returns('user1_id')
|
||||
password = provider.password
|
||||
expect(password).to eq(nil)
|
||||
end
|
||||
@ -219,7 +245,7 @@ ac43ec53d5a74a0b9f51523ae41a29f0
|
||||
mock_creds.user_id='project1_id'
|
||||
mock_creds.domain_name='domain1'
|
||||
Puppet::Provider::Openstack::CredentialsV3.expects(:new).returns(mock_creds)
|
||||
Puppet::Provider::Openstack.expects(:openstack)
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', ['--user', 'project1_id', '--long'])
|
||||
.returns('"ID","Name","Domain ID","Description","Enabled"
|
||||
')
|
||||
@ -239,7 +265,8 @@ ac43ec53d5a74a0b9f51523ae41a29f0
|
||||
|
||||
describe 'when updating a user with unmanaged password' do
|
||||
|
||||
let(:user_attrs) do
|
||||
let(:resource_attrs) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
{
|
||||
:name => 'user1',
|
||||
:ensure => 'present',
|
||||
@ -247,12 +274,12 @@ ac43ec53d5a74a0b9f51523ae41a29f0
|
||||
:password => 'secret',
|
||||
:replace_password => 'False',
|
||||
:email => 'user1@example.com',
|
||||
:domain => 'domain1',
|
||||
:domain => 'domain1'
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) do
|
||||
Puppet::Type::Keystone_user.new(user_attrs)
|
||||
Puppet::Type::Keystone_user.new(resource_attrs)
|
||||
end
|
||||
|
||||
let :provider do
|
||||
@ -265,42 +292,10 @@ ac43ec53d5a74a0b9f51523ae41a29f0
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with no domain in resource' do
|
||||
let(:user_attrs) do
|
||||
{
|
||||
:name => 'user1',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:password => 'secret',
|
||||
:email => 'user1@example.com',
|
||||
}
|
||||
end
|
||||
|
||||
it 'adds default domain to commands' do
|
||||
mock = {
|
||||
'identity' => {'default_domain_id' => 'domain1_id'}
|
||||
}
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
File.expects(:exists?).with('/etc/keystone/keystone.conf').returns(true)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"domain1_id","domain1",True,"domain1"
|
||||
"domain2_id","domain2",True,"domain2"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'list', '--quiet', '--format', 'csv', ['--user', 'user1_id', '--long'])
|
||||
.returns('"ID","Name"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'show', '--format', 'shell', '_member_')
|
||||
.returns('
|
||||
name="_member_"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'add', ['_member_', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider.class.expects(:openstack)
|
||||
describe 'when managing an user using v3 domains' do
|
||||
describe '#create' do
|
||||
before(:each) do
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'create', '--format', 'shell', ['user1', '--enable', '--password', 'secret', '--email', 'user1@example.com', '--domain', 'domain1'])
|
||||
.returns('email="user1@example.com"
|
||||
enabled="True"
|
||||
@ -308,96 +303,103 @@ id="user1_id"
|
||||
name="user1"
|
||||
username="user1"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'show', '--format', 'shell', ['project1', '--domain', 'domain2'])
|
||||
.returns('name="project1"
|
||||
id="project1_id"
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("user1_id")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with domain in resource' do
|
||||
let(:user_attrs) do
|
||||
include_examples 'create the correct resource', [
|
||||
{
|
||||
'expected_results' => {
|
||||
:domain => 'domain1',
|
||||
:id => 'user1_id',
|
||||
:name => 'user1',
|
||||
:domain => 'domain1'
|
||||
}
|
||||
},
|
||||
{
|
||||
:name => 'domain1',
|
||||
:times => 2,
|
||||
:attributes => {
|
||||
'Default' => {
|
||||
:title => 'user1',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:password => 'secret',
|
||||
:email => 'user1@example.com'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'domain in parameter' => {
|
||||
:name => 'user1',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:password => 'secret',
|
||||
:email => 'user1@example.com',
|
||||
:domain => 'domain1',
|
||||
:default_domain => 1
|
||||
}
|
||||
end
|
||||
|
||||
it 'uses given domain in commands' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('user', 'create', '--format', 'shell', ['user1', '--enable', '--password', 'secret', '--email', 'user1@example.com', '--domain', 'domain1'])
|
||||
.returns('email="user1@example.com"
|
||||
enabled="True"
|
||||
id="user1_id"
|
||||
name="user1"
|
||||
username="user1"
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("user1_id")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with domain in name/title' do
|
||||
let(:user_attrs) do
|
||||
},
|
||||
{
|
||||
:name => 'user1::domain1',
|
||||
'domain in title' => {
|
||||
:title => 'user1::domain1',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:password => 'secret',
|
||||
:email => 'user1@example.com',
|
||||
:default_domain => 1
|
||||
}
|
||||
end
|
||||
|
||||
it 'uses given domain in commands' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('user', 'create', '--format', 'shell', ['user1', '--enable', '--password', 'secret', '--email', 'user1@example.com', '--domain', 'domain1'])
|
||||
.returns('email="user1@example.com"
|
||||
enabled="True"
|
||||
id="user1_id"
|
||||
name="user1"
|
||||
username="user1"
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("user1_id")
|
||||
expect(provider.name).to eq('user1::domain1')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'v3 domains with domain in name/title and in resource' do
|
||||
let(:user_attrs) do
|
||||
},
|
||||
{
|
||||
:name => 'user1::domain1',
|
||||
'domain in parameter override domain in title' => {
|
||||
:title => 'user1::foobar',
|
||||
:ensure => 'present',
|
||||
:enabled => 'True',
|
||||
:password => 'secret',
|
||||
:email => 'user1@example.com',
|
||||
:domain => 'domain1',
|
||||
:default_domain => 1
|
||||
}
|
||||
},
|
||||
]
|
||||
end
|
||||
|
||||
it 'uses the resource domain in commands' do
|
||||
provider.class.expects(:openstack)
|
||||
.with('user', 'create', '--format', 'shell', ['user1', '--enable', '--password', 'secret', '--email', 'user1@example.com', '--domain', 'domain1'])
|
||||
.returns('email="user1@example.com"
|
||||
enabled="True"
|
||||
id="user1_id"
|
||||
name="user1"
|
||||
username="user1"
|
||||
describe '#prefetch' do
|
||||
let(:resources) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
[
|
||||
Puppet::Type.type(:keystone_user)
|
||||
.new(:title => 'exists::domain1', :ensure => :present),
|
||||
Puppet::Type.type(:keystone_user)
|
||||
.new(:title => 'non_exists::domain1', :ensure => :present)
|
||||
]
|
||||
end
|
||||
before(:each) do
|
||||
# Used to make the final display name
|
||||
provider_class.expects(:default_domain).times(3).returns('Default')
|
||||
|
||||
provider_class.expects(:domain_name_from_id)
|
||||
.with('domain1_id').returns('domain1')
|
||||
provider_class.expects(:domain_name_from_id)
|
||||
.with('domain2_id').returns('bar')
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'list', '--quiet', '--format', 'csv', ['--long'])
|
||||
.returns('"ID","Name","Project Id","Domain","Description","Email","Enabled"
|
||||
"user1_id","exists","project1_id","domain1_id","user1 description","user1@example.com",True
|
||||
"user2_id","user2","project2_id","domain2_id","user2 description","user2@example.com",True
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
expect(provider.id).to eq("user1_id")
|
||||
expect(provider.name).to eq('user1::domain1')
|
||||
end
|
||||
include_examples 'prefetch the resources'
|
||||
end
|
||||
|
||||
context 'different name, identical resource' do
|
||||
let(:resources) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
[
|
||||
Puppet::Type.type(:keystone_user)
|
||||
.new(:title => 'name::domain_one', :ensure => :present),
|
||||
Puppet::Type.type(:keystone_user)
|
||||
.new(:title => 'name', :domain => 'domain_one', :ensure => :present)
|
||||
]
|
||||
end
|
||||
include_examples 'detect duplicate resource'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -20,16 +20,17 @@ describe provider_class do
|
||||
after(:each) { provider_class.reset }
|
||||
|
||||
describe 'when managing a user\'s role' do
|
||||
let(:user_role_attrs) do
|
||||
let(:resource_attrs) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
{
|
||||
:name => 'user1::domain1@project1::domain1',
|
||||
:title => 'user1::domain1@project1::domain1',
|
||||
:ensure => 'present',
|
||||
:roles => ['role1', 'role2'],
|
||||
:roles => %w(role1 role2)
|
||||
}
|
||||
end
|
||||
|
||||
let(:resource) do
|
||||
Puppet::Type::Keystone_user_role.new(user_role_attrs)
|
||||
Puppet::Type::Keystone_user_role.new(resource_attrs)
|
||||
end
|
||||
|
||||
let(:provider) do
|
||||
@ -37,31 +38,86 @@ describe provider_class do
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
it 'adds all the roles to the user' do
|
||||
before(:each) do
|
||||
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'list', '--quiet', '--format', 'csv', ['--project', 'project1_id', '--user', 'user1_id' ])
|
||||
provider_class.expects(:openstack)
|
||||
.with('role', 'list', '--quiet', '--format', 'csv',
|
||||
['--project', 'project1_id', '--user', 'user1_id' ])
|
||||
.returns('"ID","Name","Project","User"
|
||||
"role1_id","role1","project1","user1"
|
||||
"role2_id","role2","project1","user1"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'add', ['role1', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'add', ['role2', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'show', '--format', 'shell', ['project1', '--domain', 'domain1'])
|
||||
provider_class.expects(:openstack)
|
||||
.with('role', 'add',
|
||||
['role1', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider_class.expects(:openstack)
|
||||
.with('role', 'add',
|
||||
['role2', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider_class.expects(:openstack)
|
||||
.with('project', 'show', '--format', 'shell',
|
||||
['project1', '--domain', 'domain1'])
|
||||
.returns('name="project1"
|
||||
id="project1_id"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('user', 'show', '--format', 'shell', ['user1', '--domain', 'domain1'])
|
||||
provider_class.expects(:openstack)
|
||||
.with('user', 'show', '--format', 'shell',
|
||||
['user1', '--domain', 'domain1'])
|
||||
.returns('name="user1"
|
||||
id="user1_id"
|
||||
')
|
||||
provider.create
|
||||
expect(provider.exists?).to be_truthy
|
||||
end
|
||||
include_examples 'create the correct resource', [
|
||||
{
|
||||
'expected_results' => {}
|
||||
},
|
||||
{
|
||||
:name => 'domain1',
|
||||
:times => 4,
|
||||
:attributes => {
|
||||
'Default' => {
|
||||
:title => 'user1@project1',
|
||||
:ensure => 'present',
|
||||
:roles => %w(role1 role2)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'all in the title' => {
|
||||
:title => 'user1::domain1@project1::domain1',
|
||||
:ensure => 'present',
|
||||
:roles => %w(role1 role2),
|
||||
:default_domain => 2
|
||||
}
|
||||
},
|
||||
{
|
||||
'user complete and project in the params' => {
|
||||
:title => 'user1::domain1@project1',
|
||||
:ensure => 'present',
|
||||
:project_domain => 'domain1',
|
||||
:roles => %w(role1 role2),
|
||||
:default_domain => 2
|
||||
}
|
||||
},
|
||||
{
|
||||
'user and project in the params' => {
|
||||
:title => 'user1@project1',
|
||||
:ensure => 'present',
|
||||
:project_domain => 'domain1',
|
||||
:user_domain => 'domain1',
|
||||
:roles => %w(role1 role2),
|
||||
:default_domain => 2
|
||||
}
|
||||
},
|
||||
{
|
||||
'project complet and user in the params' => {
|
||||
:title => 'user1@project1::domain1',
|
||||
:ensure => 'present',
|
||||
:user_domain => 'domain1',
|
||||
:roles => %w(role1 role2),
|
||||
:default_domain => 2
|
||||
}
|
||||
},
|
||||
]
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
@ -108,7 +164,7 @@ id="project1_id"
|
||||
.returns('name="user1"
|
||||
id="user1_id"
|
||||
')
|
||||
response = provider.exists?
|
||||
provider.exists?
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
@ -119,10 +175,12 @@ id="user1_id"
|
||||
project_class = Puppet::Type.type(:keystone_tenant).provider(:openstack)
|
||||
user_class = Puppet::Type.type(:keystone_user).provider(:openstack)
|
||||
|
||||
usermock = user_class.new({:id => 'user1_id', :name => 'user1'})
|
||||
usermock = user_class.new(:id => 'user1_id', :name => 'user1')
|
||||
user_class.expects(:instances).with(any_parameters).returns([usermock])
|
||||
|
||||
projectmock = project_class.new({:id => 'project1_id', :name => 'project1'})
|
||||
projectmock = project_class.new(:id => 'project1_id', :name => 'project1')
|
||||
# 2 for tenant and user and 2 for user_role
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(4).returns('Default')
|
||||
project_class.expects(:instances).with(any_parameters).returns([projectmock])
|
||||
|
||||
provider.class.expects(:openstack)
|
||||
@ -135,26 +193,31 @@ id="user1_id"
|
||||
.with('role assignment', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('
|
||||
"Role","User","Group","Project","Domain"
|
||||
"role1-id","user1_id","","project1_id",""
|
||||
"role2-id","user1_id","","project1_id",""
|
||||
"role1-id","user1_id","","project1_id","Default"
|
||||
"role2-id","user1_id","","project1_id","Default"
|
||||
')
|
||||
instances = provider.class.instances
|
||||
expect(instances.count).to eq(1)
|
||||
expect(instances[0].name).to eq('user1@project1')
|
||||
expect(instances[0].roles).to eq(['role1', 'role2'])
|
||||
expect(instances[0].user).to eq('user1')
|
||||
expect(instances[0].user_domain).to eq('Default')
|
||||
expect(instances[0].project).to eq('project1')
|
||||
expect(instances[0].project_domain).to eq('Default')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#roles=' do
|
||||
let(:user_role_attrs) do
|
||||
let(:resource_attrs) do
|
||||
{
|
||||
:name => 'foo@foo',
|
||||
:title => 'foo@foo',
|
||||
:ensure => 'present',
|
||||
:roles => ['one', 'two'],
|
||||
}
|
||||
end
|
||||
|
||||
it 'applies new roles' do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(4).returns('Default')
|
||||
provider.instance_variable_get('@property_hash')[:roles] = ['foo', 'bar']
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'remove', ['foo', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
@ -164,11 +227,6 @@ id="user1_id"
|
||||
.with('role', 'add', ['one', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider.class.expects(:openstack)
|
||||
.with('role', 'add', ['two', '--project', 'project1_id', '--user', 'user1_id'])
|
||||
provider.class.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"Default Domain"
|
||||
')
|
||||
provider.class.expects(:openstack)
|
||||
.with('project', 'show', '--format', 'shell', ['foo', '--domain', 'Default'])
|
||||
.returns('name="foo"
|
||||
@ -179,8 +237,25 @@ id="project1_id"
|
||||
.returns('name="foo"
|
||||
id="user1_id"
|
||||
')
|
||||
provider.roles=(['one', 'two'])
|
||||
end
|
||||
provider.roles = %w(one two)
|
||||
end
|
||||
end
|
||||
|
||||
context 'different name, identical resource' do
|
||||
let(:resources) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(4).returns('Default')
|
||||
[
|
||||
Puppet::Type.type(:keystone_user_role)
|
||||
.new(:title => 'user::domain_user@project::domain_project',
|
||||
:ensure => :present),
|
||||
Puppet::Type.type(:keystone_user_role)
|
||||
.new(:title => 'user@project',
|
||||
:user_domain => 'domain_user',
|
||||
:project_domain => 'domain_project',
|
||||
:ensure => :present)
|
||||
]
|
||||
end
|
||||
include_examples 'detect duplicate resource'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,22 +4,63 @@ require 'puppet/type/keystone_tenant'
|
||||
|
||||
describe Puppet::Type.type(:keystone_tenant) do
|
||||
|
||||
before :each do
|
||||
@project = Puppet::Type.type(:keystone_tenant).new(
|
||||
:name => 'foo',
|
||||
:domain => 'foo-domain',
|
||||
let(:project) do
|
||||
Puppet::Type.type(:keystone_tenant).new(
|
||||
:id => 'blah',
|
||||
:name => 'project',
|
||||
:domain => 'domain_project'
|
||||
)
|
||||
|
||||
@domain = @project.parameter('domain')
|
||||
end
|
||||
|
||||
it 'should not be in sync for domain changes' do
|
||||
expect { @domain.insync?('not-the-domain') }.to raise_error(Puppet::Error, /The domain cannot be changed from/)
|
||||
expect { @domain.insync?(nil) }.to raise_error(Puppet::Error, /The domain cannot be changed from/)
|
||||
it 'should not accept id property' do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
expect { project }.to raise_error(Puppet::Error,
|
||||
/This is a read only property/)
|
||||
end
|
||||
|
||||
it 'should be in sync if domain is the same' do
|
||||
expect(@domain.insync?('foo-domain')).to be true
|
||||
describe 'name::domain' do
|
||||
include_examples 'parse title correctly',
|
||||
:name => 'name',
|
||||
:domain => 'domain',
|
||||
:calling_default => 1
|
||||
end
|
||||
describe 'name' do
|
||||
include_examples 'parse title correctly',
|
||||
:name => 'name', :domain => 'Default', :calling_default => 2
|
||||
end
|
||||
describe 'name::domain::extra' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
|
||||
describe '#autorequire' do
|
||||
let(:domain_good) do
|
||||
Puppet::Type.type(:keystone_domain).new(:title => 'domain_project')
|
||||
end
|
||||
|
||||
let(:domain_bad) do
|
||||
Puppet::Type.type(:keystone_domain).new(:title => 'another_domain')
|
||||
end
|
||||
|
||||
context 'domain autorequire from title' do
|
||||
let(:project) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'tenant::domain_project')
|
||||
end
|
||||
describe 'should require the correct domain' do
|
||||
let(:resources) { [project, domain_good, domain_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
end
|
||||
context 'domain autorequire from parameter' do
|
||||
let(:project) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'tenant',
|
||||
:domain => 'domain_project')
|
||||
end
|
||||
describe 'should require the correct domain' do
|
||||
let(:resources) { [project, domain_good, domain_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -5,8 +5,9 @@ require 'puppet/type/keystone_user_role'
|
||||
describe Puppet::Type.type(:keystone_user_role) do
|
||||
|
||||
before :each do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).times(4).returns('Default')
|
||||
@user_roles = Puppet::Type.type(:keystone_user_role).new(
|
||||
:name => 'foo@bar',
|
||||
:title => 'foo@bar',
|
||||
:roles => ['a', 'b']
|
||||
)
|
||||
|
||||
@ -25,4 +26,192 @@ describe Puppet::Type.type(:keystone_user_role) do
|
||||
expect(@roles.insync?(['b', 'a'])).to be true
|
||||
end
|
||||
|
||||
['user', 'user@REALM', 'us:er'].each do |user|
|
||||
describe "#{user}::user_domain@project::project_domain" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'user_domain',
|
||||
:project => 'project',
|
||||
:project_domain => 'project_domain',
|
||||
:domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:calling_default => 2
|
||||
end
|
||||
|
||||
describe "#{user}::user_domain@::domain" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'user_domain',
|
||||
:project => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:project_domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:domain => 'domain',
|
||||
:calling_default => 2
|
||||
end
|
||||
|
||||
describe "#{user}::user_domain@project" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'user_domain',
|
||||
:project => 'project',
|
||||
:project_domain => 'Default',
|
||||
:domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:calling_default => 3
|
||||
end
|
||||
|
||||
describe "#{user}@project::project_domain" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'Default',
|
||||
:project => 'project',
|
||||
:project_domain => 'project_domain',
|
||||
:domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:calling_default => 3
|
||||
end
|
||||
|
||||
describe "#{user}@::domain" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'Default',
|
||||
:project => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:project_domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:domain => 'domain',
|
||||
:calling_default => 3
|
||||
end
|
||||
|
||||
describe "#{user}@project" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'Default',
|
||||
:project => 'project',
|
||||
:project_domain => 'Default',
|
||||
:domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:calling_default => 4
|
||||
end
|
||||
|
||||
describe "#{user}@proj:ect" do
|
||||
include_examples 'parse title correctly',
|
||||
:user => user,
|
||||
:user_domain => 'Default',
|
||||
:project => 'proj:ect',
|
||||
:project_domain => 'Default',
|
||||
:domain => PuppetX::Keystone::CompositeNamevar::Unset,
|
||||
:calling_default => 4
|
||||
end
|
||||
end
|
||||
describe 'name::domain::foo@project' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
describe 'name::dom@ain@project' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
describe 'name::domain@' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
describe 'name::domain@project::' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
describe '@project:project_domain' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
|
||||
describe '#autorequire' do
|
||||
let(:project_good) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'bar')
|
||||
end
|
||||
let(:project_good_ml) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'blah',
|
||||
:name => 'bar')
|
||||
end
|
||||
let(:project_good_fq) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'bar::Default')
|
||||
end
|
||||
let(:project_bad) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_tenant).new(:title => 'bar::other_domain')
|
||||
end
|
||||
let(:user_good) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
Puppet::Type.type(:keystone_user).new(:title => 'foo')
|
||||
end
|
||||
let(:user_good_ml) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).twice.returns('Default')
|
||||
Puppet::Type.type(:keystone_user).new(:title => 'blah',
|
||||
:name => 'foo')
|
||||
end
|
||||
let(:user_good_fq) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_user).new(:title => 'foo::Default')
|
||||
end
|
||||
let(:user_bad) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_user).new(:title => 'foo::other_domain')
|
||||
end
|
||||
let(:domain) do
|
||||
Puppet::Type.type(:keystone_domain).new(:title => 'bar')
|
||||
end
|
||||
|
||||
context 'tenant' do
|
||||
describe 'normal tenant title' do
|
||||
let(:resources) { [@user_roles, project_good, project_bad] }
|
||||
include_examples 'autorequire the correct resources',
|
||||
:default_domain => 2
|
||||
end
|
||||
|
||||
describe 'meaningless tenant title' do
|
||||
let(:resources) { [@user_roles, project_good_ml, project_bad] }
|
||||
include_examples 'autorequire the correct resources',
|
||||
:default_domain => 1
|
||||
end
|
||||
|
||||
describe 'meaningless tenant title' do
|
||||
let(:resources) { [@user_roles, project_good_fq, project_bad] }
|
||||
include_examples 'autorequire the correct resources',
|
||||
:default_domain => 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'domain' do
|
||||
it 'should not autorequire any tenant' do
|
||||
catalog.add_resource @user_roles, domain
|
||||
dependency = @user_roles.autorequire
|
||||
expect(dependency.size).to eq(0)
|
||||
end
|
||||
let(:resources) { [@user_roles, project_good, project_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
|
||||
context 'user' do
|
||||
describe 'normal user title' do
|
||||
let(:resources) { [@user_roles, user_good, user_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
describe 'meaningless user title' do
|
||||
let(:resources) { [@user_roles, user_good_ml, user_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
|
||||
describe 'fq user title' do
|
||||
let(:resources) { [@user_roles, user_good_fq, user_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'parameter conflict' do
|
||||
let(:user_roles) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).at_least(0).returns('Default')
|
||||
Puppet::Type.type(:keystone_user_role).new(
|
||||
:title => 'user@::domain',
|
||||
:project => 'project',
|
||||
:roles => %w(a b)
|
||||
)
|
||||
end
|
||||
let(:domain) { user_roles.parameter('domain') }
|
||||
|
||||
it 'should not allow domain and project at the same time' do
|
||||
expect { domain.validate }.to raise_error(Puppet::ResourceError, /Cannot define both project and domain/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,22 +4,49 @@ require 'puppet/type/keystone_user'
|
||||
|
||||
describe Puppet::Type.type(:keystone_user) do
|
||||
|
||||
before :each do
|
||||
@project = Puppet::Type.type(:keystone_user).new(
|
||||
:name => 'foo',
|
||||
:domain => 'foo-domain',
|
||||
describe 'name::domain' do
|
||||
include_examples 'parse title correctly',
|
||||
:name => 'name', :domain => 'domain', :calling_default => 1
|
||||
end
|
||||
describe 'name' do
|
||||
include_examples 'parse title correctly',
|
||||
:name => 'name', :domain => 'Default', :calling_default => 2
|
||||
end
|
||||
describe 'name::domain::foo' do
|
||||
include_examples 'croak on the title'
|
||||
end
|
||||
|
||||
describe '#autorequire' do
|
||||
let(:domain_good) do
|
||||
Puppet::Type.type(:keystone_domain).new(:title => 'domain_user')
|
||||
end
|
||||
|
||||
let(:domain_bad) do
|
||||
Puppet::Type.type(:keystone_domain).new(:title => 'another_domain')
|
||||
end
|
||||
|
||||
context 'domain autorequire from title' do
|
||||
let(:user) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_user).new(:title => 'foo::domain_user')
|
||||
end
|
||||
describe 'should require the correct domain' do
|
||||
let(:resources) { [user, domain_good, domain_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
end
|
||||
context 'domain autorequire from parameter' do
|
||||
let(:user) do
|
||||
Puppet::Provider::Keystone.expects(:default_domain).returns('Default')
|
||||
Puppet::Type.type(:keystone_user).new(
|
||||
:title => 'foo',
|
||||
:domain => 'domain_user'
|
||||
)
|
||||
|
||||
@domain = @project.parameter('domain')
|
||||
end
|
||||
|
||||
it 'should not be in sync for domain changes' do
|
||||
expect { @domain.insync?('not-the-domain') }.to raise_error(Puppet::Error, /The domain cannot be changed from/)
|
||||
expect { @domain.insync?(nil) }.to raise_error(Puppet::Error, /The domain cannot be changed from/)
|
||||
describe 'should require the correct domain' do
|
||||
let(:resources) { [user, domain_good, domain_bad] }
|
||||
include_examples 'autorequire the correct resources'
|
||||
end
|
||||
end
|
||||
|
||||
it 'should be in sync if domain is the same' do
|
||||
expect(@domain.insync?('foo-domain')).to be true
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user