Use system scope credentials to request keystone

When SRBAC is enforced, Keystone allows only system admin to create
resources like user, role, role assignment and etc. With this change
now each provider uses system scope credential to create resources
like user, endpoint and etc.

This change also replaces /etc/keystone/puppet.conf by the yaml file
for openstackclient(/etc/openstack/puppet/admin-clouds.yaml)
This allows us to switch a system scope credential and a project
scope credential, and helps us implement a new provider which requires
project scope, in the future.

Depends-on: https://review.opendev.org/828025
Change-Id: I27eb6b11df593581c94ef0affaf5abb8e333833b
This commit is contained in:
Takashi Kajinami 2021-08-29 00:14:52 +09:00
parent 845eb1c9a1
commit c140a44aeb
23 changed files with 184 additions and 207 deletions

View File

@ -8,68 +8,15 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
extend Puppet::Provider::Openstack::Auth extend Puppet::Provider::Openstack::Auth
INI_FILENAME = '/etc/keystone/keystone.conf'
DEFAULT_DOMAIN = 'Default' DEFAULT_DOMAIN = 'Default'
@@default_domain_id = nil @@default_domain_id = nil
def self.conf_filename
'/etc/keystone/puppet.conf'
end
def self.keystone_puppet_conf
return @keystone_puppet_conf if @keystone_puppet_conf
@keystone_puppet_conf = Puppet::Util::IniConfig::File.new
@keystone_puppet_conf.read(conf_filename)
@keystone_puppet_conf
end
def self.get_keystone_puppet_credentials
auth_keys = ['auth_url', 'project_name', 'username', 'password']
conf = keystone_puppet_conf ? keystone_puppet_conf['keystone_authtoken'] : {}
if conf and auth_keys.all?{|k| !conf[k].nil?}
creds = Hash[ auth_keys.map { |k| [k, conf[k].strip] } ]
if conf['project_domain_name']
creds['project_domain_name'] = conf['project_domain_name']
else
creds['project_domain_name'] = 'Default'
end
if conf['user_domain_name']
creds['user_domain_name'] = conf['user_domain_name']
else
creds['user_domain_name'] = 'Default'
end
if conf['region_name']
creds['region_name'] = conf['region_name']
end
if conf['interface']
creds['interface'] = conf['interface']
end
return creds
else
raise(Puppet::Error, "File: #{conf_filename} does not contain all " +
"required configuration keys. Cannot authenticate to Keystone.")
end
end
def self.keystone_puppet_credentials
@keystone_puppet_credentials ||= get_keystone_puppet_credentials
end
def keystone_puppet_credentials
self.class.keystone_puppet_credentials
end
def self.get_auth_endpoint def self.get_auth_endpoint
q = keystone_puppet_credentials configs = self.request('configuration', 'show')
"#{q['auth_url']}" "#{configs['auth.auth_url']}"
rescue Puppet::Error::OpenstackAuthInputError
nil
end end
def self.auth_endpoint def self.auth_endpoint
@ -171,7 +118,7 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
def self.domain_name_from_id(id) def self.domain_name_from_id(id)
unless @domain_hash unless @domain_hash
list = request('domain', 'list') list = system_request('domain', 'list')
if list.nil? if list.nil?
err("Could not list domains") err("Could not list domains")
else else
@ -179,7 +126,7 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
end end
end end
unless @domain_hash.include?(id) unless @domain_hash.include?(id)
domain = request('domain', 'show', id) domain = system_request('domain', 'show', id)
if domain && domain.key?(:name) if domain && domain.key?(:name)
@domain_hash[id] = domain[:name] @domain_hash[id] = domain[:name]
else else
@ -191,11 +138,11 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
def self.domain_id_from_name(name) def self.domain_id_from_name(name)
unless @domain_hash_name unless @domain_hash_name
list = request('domain', 'list') list = system_request('domain', 'list')
@domain_hash_name = Hash[list.collect{|domain| [domain[:name], domain[:id]]}] @domain_hash_name = Hash[list.collect{|domain| [domain[:name], domain[:id]]}]
end end
unless @domain_hash_name.include?(name) unless @domain_hash_name.include?(name)
domain = request('domain', 'show', name) domain = system_request('domain', 'show', name)
if domain && domain.key?(:id) if domain && domain.key?(:id)
@domain_hash_name[name] = domain[:id] @domain_hash_name[name] = domain[:id]
else else
@ -207,7 +154,7 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
def self.fetch_project(name, domain) def self.fetch_project(name, domain)
domain ||= default_domain domain ||= default_domain
request('project', 'show', system_request('project', 'show',
[name, '--domain', domain], [name, '--domain', domain],
{:no_retry_exception_msgs => /No project with a name or ID/}) {:no_retry_exception_msgs => /No project with a name or ID/})
rescue Puppet::ExecutionFailure => e rescue Puppet::ExecutionFailure => e
@ -216,7 +163,7 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
def self.fetch_user(name, domain) def self.fetch_user(name, domain)
domain ||= default_domain domain ||= default_domain
request('user', 'show', system_request('user', 'show',
[name, '--domain', domain], [name, '--domain', domain],
{:no_retry_exception_msgs => /No user with a name or ID/}) {:no_retry_exception_msgs => /No user with a name or ID/})
rescue Puppet::ExecutionFailure => e rescue Puppet::ExecutionFailure => e
@ -234,40 +181,12 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
return auth_url return auth_url
end end
def self.ini_filename def self.project_request(service, action, properties=nil, options={})
INI_FILENAME self.request(service, action, properties, options, 'project')
end end
def self.keystone_file def self.system_request(service, action, properties=nil, options={})
return @keystone_file if @keystone_file self.request(service, action, properties, options, 'system')
if File.exists?(ini_filename)
@keystone_file = Puppet::Util::IniConfig::File.new
@keystone_file.read(ini_filename)
@keystone_file
end
end
def self.request(service, action, properties=nil, options={})
super
rescue Puppet::Error::OpenstackAuthInputError, Puppet::Error::OpenstackUnauthorizedError => error
keystone_request(service, action, error, properties)
end
def self.keystone_request(service, action, error, properties=nil)
properties ||= []
@credentials.username = keystone_puppet_credentials['username']
@credentials.password = keystone_puppet_credentials['password']
@credentials.project_name = keystone_puppet_credentials['project_name']
@credentials.auth_url = auth_endpoint
if keystone_puppet_credentials['region_name']
@credentials.region_name = keystone_puppet_credentials['region_name']
end
if @credentials.version == '3'
@credentials.user_domain_name = keystone_puppet_credentials['user_domain_name']
@credentials.project_domain_name = keystone_puppet_credentials['project_domain_name']
end
raise error unless @credentials.set?
Puppet::Provider::Openstack.request(service, action, properties, @credentials)
end end
def self.set_domain_for_name(name, domain_name) def self.set_domain_for_name(name, domain_name)

View File

@ -37,7 +37,7 @@ Puppet::Type.type(:keystone_domain).provide(
properties << '--description' properties << '--description'
properties << resource[:description] properties << resource[:description]
end end
@property_hash = self.class.request('domain', 'create', properties) @property_hash = self.class.system_request('domain', 'create', properties)
@property_hash[:is_default] = sym_to_bool(resource[:is_default]) @property_hash[:is_default] = sym_to_bool(resource[:is_default])
@property_hash[:ensure] = :present @property_hash[:ensure] = :present
ensure_default_domain(true) ensure_default_domain(true)
@ -53,8 +53,8 @@ Puppet::Type.type(:keystone_domain).provide(
end end
# have to disable first - Keystone does not allow you to delete an # have to disable first - Keystone does not allow you to delete an
# enabled domain # enabled domain
self.class.request('domain', 'set', [resource[:name], '--disable']) self.class.system_request('domain', 'set', [resource[:name], '--disable'])
self.class.request('domain', 'delete', resource[:name]) self.class.system_request('domain', 'delete', resource[:name])
@property_hash[:ensure] = :absent @property_hash[:ensure] = :absent
ensure_default_domain(false, true) ensure_default_domain(false, true)
@property_hash.clear @property_hash.clear
@ -111,7 +111,7 @@ Puppet::Type.type(:keystone_domain).provide(
def self.instances def self.instances
self.do_not_manage = true self.do_not_manage = true
list = request('domain', 'list').collect do |domain| list = system_request('domain', 'list').collect do |domain|
new( new(
:name => domain[:name], :name => domain[:name],
:ensure => :present, :ensure => :present,
@ -142,7 +142,7 @@ Puppet::Type.type(:keystone_domain).provide(
if @property_flush[:description] if @property_flush[:description]
options << '--description' << resource[:description] options << '--description' << resource[:description]
end end
self.class.request('domain', 'set', [resource[:name]] + options) unless options.empty? self.class.system_request('domain', 'set', [resource[:name]] + options) unless options.empty?
if @property_flush[:is_default] if @property_flush[:is_default]
ensure_default_domain(false, false, @property_flush[:is_default]) ensure_default_domain(false, false, @property_flush[:is_default])
end end

View File

@ -65,7 +65,7 @@ Puppet::Type.type(:keystone_endpoint).provide(
end end
ids = @property_hash[:id].split(',') ids = @property_hash[:id].split(',')
ids.each do |id| ids.each do |id|
self.class.request('endpoint', 'delete', id) self.class.system_request('endpoint', 'delete', id)
end end
@property_hash.clear @property_hash.clear
end end
@ -153,10 +153,9 @@ Puppet::Type.type(:keystone_endpoint).provide(
scope.to_s.sub(/_url$/, ''), scope.to_s.sub(/_url$/, ''),
property_flush[scope])[:id] property_flush[scope])[:id]
else else
self.class.request('endpoint', self.class.system_request('endpoint',
'set', 'set',
[ids[scope], [ids[scope], "--url=#{resource[scope]}"])
"--url=#{resource[scope]}"])
end end
end end
end end
@ -170,7 +169,7 @@ Puppet::Type.type(:keystone_endpoint).provide(
def endpoint_create(name, region, interface, url) def endpoint_create(name, region, interface, url)
properties = [name, interface, url, '--region', region] properties = [name, interface, url, '--region', region]
self.class.request('endpoint', 'create', properties) self.class.system_request('endpoint', 'create', properties)
end end
private private
@ -179,7 +178,7 @@ Puppet::Type.type(:keystone_endpoint).provide(
return @endpoints unless @endpoints.nil? return @endpoints unless @endpoints.nil?
prev_do_not_manage = self.do_not_manage prev_do_not_manage = self.do_not_manage
self.do_not_manage = true self.do_not_manage = true
@endpoints = request('endpoint', 'list') @endpoints = system_request('endpoint', 'list')
self.do_not_manage = prev_do_not_manage self.do_not_manage = prev_do_not_manage
@endpoints @endpoints
end end
@ -192,7 +191,7 @@ Puppet::Type.type(:keystone_endpoint).provide(
return @services unless @services.nil? return @services unless @services.nil?
prev_do_not_manage = self.do_not_manage prev_do_not_manage = self.do_not_manage
self.do_not_manage = true self.do_not_manage = true
@services = request('service', 'list') @services = system_request('service', 'list')
self.do_not_manage = prev_do_not_manage self.do_not_manage = prev_do_not_manage
@services @services
end end

View File

@ -35,7 +35,7 @@ Puppet::Type.type(:keystone_identity_provider).provide(
resource[:description] resource[:description]
properties << resource[:name] properties << resource[:name]
@property_hash = self.class.request('identity provider', @property_hash = self.class.system_request('identity provider',
'create', 'create',
properties) properties)
@ -54,7 +54,7 @@ Puppet::Type.type(:keystone_identity_provider).provide(
end end
def destroy def destroy
self.class.request('identity provider', 'delete', id) self.class.system_request('identity provider', 'delete', id)
@property_hash.clear @property_hash.clear
end end
@ -63,11 +63,11 @@ Puppet::Type.type(:keystone_identity_provider).provide(
end end
def self.instances def self.instances
list = request('identity provider', 'list') list = system_request('identity provider', 'list')
list.collect do |identity_provider| list.collect do |identity_provider|
current_resource = current_resource =
request('identity provider', 'show', identity_provider[:id]) system_request('identity provider', 'show', identity_provider[:id])
new( new(
:name => identity_provider[:id], :name => identity_provider[:id],
:id => identity_provider[:id], :id => identity_provider[:id],
@ -100,19 +100,19 @@ Puppet::Type.type(:keystone_identity_provider).provide(
def enabled=(value) def enabled=(value)
options = value == :false ? ['--disable'] : ['--enable'] options = value == :false ? ['--disable'] : ['--enable']
options << id options << id
self.class.request('identity provider', 'set', options) self.class.system_request('identity provider', 'set', options)
end end
def remote_ids=(value) def remote_ids=(value)
options = [] options = []
options += self.class.remote_ids_cli(value) options += self.class.remote_ids_cli(value)
self.class.request('identity provider', 'set', options + [id]) unless self.class.system_request('identity provider', 'set', options + [id]) unless
options.empty? options.empty?
end end
def remote_id_file=(value) def remote_id_file=(value)
options = ['--remote-id-file', value] options = ['--remote-id-file', value]
self.class.request('identity provider', 'set', options + [id]) self.class.system_request('identity provider', 'set', options + [id])
end end
def remote_id_file def remote_id_file
@ -121,7 +121,7 @@ Puppet::Type.type(:keystone_identity_provider).provide(
# bug/python-openstackclient/1478995: when fixed, parsing will be done by OSC. # bug/python-openstackclient/1478995: when fixed, parsing will be done by OSC.
def self.clean_remote_ids(remote_ids) def self.clean_remote_ids(remote_ids)
version = request('--version', '').sub(/openstack\s+/i, '').strip version = system_request('--version', '').sub(/openstack\s+/i, '').strip
if Gem::Version.new(version) < Gem::Version.new('1.9.0') if Gem::Version.new(version) < Gem::Version.new('1.9.0')
clean_remote_ids_old(remote_ids) clean_remote_ids_old(remote_ids)
else else

View File

@ -26,7 +26,7 @@ Puppet::Type.type(:keystone_role).provide(
if self.class.do_not_manage if self.class.do_not_manage
fail("Not managing Keystone_role[#{@resource[:name]}] due to earlier Keystone API failures.") fail("Not managing Keystone_role[#{@resource[:name]}] due to earlier Keystone API failures.")
end end
self.class.request('role', 'create', name) self.class.system_request('role', 'create', name)
@property_hash[:ensure] = :present @property_hash[:ensure] = :present
end end
@ -34,7 +34,7 @@ Puppet::Type.type(:keystone_role).provide(
if self.class.do_not_manage if self.class.do_not_manage
fail("Not managing Keystone_role[#{@resource[:name]}] due to earlier Keystone API failures.") fail("Not managing Keystone_role[#{@resource[:name]}] due to earlier Keystone API failures.")
end end
self.class.request('role', 'delete', @property_hash[:id]) self.class.system_request('role', 'delete', @property_hash[:id])
@property_hash.clear @property_hash.clear
end end
@ -48,7 +48,7 @@ Puppet::Type.type(:keystone_role).provide(
def self.instances def self.instances
self.do_not_manage = true self.do_not_manage = true
list = request('role', 'list') list = system_request('role', 'list')
reallist = list.collect do |role| reallist = list.collect do |role|
new( new(
:name => role[:name].downcase, :name => role[:name].downcase,

View File

@ -33,7 +33,7 @@ Puppet::Type.type(:keystone_service).provide(
if resource[:description] if resource[:description]
properties << '--description' << resource[:description] properties << '--description' << resource[:description]
end end
created = self.class.request('service', 'create', properties) created = self.class.system_request('service', 'create', properties)
@property_hash[:ensure] = :present @property_hash[:ensure] = :present
@property_hash[:type] = resource[:type] @property_hash[:type] = resource[:type]
@property_hash[:id] = created[:id] @property_hash[:id] = created[:id]
@ -44,7 +44,7 @@ Puppet::Type.type(:keystone_service).provide(
if self.class.do_not_manage if self.class.do_not_manage
fail("Not managing Keystone_service[#{@resource[:name]}] due to earlier Keystone API failures.") fail("Not managing Keystone_service[#{@resource[:name]}] due to earlier Keystone API failures.")
end end
self.class.request('service', 'delete', @property_hash[:id]) self.class.system_request('service', 'delete', @property_hash[:id])
@property_hash.clear @property_hash.clear
end end
@ -70,7 +70,7 @@ Puppet::Type.type(:keystone_service).provide(
def self.instances def self.instances
self.do_not_manage = true self.do_not_manage = true
list = request('service', 'list', '--long') list = system_request('service', 'list', '--long')
reallist = list.collect do |service| reallist = list.collect do |service|
new( new(
:name => resource_to_name(service[:type], service[:name], false), :name => resource_to_name(service[:type], service[:name], false),
@ -100,7 +100,7 @@ Puppet::Type.type(:keystone_service).provide(
options << "--name=#{resource[:name]}" options << "--name=#{resource[:name]}"
options << "--description=#{resource[:description]}" if @property_flush[:description] options << "--description=#{resource[:description]}" if @property_flush[:description]
options << "--type=#{resource[:type]}" if @property_flush[:type] options << "--type=#{resource[:type]}" if @property_flush[:type]
self.class.request('service', 'set', [@property_hash[:id]] + options) unless options.empty? self.class.system_request('service', 'set', [@property_hash[:id]] + options) unless options.empty?
@property_flush.clear @property_flush.clear
end end
end end

View File

@ -41,7 +41,7 @@ Puppet::Type.type(:keystone_tenant).provide(
properties << '--domain' properties << '--domain'
properties << resource[:domain] properties << resource[:domain]
@property_hash = self.class.request('project', 'create', properties) @property_hash = self.class.system_request('project', 'create', properties)
@property_hash[:name] = resource[:name] @property_hash[:name] = resource[:name]
@property_hash[:domain] = resource[:domain] @property_hash[:domain] = resource[:domain]
@property_hash[:ensure] = :present @property_hash[:ensure] = :present
@ -63,7 +63,7 @@ Puppet::Type.type(:keystone_tenant).provide(
if self.class.do_not_manage if self.class.do_not_manage
fail("Not managing Keystone_tenant[#{@resource[:name]}] due to earlier Keystone API failures.") fail("Not managing Keystone_tenant[#{@resource[:name]}] due to earlier Keystone API failures.")
end end
self.class.request('project', 'delete', id) self.class.system_request('project', 'delete', id)
@property_hash.clear @property_hash.clear
end end
@ -90,7 +90,7 @@ Puppet::Type.type(:keystone_tenant).provide(
warning(default_domain_deprecation_message) warning(default_domain_deprecation_message)
end end
self.do_not_manage = true self.do_not_manage = true
projects = request('project', 'list', '--long') projects = system_request('project', 'list', '--long')
list = projects.collect do |project| list = projects.collect do |project|
domain_name = domain_name_from_id(project[:domain_id]) domain_name = domain_name_from_id(project[:domain_id])
new( new(
@ -125,7 +125,7 @@ Puppet::Type.type(:keystone_tenant).provide(
options << '--disable' options << '--disable'
end end
(options << "--description=#{resource[:description]}") if @property_flush[:description] (options << "--description=#{resource[:description]}") if @property_flush[:description]
self.class.request('project', 'set', [id] + options) unless options.empty? self.class.system_request('project', 'set', [id] + options) unless options.empty?
@property_flush.clear @property_flush.clear
end end
end end

View File

@ -45,14 +45,14 @@ Puppet::Type.type(:keystone_user).provide(
properties << '--domain' properties << '--domain'
properties << user_domain properties << user_domain
end end
@property_hash = self.class.request('user', 'create', properties) @property_hash = self.class.system_request('user', 'create', properties)
@property_hash[:name] = resource[:name] @property_hash[:name] = resource[:name]
@property_hash[:domain] = user_domain @property_hash[:domain] = user_domain
@property_hash[:ensure] = :present @property_hash[:ensure] = :present
end end
def destroy def destroy
self.class.request('user', 'delete', id) self.class.system_request('user', 'delete', id)
@property_hash.clear @property_hash.clear
end end
@ -67,7 +67,7 @@ Puppet::Type.type(:keystone_user).provide(
# project handled in tenant= separately # project handled in tenant= separately
unless options.empty? unless options.empty?
options << id options << id
self.class.request('user', 'set', options) self.class.system_request('user', 'set', options)
end end
@property_flush.clear @property_flush.clear
end end
@ -125,7 +125,7 @@ Puppet::Type.type(:keystone_user).provide(
# Need to specify a project id to get a project scoped token. List # Need to specify a project id to get a project scoped token. List
# all of the projects for the user, and use the id for the first one # all of the projects for the user, and use the id for the first one
# that is enabled then fallback to domain id only. # that is enabled then fallback to domain id only.
projects = self.class.request('project', 'list', ['--user', id, '--long']) projects = self.class.system_request('project', 'list', ['--user', id, '--long'])
first_project = nil first_project = nil
if projects && projects.respond_to?(:each) if projects && projects.respond_to?(:each)
first_project = projects.detect { |p| p && p[:id] && p[:enabled] == 'True' } first_project = projects.detect { |p| p && p[:id] && p[:enabled] == 'True' }

View File

@ -29,7 +29,7 @@ Puppet::Type.type(:keystone_user_role).provide(
if resource[:roles] if resource[:roles]
options = properties options = properties
resource[:roles].each do |role| resource[:roles].each do |role|
self.class.request('role', 'add', [role] + options) self.class.system_request('role', 'add', [role] + options)
end end
end end
end end
@ -38,14 +38,14 @@ Puppet::Type.type(:keystone_user_role).provide(
if @property_hash[:roles] if @property_hash[:roles]
options = properties options = properties
@property_hash[:roles].each do |role| @property_hash[:roles].each do |role|
self.class.request('role', 'remove', [role] + options) self.class.system_request('role', 'remove', [role] + options)
end end
end end
@property_hash[:ensure] = :absent @property_hash[:ensure] = :absent
end end
def exists? def exists?
roles_db = self.class.request('role assignment', 'list', ['--names'] + properties) roles_db = self.class.system_request('role assignment', 'list', ['--names'] + properties)
@property_hash[:name] = resource[:name] @property_hash[:name] = resource[:name]
if roles_db.empty? if roles_db.empty?
@property_hash[:ensure] = :absent @property_hash[:ensure] = :absent
@ -73,10 +73,10 @@ Puppet::Type.type(:keystone_user_role).provide(
remove = current_roles - Array(value) remove = current_roles - Array(value)
add = Array(value) - current_roles add = Array(value) - current_roles
add.each do |role_name| add.each do |role_name|
self.class.request('role', 'add', [role_name] + properties) self.class.system_request('role', 'add', [role_name] + properties)
end end
remove.each do |role_name| remove.each do |role_name|
self.class.request('role', 'remove', [role_name] + properties) self.class.system_request('role', 'remove', [role_name] + properties)
end end
end end

View File

@ -4,6 +4,8 @@ Puppet::Type.newtype(:keystone_puppet_config) do
'puppet_x', 'keystone_config', 'ini_setting')) 'puppet_x', 'keystone_config', 'ini_setting'))
extend PuppetX::KeystoneConfig::IniSetting extend PuppetX::KeystoneConfig::IniSetting
desc 'Type for /etc/keystone/puppet.conf (DEPRECATED!!)'
create_parameters create_parameters
autorequire(:file) do autorequire(:file) do

View File

@ -159,33 +159,45 @@ class keystone::bootstrap (
}) })
} }
# The below creates and populates the /etc/keystone/puppet.conf file that contains # NOTE(tkajinam): puppet.conf is no longer required and now clouds.yaml
# the credentials that can be loaded by providers. Ensure it has the proper owner, # is used instead.
# group and mode so that it cannot be read by anything other than root. # TODO(tkajinam): Remove this after Y release.
file { '/etc/keystone/puppet.conf': file { '/etc/keystone/puppet.conf':
ensure => 'present', ensure => 'absent',
replace => false, require => Anchor['keystone::config::begin'],
content => '', before => Anchor['keystone::config::end'],
owner => 'root',
group => 'root',
mode => '0600',
require => Anchor['keystone::install::end'],
} }
if $interface == 'admin' { $auth_url_real = $interface ? {
$auth_url_real = $admin_url 'admin' => $admin_url,
} elsif $interface == 'internal' { 'internal' => $internal_url_real,
$auth_url_real = $internal_url_real default => $public_url
} else {
$auth_url_real = $public_url
} }
keystone::resource::authtoken { 'keystone_puppet_config': ensure_resource('file', '/etc/openstack', {
'ensure' => 'directory',
'mode' => '0755',
'owner' => 'root',
'group' => 'root',
})
ensure_resource('file', '/etc/openstack/puppet', {
'ensure' => 'directory',
'mode' => '0755',
'owner' => 'root',
'group' => 'root',
})
openstacklib::clouds { '/etc/openstack/puppet/admin-clouds.yaml':
username => $username, username => $username,
password => $password, password => $password,
auth_url => $auth_url_real, auth_url => $auth_url_real,
project_name => $project_name, project_name => $project_name,
system_scope => 'all',
region_name => $region, region_name => $region,
interface => $interface, interface => $interface,
} }
Anchor['keystone::config::begin']
-> Openstacklib::Clouds['/etc/openstack/puppet/admin-clouds.yaml']
-> Anchor['keystone::config::end']
} }

View File

@ -0,0 +1,23 @@
---
upgrades:
- |
Now the following resource types require system scope credential instead
of project scope credential when sending requests to Keystone API.
- ``keystone_domain``
- ``keystone_endpoint``
- ``keystone_identity_provider``
- ``keystone_role``
- ``keystone_service``
- ``keystone_tenant``
- ``keystone_user_role``
- ``keystone_user``
- |
The ``/etc/keystone/puppet.conf`` file has been replaced by
the ``/etc/openstack/puppet/admin-clouds.yaml`` file.
deprecations:
- |
The ``keystone_puppet_config`` resource type has been deprecated and will
be removed in a future release.

View File

@ -71,20 +71,31 @@ describe 'keystone::bootstrap' do
)} )}
it { is_expected.to contain_file('/etc/keystone/puppet.conf').with( it { is_expected.to contain_file('/etc/keystone/puppet.conf').with(
:ensure => 'present', :ensure => 'absent',
:replace => false, :require => 'Anchor[keystone::config::begin]',
:content => '', :before => 'Anchor[keystone::config::end]',
:owner => 'root',
:group => 'root',
:mode => '0600',
:require => 'Anchor[keystone::install::end]',
)} )}
it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( it { is_expected.to contain_file('/etc/openstack').with(
:ensure => 'directory',
:mode => '0755',
:owner => 'root',
:group => 'root',
)}
it { is_expected.to contain_file('/etc/openstack/puppet').with(
:ensure => 'directory',
:mode => '0755',
:owner => 'root',
:group => 'root',
)}
it { is_expected.to contain_openstacklib__clouds('/etc/openstack/puppet/admin-clouds.yaml').with(
:username => 'admin', :username => 'admin',
:password => 'secret', :password => 'secret',
:auth_url => 'http://127.0.0.1:5000', :auth_url => 'http://127.0.0.1:5000',
:project_name => 'admin', :project_name => 'admin',
:system_scope => 'all',
:region_name => 'RegionOne', :region_name => 'RegionOne',
:interface => 'public', :interface => 'public',
)} )}
@ -170,21 +181,32 @@ describe 'keystone::bootstrap' do
)} )}
it { is_expected.to contain_file('/etc/keystone/puppet.conf').with( it { is_expected.to contain_file('/etc/keystone/puppet.conf').with(
:ensure => 'present', :ensure => 'absent',
:replace => false, :require => 'Anchor[keystone::config::begin]',
:content => '', :before => 'Anchor[keystone::config::end]',
:owner => 'root',
:group => 'root',
:mode => '0600',
:require => 'Anchor[keystone::install::end]',
)} )}
it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( it { is_expected.to contain_file('/etc/openstack').with(
:ensure => 'directory',
:mode => '0755',
:owner => 'root',
:group => 'root',
)}
it { is_expected.to contain_file('/etc/openstack/puppet').with(
:ensure => 'directory',
:mode => '0755',
:owner => 'root',
:group => 'root',
)}
it { is_expected.to contain_openstacklib__clouds('/etc/openstack/puppet/admin-clouds.yaml').with(
:username => 'user', :username => 'user',
:password => 'secret', :password => 'secret',
:auth_url => 'http://admin:1234', :auth_url => 'http://admin:1234',
:project_name => 'adminproj', :project_name => 'adminproj',
:region_name => 'RegionTwo', :region_name => 'RegionTwo',
:system_scope => 'all',
:interface => 'admin', :interface => 'admin',
)} )}
end end
@ -210,20 +232,31 @@ describe 'keystone::bootstrap' do
it { is_expected.to_not contain_keystone_endpoint('RegionOne/keystone::identity') } it { is_expected.to_not contain_keystone_endpoint('RegionOne/keystone::identity') }
it { is_expected.to contain_file('/etc/keystone/puppet.conf').with( it { is_expected.to contain_file('/etc/keystone/puppet.conf').with(
:ensure => 'present', :ensure => 'absent',
:replace => false, :require => 'Anchor[keystone::config::begin]',
:content => '', :before => 'Anchor[keystone::config::end]',
:owner => 'root',
:group => 'root',
:mode => '0600',
:require => 'Anchor[keystone::install::end]',
)} )}
it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( it { is_expected.to contain_file('/etc/openstack').with(
:ensure => 'directory',
:mode => '0755',
:owner => 'root',
:group => 'root',
)}
it { is_expected.to contain_file('/etc/openstack/puppet').with(
:ensure => 'directory',
:mode => '0755',
:owner => 'root',
:group => 'root',
)}
it { is_expected.to contain_openstacklib__clouds('/etc/openstack/puppet/admin-clouds.yaml').with(
:username => 'admin', :username => 'admin',
:password => 'secret', :password => 'secret',
:auth_url => 'http://127.0.0.1:5000', :auth_url => 'http://127.0.0.1:5000',
:project_name => 'admin', :project_name => 'admin',
:system_scope => 'all',
:region_name => 'RegionOne', :region_name => 'RegionOne',
:interface => 'public', :interface => 'public',
)} )}
@ -254,7 +287,7 @@ describe 'keystone::bootstrap' do
} }
end end
it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( it { is_expected.to contain_openstacklib__clouds('/etc/openstack/puppet/admin-clouds.yaml').with(
:auth_url => 'http://internal:1234', :auth_url => 'http://internal:1234',
:interface => 'internal', :interface => 'internal',
)} )}

View File

@ -30,9 +30,6 @@ at_exit { RSpec::Puppet::Coverage.report! }
def setup_provider_tests def setup_provider_tests
Puppet::Provider::Keystone.class_exec do Puppet::Provider::Keystone.class_exec do
def self.reset def self.reset
@public_endpoint = nil
@tenant_hash = nil
@keystone_file = nil
Puppet::Provider::Keystone.class_variable_set('@@default_domain_id', nil) Puppet::Provider::Keystone.class_variable_set('@@default_domain_id', nil)
@domain_hash = nil @domain_hash = nil
@users_name = nil @users_name = nil

View File

@ -9,8 +9,8 @@ describe Puppet::Type.type(:keystone_domain).provider(:openstack) do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v2.0' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
end end
describe 'when managing a domain' do describe 'when managing a domain' do

View File

@ -7,7 +7,7 @@ describe Puppet::Type.type(:keystone_endpoint).provider(:openstack) do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
end end

View File

@ -6,7 +6,7 @@ describe Puppet::Type.type(:keystone_identity_provider).provider(:openstack) do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end

View File

@ -9,7 +9,7 @@ describe provider_class do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
end end

View File

@ -9,7 +9,7 @@ describe provider_class do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end

View File

@ -15,7 +15,7 @@ describe Puppet::Provider::Keystone do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end
@ -66,7 +66,7 @@ id="newid"
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end
@ -98,7 +98,7 @@ id="the_project_id"
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end
@ -127,14 +127,6 @@ id="the_user_id"
end end
describe '#get_auth_url' do describe '#get_auth_url' do
it 'should raise when OS_AUTH_URL is no defined in either the environment or the openrc file and there is no keystone puppet config file' do
home = ENV['HOME']
ENV.clear
File.expects(:exists?).with("#{home}/openrc").returns(false)
File.expects(:exists?).with('/root/openrc').returns(false)
expect { klass.get_auth_url }.to raise_error(Puppet::Error, "File: /etc/keystone/puppet.conf does not contain all required configuration keys. Cannot authenticate to Keystone.")
end
it 'should return the OS_AUTH_URL from the environment' do it 'should return the OS_AUTH_URL from the environment' do
ENV.clear ENV.clear
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5001' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5001'

View File

@ -54,7 +54,7 @@ describe provider_class do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end

View File

@ -10,7 +10,7 @@ describe Puppet::Type.type(:keystone_user).provider(:openstack) do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
end end

View File

@ -9,7 +9,7 @@ describe Puppet::Type.type(:keystone_user_role).provider(:openstack) do
let(:set_env) do let(:set_env) do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
end end