diff --git a/README.md b/README.md index 28deca5f4..71d58feea 100644 --- a/README.md +++ b/README.md @@ -53,21 +53,14 @@ To utilize the keystone module's functionality you will need to declare multiple ```puppet class { 'keystone': catalog_type => 'sql', - admin_token => 'random_uuid', database_connection => 'mysql://keystone_admin:super_secret_db_password@openstack-controller.example.com/keystone', } -# Adds the admin credential to keystone. -class { 'keystone::roles::admin': - email => 'admin@example.com', +class { 'keystone::bootstrap': password => 'super_secret', -} - -# Installs the service user endpoint. -class { 'keystone::endpoint': - public_url => 'http://10.16.0.101:5000/v2.0', - admin_url => 'http://10.16.1.101:5000/v2.0', - internal_url => 'http://10.16.2.101:5000/v2.0', + public_url => 'http://10.16.0.101:5000', + admin_url => 'http://10.16.1.101:5000', + internal_url => 'http://10.16.2.101:5000', region => 'example-1', } ``` diff --git a/examples/apache_dropin.pp b/examples/apache_dropin.pp index b0880943c..a1a683bcc 100644 --- a/examples/apache_dropin.pp +++ b/examples/apache_dropin.pp @@ -28,16 +28,12 @@ class { 'keystone': debug => true, database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone', catalog_type => 'sql', - admin_token => 'admin_token', enabled => false, } -class { 'keystone::roles::admin': - email => 'test@puppetlabs.com', - password => 'ChangeMe', -} -class { 'keystone::endpoint': - public_url => "https://${::fqdn}:5000/", - admin_url => "https://${::fqdn}:5000/", +class { 'keystone::bootstrap': + password => 'ChangeMe', + public_url => "https://${::fqdn}:5000", + admin_url => "https://${::fqdn}:5000", } keystone_config { 'ssl/enable': value => true } diff --git a/examples/apache_with_paths.pp b/examples/apache_with_paths.pp index 7245cbd9f..5e23ccf01 100644 --- a/examples/apache_with_paths.pp +++ b/examples/apache_with_paths.pp @@ -28,17 +28,13 @@ class { 'keystone': debug => true, database_connection => 'mysql://keystone_admin:keystone@127.0.0.1/keystone', catalog_type => 'sql', - admin_token => 'admin_token', enabled => true, } class { 'keystone::cron::token_flush': } -class { 'keystone::roles::admin': - email => 'test@puppetlabs.com', - password => 'ChangeMe', -} -class { 'keystone::endpoint': - public_url => "https://${::fqdn}:443/main/", - admin_url => "https://${::fqdn}:443/admin/", +class { 'keystone::bootstrap': + password => 'ChangeMe', + public_url => "https://${::fqdn}:443/main", + admin_url => "https://${::fqdn}:443/admin", } keystone_config { 'ssl/enable': ensure => absent } diff --git a/examples/k2k_sp_shib.pp b/examples/k2k_sp_shib.pp index e1fd623e4..ccdd457dd 100644 --- a/examples/k2k_sp_shib.pp +++ b/examples/k2k_sp_shib.pp @@ -45,18 +45,13 @@ class { 'keystone': debug => true, database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone', catalog_type => 'sql', - admin_token => 'admin_token', enabled => false, } -class { 'keystone::roles::admin': - email => 'test@puppetlabs.com', - password => 'ChangeMe', -} - -class { 'keystone::endpoint': - public_url => "https://${::fqdn}:5000/", - admin_url => "https://${::fqdn}:5000/", +class { 'keystone::bootstrap': + password => 'ChangeMe', + public_url => "https://${::fqdn}:5000", + admin_url => "https://${::fqdn}:5000", } keystone_config { 'ssl/enable': value => true } diff --git a/examples/ldap_backend.pp b/examples/ldap_backend.pp index 14cb7aded..9544fbd39 100644 --- a/examples/ldap_backend.pp +++ b/examples/ldap_backend.pp @@ -1,7 +1,6 @@ class { 'keystone': debug => true, database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone', - admin_token => 'admin_token', enabled => true, # helper for using domains using_domain_config => true @@ -9,19 +8,13 @@ class { 'keystone': # Ensure this matches what is in LDAP or keystone will try to recreate # the admin user -class { 'keystone::roles::admin': - email => 'test@example.com', - password => 'ChangeMe', - admin_user_domain => 'domain_1', - admin_project_domain => 'domain_1', - service_project_domain => 'domain_1', +class { 'keystone::bootstrap': + password => 'ChangeMe', } -# Waiting to have keystone::roles::admin being a define instead of a -# class to make the admin for domain_2. keystone_domain { 'domain_2': ensure => present } -keystone::ldap_backend { 'domain_1': +keystone::ldap_backend { 'Default': url => 'ldap://ldap.example.com:389', user => 'uid=bind,cn=users,cn=accounts,dc=example,dc=com', password => 'SecretPass', diff --git a/examples/ldap_full.pp b/examples/ldap_full.pp index f9e74c1a7..339b3414f 100644 --- a/examples/ldap_full.pp +++ b/examples/ldap_full.pp @@ -3,8 +3,7 @@ # Ensure this matches what is in LDAP or keystone will try to recreate # the admin user -class { 'keystone::roles::admin': - email => 'test@example.com', +class { 'keystone::bootstrap': password => 'ChangeMe', } diff --git a/examples/ldap_identity.pp b/examples/ldap_identity.pp index 9b282be7f..66e698316 100644 --- a/examples/ldap_identity.pp +++ b/examples/ldap_identity.pp @@ -3,8 +3,7 @@ # Ensure this matches what is in LDAP or keystone will try to recreate # the admin user -class { 'keystone::roles::admin': - email => 'test@example.com', +class { 'keystone::bootstrap': password => 'ChangeMe', } diff --git a/examples/v3_basic.pp b/examples/v3_basic.pp index 5c5326e6c..06600c0d1 100644 --- a/examples/v3_basic.pp +++ b/examples/v3_basic.pp @@ -1,24 +1,3 @@ -# Example using v3 domains. The admin user is created in the domain -# named 'admin_domain', and assigned the role 'admin' in the 'admin' -# project in the domain 'admin_domain'. The keystone service account is -# created in default domain, and assigned the -# role 'admin' in the project 'services' in the default domain. -# NOTE: Until all of the other services support using Keystone v3 -# with keystone_authtoken middleware that supports v3, they cannot -# specify a domain for authentication, and so have to be in the -# default domain. -# -# To be sure everything is working, run: -# $ export OS_IDENTITY_API_VERSION=3 -# $ export OS_USERNAME=admin -# $ export OS_USER_DOMAIN_NAME=admin_domain -# $ export OS_PASSWORD=ChangeMe -# $ export OS_PROJECT_NAME=admin -# $ export OS_PROJECT_DOMAIN_NAME=admin_domain -# $ export OS_AUTH_URL=http://keystone.local:5000/v3 -# $ openstack user list -# - Exec { logoutput => 'on_failure' } @@ -29,18 +8,8 @@ class { 'keystone::db::mysql': class { 'keystone': debug => true, database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone', - admin_token => 'admin_token', enabled => true, } -class { 'keystone::roles::admin': - email => 'test@example.tld', - password => 'a_big_secret', - admin => 'admin', # username - admin_tenant => 'admin', # project name - admin_user_domain => 'admin', # domain for user - admin_tenant_domain => 'admin', # domain for project -} -class { 'keystone::endpoint': - public_url => 'http://127.0.0.1:5000/', - admin_url => 'http://127.0.0.1:5000/', +class { 'keystone::bootstrap': + password => 'a_big_secret', } diff --git a/examples/v3_domain_configuration.pp b/examples/v3_domain_configuration.pp index 24042b4c6..3d340ad17 100644 --- a/examples/v3_domain_configuration.pp +++ b/examples/v3_domain_configuration.pp @@ -1,8 +1,3 @@ -# Example using v3 domain configuration. This setup a directory where -# the domain configurations will be and adjust the keystone. -# For the rest of the configuration check v3_basic.pp. -# - Exec { logoutput => 'on_failure' } class { 'mysql::server': } @@ -12,18 +7,14 @@ class { 'keystone::db::mysql': class { 'keystone': debug => true, database_connection => 'mysql://keystone:keystone@192.168.1.1/keystone', - admin_token => 'admin_token', enabled => true, # The domain configuration setup at keystone level using_domain_config => true, } -class { 'keystone::roles::admin': - email => 'test@example.tld', - password => 'a_big_secret', -} -class { 'keystone::endpoint': - public_url => 'http://192.168.1.1:5000/', - admin_url => 'http://192.168.1.1:5000/', +class { 'keystone::bootstrap': + password => 'a_big_secret', + public_url => 'http://192.168.1.1:5000', + admin_url => 'http://192.168.1.1:5000', } # Creates the /etc/keystone/domains/keystone.my_domain.conf file and diff --git a/lib/puppet/provider/keystone.rb b/lib/puppet/provider/keystone.rb index cc30a56da..9911b6e5a 100644 --- a/lib/puppet/provider/keystone.rb +++ b/lib/puppet/provider/keystone.rb @@ -13,12 +13,60 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack @@default_domain_id = nil - def self.public_endpoint - @public_endpoint ||= get_public_endpoint + def self.conf_filename + '/etc/keystone/puppet.conf' end - def self.admin_token - @admin_token ||= get_admin_token + 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 + if conf and conf['keystone_authtoken'] and + auth_keys.all?{|k| !conf['keystone_authtoken'][k].nil?} + creds = Hash[ auth_keys.map \ + { |k| [k, conf['keystone_authtoken'][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['keystone_authtoken']['region_name'] + creds['region_name'] = conf['keystone_authtoken']['region_name'] + 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 + q = keystone_puppet_credentials + "#{q['auth_url']}" + end + + def self.auth_endpoint + @auth_endpoint ||= get_auth_endpoint end def self.default_domain_from_ini_file @@ -152,50 +200,17 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack raise e unless e.message =~ /No user with a name or ID/ end - def self.get_public_endpoint - endpoint = nil - if url = get_section('DEFAULT', 'public_endpoint') - endpoint = url.chomp('/') - end - return endpoint - end - - def self.get_admin_token - get_section('DEFAULT', 'admin_token') - end - def self.get_auth_url auth_url = nil if ENV['OS_AUTH_URL'] auth_url = ENV['OS_AUTH_URL'].dup elsif auth_url = get_os_vars_from_rcfile(rc_filename)['OS_AUTH_URL'] else - auth_url = public_endpoint + auth_url = auth_endpoint end return auth_url end - def self.get_section(group, name) - if keystone_file && keystone_file[group] && keystone_file[group][name] - return keystone_file[group][name].strip - end - return nil - end - - def self.get_service_url - service_url = nil - if ENV['OS_ENDPOINT'] - service_url = ENV['OS_ENDPOINT'].dup - # Compatibility with pre-4.0.0 openstackclient - elsif ENV['OS_URL'] - service_url = ENV['OS_URL'].dup - elsif public_endpoint - service_url = public_endpoint - service_url << "/v#{@credentials.version}" - end - return service_url - end - def self.ini_filename INI_FILENAME end @@ -212,27 +227,32 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack def self.request(service, action, properties=nil, options={}) super rescue Puppet::Error::OpenstackAuthInputError, Puppet::Error::OpenstackUnauthorizedError => error - request_by_service_token(service, action, error, properties, options=options) + keystone_request(service, action, error, properties) end - def self.request_by_service_token(service, action, error, properties=nil, options={}) + def self.keystone_request(service, action, error, properties=nil) properties ||= [] - @credentials.token = admin_token - @credentials.endpoint = service_url - raise error unless @credentials.service_token_set? + @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? begin - Puppet::Provider::Openstack.request(service, action, properties, @credentials, options) + Puppet::Provider::Openstack.request(service, action, properties, @credentials) rescue Puppet::ExecutionFailure, Puppet::Error::OpenstackUnauthorizedError # openstackclient < 4.0.0 does not support --os-endpoint and requires --os-url - @credentials.url = service_url - Puppet::Provider::Openstack.request(service, action, properties, @credentials, options) + @credentials.url = auth_endpoint + Puppet::Provider::Openstack.request(service, action, properties, @credentials) end end - def self.service_url - @service_url ||= get_service_url - end - def self.set_domain_for_name(name, domain_name) if domain_name.nil? || domain_name.empty? raise(Puppet::Error, "Missing domain name for resource #{name}") diff --git a/lib/puppet/provider/keystone_puppet_config/ini_setting.rb b/lib/puppet/provider/keystone_puppet_config/ini_setting.rb new file mode 100644 index 000000000..f3405bcc2 --- /dev/null +++ b/lib/puppet/provider/keystone_puppet_config/ini_setting.rb @@ -0,0 +1,8 @@ +Puppet::Type.type(:keystone_puppet_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:openstack_config).provider(:ini_setting) +) do + def self.file_path + '/etc/keystone/puppet.conf' + end +end diff --git a/lib/puppet/type/keystone_puppet_config.rb b/lib/puppet/type/keystone_puppet_config.rb new file mode 100644 index 000000000..21afb1425 --- /dev/null +++ b/lib/puppet/type/keystone_puppet_config.rb @@ -0,0 +1,12 @@ +Puppet::Type.newtype(:keystone_puppet_config) do + require File.expand_path(File.join( + File.dirname(__FILE__), '..', '..', + 'puppet_x', 'keystone_config', 'ini_setting')) + extend PuppetX::KeystoneConfig::IniSetting + + create_parameters + + autorequire(:file) do + ['/etc/keystone/puppet.conf'] + end +end diff --git a/manifests/bootstrap.pp b/manifests/bootstrap.pp new file mode 100644 index 000000000..3a8cf7e05 --- /dev/null +++ b/manifests/bootstrap.pp @@ -0,0 +1,307 @@ +# == Class: keystone::bootstrap +# +# Bootstrap keystone with keystone-manage bootstrap. +# +# === Parameters +# +# [*password*] +# (Optional) The password for the user. +# WARNING: This parameter will be required in a future release. +# Defaults to undef +# +# [*username*] +# (Optional) The username. +# Defaults to 'admin' +# +# [*email*] +# (Optional) The email for the user. +# Defaults to 'admin@localhost' +# +# [*project_name*] +# (Optional) The project name. +# Defaults to 'admin' +# +# [*service_project_name*] +# (Optional) The service project name. +# Defaults to 'services' +# +# [*role_name*] +# (Optional) The role name. +# Defaults to 'admin' +# +# [*service_name*] +# (Optional) The service name. +# Defaults to 'keystone' +# +# [*admin_url*] +# (Optional) Admin URL for Keystone endpoint. +# This url should *not* contain any version or trailing '/'. +# Defaults to 'http://127.0.0.1:5000' +# +# [*public_url*] +# (Optional) Public URL for Keystone endpoint. +# This URL should *not* contain any version or trailing '/'. +# Defaults to 'http://127.0.0.1:5000' +# +# [*internal_url*] +# (Optional) Internal URL for Keystone endpoint. +# This URL should *not* contain any version or trailing '/'. +# Defaults to $public_url +# +# [*region*] +# (Optional) Region for endpoint. +# Defaults to 'RegionOne' +# +# [*interface*] +# (Optional) Which interface endpoint should be used. +# Defaults to 'public' +# +class keystone::bootstrap ( + # TODO(tobias-urdin): Make the password required when compat is removed. + $password = undef, + $username = 'admin', + $email = 'admin@localhost', + $project_name = 'admin', + $service_project_name = 'services', + $role_name = 'admin', + $service_name = 'keystone', + $admin_url = 'http://127.0.0.1:5000', + $public_url = 'http://127.0.0.1:5000', + $internal_url = undef, + $region = 'RegionOne', + $interface = 'public', +) inherits keystone::params { + + include ::keystone::deps + + # TODO(tobias-urdin): Remove compat layer. + if $password == undef { + if defined('$::keystone::admin_password') and $::keystone::admin_password != undef { + $password_real = $::keystone::admin_password + warning('Using deprecated keystone::admin_password as admin password') + # Check if we differ from the roles admin pw + if defined('$::keystone::roles::admin::password') and $::keystone::roles::admin::password != $password_real { + warning('The keystone::admin_password and keystone::roles::admin::password differs and will cause a flip-flopping\ + behaviour and authentication issues for the admin user.') + } + } elsif defined('$::keystone::admin_token') and $::keystone::admin_token != undef { + $password_real = $::keystone::admin_token + warning('Using deprecated keystone::admin_token as admin password') + # Check if we differ from the roles admin pw + if defined('$::keystone::roles::admin::password') and $::keystone::roles::admin::password != $password_real { + warning('The keystone::admin_token and keystone::roles::admin::password differs and will cause a flip-flopping\ + behaviour and authentication issues for the admin user.') + } + } else { + # Check the keystone::roles::admin class as well. + if defined('$::keystone::roles::admin::password') and $::keystone::roles::admin::password != undef { + $password_real = $::keystone::roles::admin::password + warning('Using deprecated keystone::roles::admin::password as admin password') + } else { + fail('keystone::bootstrap::password is undef, could not resolve a password') + } + } + } else { + $password_real = $password + } + if defined('$::keystone::endpoint::public_url') and $::keystone::endpoint::public_url != undef { + $public_url_real = $::keystone::endpoint::public_url + $using_deprecated_public_url = true + warning('Using deprecated keystone::endpoint::public_url, please update to using keystone::bootstrap') + } else { + $public_url_real = $public_url + $using_deprecated_public_url = false + } + if defined('$::keystone::endpoint::internal_url') and $::keystone::endpoint::internal_url != undef { + $internal_url_final = $::keystone::endpoint::internal_url + $using_deprecated_internal_url = true + warning('Using deprecated keystone::endpoint::internal_url, please update to using keystone::bootstrap') + } else { + $internal_url_final = $internal_url + $using_deprecated_internal_url = false + } + if defined('$::keystone::endpoint::admin_url') and $::keystone::endpoint::admin_url != undef { + $admin_url_real = $::keystone::endpoint::admin_url + warning('Using deprecated keystone::endpoint::admin_url, please update to using keystone::bootstrap') + } else { + $admin_url_real = $admin_url + } + if defined('$::keystone::endpoint::region') and $::keystone::endpoint::region != undef { + $region_real = $::keystone::endpoint::region + warning('Using deprecated keystone::endpoint::region, please update to using keystone::bootstrap') + } else { + $region_real = $region + } + if !$using_deprecated_internal_url and $internal_url == undef and $using_deprecated_public_url { + warning('Using deprecated keystone::endpoint::public_url for keystone::bootstrap::internal_url') + } + if defined('$::keystone::roles::admin::admin') and $::keystone::roles::admin::admin != undef { + $username_real = $::keystone::roles::admin::admin + if $username_real != $username and $username == 'admin' { + warning('Using keystone::roles::admin::admin as username, the keystone::bootstrap::username default is different\ + dont forget to set that later') + } + } else { + $username_real = $username + } + if defined('$::keystone::roles::admin::email') and $::keystone::roles::admin::email != undef { + $email_real = $::keystone::roles::admin::email + if $email_real != $email and $email == 'admin@localhost' { + warning('Using keystone::roles::admin::email as email, the keystone::bootstrap::email default is different\ + dont forget to set that later') + } + } else { + $email_real = $email + } + if defined('$::keystone::roles::admin::admin_roles') and $::keystone::roles::admin::admin_roles != undef { + $role_name_real = $::keystone::roles::admin::admin_roles + warning("Using keystone::roles::admin::admin_roles with value ${role_name_real} note that the\ + keystone::bootstrap when used will only set a single role, by default the 'admin' role.") + warning('Will use the first value in admin_roles for bootstrap and all (if multiple) for all other resources!') + if is_array($role_name_real) { + $bootstrap_role_name = $role_name_real[0] + } else { + $bootstrap_role_name = $role_name_real + } + } else { + $role_name_real = [$role_name] + $bootstrap_role_name = $role_name + } + if defined('$::keystone::roles::admin::admin_tenant') { + $admin_tenant = $::keystone::roles::admin::admin_tenant + if ($admin_tenant == undef or $admin_tenant == 'openstack') { + # Try to keep the backward compatible creation of the openstack project. + # We still create the 'admin' project with the bootstrap process below. + # This is a best effort, we still ignore the description and default domain. + ensure_resource('keystone_tenant', 'openstack', { + 'ensure' => 'present', + 'enabled' => true, + }) + ensure_resource('keystone_user_role', "${username_real}@openstack", { + 'ensure' => 'present', + 'roles' => $role_name_real, + }) + + # Use the default value so we create the "admin" project + $project_name_real = $project_name + } else { + warning('Using keystone::roles::admin::admin_tenant as project name for admin') + $project_name_real = $admin_tenant + } + } else { + $project_name_real = $project_name + } + if defined('$::keystone::roles::admin::service_tenant') and $::keystone::roles::admin::service_tenant != undef { + warning('Using keystone::roles::admin::service_tenant as service project name') + $service_project_name_real = $::keystone::roles::admin::service_tenant + } else { + $service_project_name_real = $service_project_name + } + # Compat code ends here. + + $internal_url_real = $internal_url_final ? { + undef => $public_url_real, + default => $internal_url_final + } + + if defined('$::keystone::keystone_user') { + $keystone_user = $::keystone::keystone_user + } else { + $keystone_user = $::keystone::params::keystone_user + } + + # The initial bootstrap that creates all resources required but + # only subscribes to notifies from the keystone::dbsync::end anchor + # which means this is not guaranteed to execute on each run. + exec { 'keystone bootstrap': + command => 'keystone-manage bootstrap', + environment => [ + "OS_BOOTSTRAP_USERNAME=${username_real}", + "OS_BOOTSTRAP_PASSWORD=${password_real}", + "OS_BOOTSTRAP_PROJECT_NAME=${project_name_real}", + "OS_BOOTSTRAP_ROLE_NAME=${bootstrap_role_name}", + "OS_BOOTSTRAP_SERVICE_NAME=${service_name}", + "OS_BOOTSTRAP_ADMIN_URL=${admin_url_real}", + "OS_BOOTSTRAP_PUBLIC_URL=${public_url_real}", + "OS_BOOTSTRAP_INTERNAL_URL=${internal_url_real}", + "OS_BOOTSTRAP_REGION_ID=${region_real}", + ], + user => $keystone_user, + path => '/usr/bin', + refreshonly => true, + subscribe => Anchor['keystone::dbsync::end'], + notify => Anchor['keystone::service::begin'], + tag => 'keystone-bootstrap', + } + + # Since the bootstrap is not guaranteed to execute on each run we + # use the below resources to make sure the current resources are + # correct so if some value was updated we set that. + + ensure_resource('keystone_role', $role_name_real, { + 'ensure' => 'present', + }) + + ensure_resource('keystone_user', $username_real, { + 'ensure' => 'present', + 'enabled' => true, + 'email' => $email_real, + 'password' => $password_real, + }) + + ensure_resource('keystone_tenant', $service_project_name_real, { + 'ensure' => 'present', + 'enabled' => true, + }) + + ensure_resource('keystone_tenant', $project_name_real, { + 'ensure' => 'present', + 'enabled' => true, + }) + + ensure_resource('keystone_user_role', "${username_real}@${project_name_real}", { + 'ensure' => 'present', + 'roles' => $role_name_real, + }) + + ensure_resource('keystone_service', "${service_name}::identity", { + 'ensure' => 'present', + }) + + ensure_resource('keystone_endpoint', "${region_real}/${service_name}::identity", { + 'ensure' => 'present', + 'public_url' => $public_url_real, + 'admin_url' => $admin_url_real, + 'internal_url' => $internal_url_real, + }) + + # The below creates and populates the /etc/keystone/puppet.conf file that contains + # the credentials that can be loaded by providers. Ensure it has the proper owner, + # group and mode so that it cannot be read by anything other than root. + file { '/etc/keystone/puppet.conf': + ensure => 'present', + replace => false, + content => '', + owner => 'root', + group => 'root', + mode => '0600', + require => Anchor['keystone::install::end'], + } + + if $interface == 'admin' { + $auth_url_real = $admin_url_real + } elsif $interface == 'internal' { + $auth_url_real = $internal_url_real + } else { + $auth_url_real = $public_url_real + } + + keystone::resource::authtoken { 'keystone_puppet_config': + username => $username_real, + password => $password_real, + auth_url => $auth_url_real, + project_name => $project_name_real, + region_name => $region_real, + interface => $interface, + } +} diff --git a/manifests/endpoint.pp b/manifests/endpoint.pp index b4d7bfbf0..d5a2e1eb1 100644 --- a/manifests/endpoint.pp +++ b/manifests/endpoint.pp @@ -1,26 +1,29 @@ # == Class: keystone::endpoint # +# DEPRECATED! +# # Creates the auth endpoints for keystone # # === Parameters # # [*public_url*] # (optional) Public url for keystone endpoint. -# Defaults to 'http://127.0.0.1:5000' +# Defaults to undef # This url should *not* contain any version or trailing '/'. # # [*internal_url*] # (optional) Internal url for keystone endpoint. -# Defaults to $public_url +# Defaults to undef # This url should *not* contain any version or trailing '/'. # # [*admin_url*] # (optional) Admin url for keystone endpoint. -# Defaults to 'http://127.0.0.1:5000' +# Defaults to undef # This url should *not* contain any version or trailing '/'. # # [*region*] -# (optional) Region for endpoint. (Defaults to 'RegionOne') +# (optional) Region for endpoint. +# Defaults to undef # # [*user_domain*] # (Optional) Domain for $auth_name @@ -38,10 +41,7 @@ # # [*version*] # (optional) API version for endpoint. -# Defaults to ''. Valid values are 'v2.0', 'v3', or the empty string ''. -# If the version is set to the empty string (''), then it won't be -# used. This is the expected behaviour since Keystone V3 handles API versions -# from the context. +# Defaults to undef. # # === Examples # @@ -52,59 +52,23 @@ # } # class keystone::endpoint ( - $public_url = 'http://127.0.0.1:5000', + $public_url = undef, $internal_url = undef, - $admin_url = 'http://127.0.0.1:5000', - $region = 'RegionOne', + $admin_url = undef, + $region = undef, $user_domain = undef, $project_domain = undef, $default_domain = undef, - $version = '', + $version = undef, ) { - include keystone::deps + warning('The keystone::endpoint class has been replaced with keystone::bootstrap class\ + will try to use the backward compatible approach') - if $version == 'unset' { - warning('keystone::endpoint::version parameter is deprecated and will be removed in a future release.') - $_version = 'v2.0' - } else { - $_version = $version - } - if empty($_version) { - $admin_url_real = $admin_url - $public_url_real = $public_url - - if $internal_url { - $internal_url_real = $internal_url - } - else { - $internal_url_real = $public_url - } - } - else { - $public_url_real = "${public_url}/${_version}" - $admin_url_real = "${admin_url}/${_version}" - - if $internal_url { - $internal_url_real = "${internal_url}/${_version}" - } - else { - $internal_url_real = "${public_url}/${_version}" - } + if !defined('$::keystone::roles::admin::admin_tenant') { + fail('You are using the backward compatible approach instead of keystone::bootstrap\ + you need to ensure that keystone::roles::admin is defined BEFORE keystone::endpoint in your manifest') } - keystone::resource::service_identity { 'keystone': - configure_user => false, - configure_user_role => false, - service_type => 'identity', - service_description => 'OpenStack Identity Service', - public_url => $public_url_real, - admin_url => $admin_url_real, - internal_url => $internal_url_real, - region => $region, - user_domain => $user_domain, - project_domain => $project_domain, - default_domain => $default_domain, - } - Keystone::Resource::Service_identity['keystone'] -> File<| tag == 'openrc' |> + include keystone::bootstrap } diff --git a/manifests/init.pp b/manifests/init.pp index aeac06673..8406c1053 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -13,18 +13,6 @@ # accepts latest or specific versions. # Defaults to present. # -# [*admin_token*] -# Admin token that can be used to authenticate as a keystone -# admin. This is not the password for the admin user -# in the Keystone database. This is a token that bypasses authentication. -# The admin_token has been deprecated by the Keystone service and this -# will be deprecated in a future changeset. Required. -# -# [*admin_password*] -# Keystone password for the admin user. This is not the admin_token. -# This is the password that the admin user signs into keystone with. -# Required. -# # [*catalog_type*] # (Optional) Type of catalog that keystone uses to store endpoints,services. # Defaults to sql. (Also accepts template) @@ -403,12 +391,6 @@ # Otherwise Puppet will manage keys with File resource. # Defaults to false # -# [*enable_bootstrap*] -# (Optional) Enable keystone bootstrapping. -# This option to true will automatically bootstrap the default domain -# user by running 'keystone-manage bootstrap'. -# Defaults to true -# # [*default_domain*] # (Optional) When Keystone v3 support is enabled, v2 clients will need # to have a domain assigned for certain operations. For example, @@ -565,25 +547,22 @@ # (Optional) The url to validate keystone against # Defaults to undef # -# == Dependencies -# None +# [*admin_token*] +# Admin token that can be used to authenticate as a keystone +# admin. This is not the password for the admin user +# in the Keystone database. This is a token that bypasses authentication. +# Defaults to undef # -# == Examples +# [*admin_password*] +# Keystone password for the admin user. This is not the admin_token. +# This is the password that the admin user signs into keystone with. +# Defaults to undef # -# class { 'keystone': -# admin_token => 'my_special_token', -# } -# -# OR -# -# class { 'keystone': -# ... -# service_name => 'httpd', -# ... -# } -# class { 'keystone::wsgi::apache': -# ... -# } +# [*enable_bootstrap*] +# (Optional) Enable keystone bootstrapping. +# This option to true will automatically bootstrap the default domain +# user by running 'keystone-manage bootstrap'. +# Defaults to undef # # == Authors # @@ -594,8 +573,6 @@ # Copyright 2012 Puppetlabs Inc, unless otherwise noted. # class keystone( - $admin_token, - $admin_password = undef, $package_ensure = 'present', $client_package_ensure = 'present', $log_dir = undef, @@ -668,7 +645,6 @@ class keystone( $default_domain = undef, $member_role_id = $::os_service_default, $member_role_name = $::os_service_default, - $enable_bootstrap = true, $memcache_dead_retry = $::os_service_default, $memcache_socket_timeout = $::os_service_default, $memcache_pool_maxsize = $::os_service_default, @@ -698,6 +674,9 @@ class keystone( $validate_insecure = undef, $validate_auth_url = undef, $validate_cacert = undef, + $admin_token = undef, + $admin_password = undef, + $enable_bootstrap = undef, ) inherits keystone::params { include keystone::deps @@ -772,14 +751,6 @@ class keystone( $public_endpoint_real = $public_endpoint } - if $admin_password == undef { - warning("admin_password is required, please set admin_password to a value != admin_token. \ -admin_token will be removed in a later release") - $admin_password_real = $admin_token - } else { - $admin_password_real = $admin_password - } - if $manage_policyrcd { # openstacklib policy_rcd only affects debian based systems. Policy_rcd <| title == 'keystone' |> -> Package['keystone'] @@ -813,8 +784,12 @@ admin_token will be removed in a later release") purge => $purge_config, } + # TODO(tobias-urdin): Remove this when admin_token is removed. + keystone_config { + 'DEFAULT/admin_token': ensure => 'absent', secret => true; + } + keystone_config { - 'DEFAULT/admin_token': value => $admin_token, secret => true; 'DEFAULT/member_role_id': value => $member_role_id; 'DEFAULT/member_role_name': value => $member_role_name; } @@ -1105,21 +1080,6 @@ running as a standalone service, or httpd for being run by a httpd server") fail('You must activate domain configuration using "using_domain_config" parameter to keystone class.') } - if $enable_bootstrap { - # this requires the database to be up and running and configured - # and is only run once, so we don't need to notify the service - exec { 'keystone-manage bootstrap': - command => 'keystone-manage bootstrap', - environment => "OS_BOOTSTRAP_PASSWORD=${admin_password_real}", - user => $keystone_user, - path => '/usr/bin', - refreshonly => true, - notify => Anchor['keystone::service::begin'], - subscribe => Anchor['keystone::dbsync::end'], - tag => 'keystone-exec', - } - } - if $using_domain_config { validate_legacy(Stdlib::Absolutepath, 'validate_absolute_path', $domain_config_directory) diff --git a/manifests/roles/admin.pp b/manifests/roles/admin.pp index 7bc5f51e2..1a6cf87dd 100644 --- a/manifests/roles/admin.pp +++ b/manifests/roles/admin.pp @@ -1,5 +1,7 @@ # == Class: keystone::roles::admin # +# DEPRECATED! +# # This class implements some reasonable admin defaults for keystone. # # It creates the following keystone objects: @@ -14,14 +16,15 @@ # [*password*] # The admin password. Required. In a later release # this will default to $keystone::admin_password. +# Defaults to undef # # [*email*] # The email address for the admin. Optional. -# Defaults to 'admin@localhost'. +# Defaults to undef # # [*admin_roles*] # The list of the roles with admin privileges. Optional. -# Defaults to ['admin']. +# Defaults to undef # # [*admin_tenant*] # The name of the tenant to be used for admin privileges. Optional. @@ -29,27 +32,27 @@ # # [*service_tenant*] # The name of service keystone tenant. Optional. -# Defaults to 'services'. +# Defaults to undef # # [*admin*] # Admin user. Optional. -# Defaults to admin. +# Defaults to undef # # [*admin_tenant_desc*] # Optional. Description for admin tenant, -# Defaults to 'admin tenant' +# Defaults to undef # # [*service_tenant_desc*] # Optional. Description for admin tenant, -# Defaults to 'Tenant for the openstack services' +# Defaults to undef # # [*configure_user*] # Optional. Should the admin user be created? -# Defaults to 'true'. +# Defaults to undef # # [*configure_user_role*] # Optional. Should the admin role be configured for the admin user? -# Defaults to 'true'. +# Defaults to undef # # [*admin_user_domain*] # Optional. Domain of the admin user @@ -79,85 +82,21 @@ # Copyright 2012 Puppetlabs Inc, unless otherwise noted. # class keystone::roles::admin( - $password, - $email = 'admin@localhost', - $admin = 'admin', + $password = undef, + $email = undef, + $admin = undef, $admin_tenant = 'openstack', - $admin_roles = ['admin'], - $service_tenant = 'services', - $admin_tenant_desc = 'admin tenant', - $service_tenant_desc = 'Tenant for the openstack services', - $configure_user = true, - $configure_user_role = true, + $admin_roles = undef, + $service_tenant = undef, + $admin_tenant_desc = undef, + $service_tenant_desc = undef, + $configure_user = undef, + $configure_user_role = undef, $admin_user_domain = undef, $admin_project_domain = undef, $service_project_domain = undef, $target_admin_domain = undef, ) { - include keystone::deps - - if $password != $keystone::admin_password_real { - warning('the main class is setting the admin password differently from this\ - class when calling bootstrap. This will lead to the password\ - flip-flopping and cause authentication issues for the admin user.\ - Please ensure that keystone::roles::admin::password and\ - keystone::admin_password are set the same.') - } - - $domains = unique(delete_undef_values([ $admin_user_domain, $admin_project_domain, $service_project_domain, $target_admin_domain])) - keystone_domain { $domains: - ensure => present, - enabled => true, - } - - keystone_tenant { $service_tenant: - ensure => present, - enabled => true, - description => $service_tenant_desc, - domain => $service_project_domain, - } - - keystone_tenant { $admin_tenant: - ensure => present, - enabled => true, - description => $admin_tenant_desc, - domain => $admin_project_domain, - } - - keystone_role { 'admin': - ensure => present, - } - - if $configure_user { - keystone_user { $admin: - ensure => present, - enabled => true, - email => $email, - password => $password, - domain => $admin_user_domain, - } - } - - if $configure_user_role { - keystone_user_role { "${admin}@${admin_tenant}": - ensure => present, - user_domain => $admin_user_domain, - project_domain => $admin_project_domain, - roles => $admin_roles, - } - Keystone_tenant[$admin_tenant] -> Keystone_user_role["${admin}@${admin_tenant}"] - Keystone_user<| title == $admin |> -> Keystone_user_role["${admin}@${admin_tenant}"] - Keystone_user_role["${admin}@${admin_tenant}"] -> File<| tag == 'openrc' |> - - if $target_admin_domain { - keystone_user_role { "${admin}@::${target_admin_domain}": - ensure => present, - user_domain => $admin_user_domain, - roles => $admin_roles, - } - Keystone_user_role["${admin}@::${target_admin_domain}"] -> File<| tag == 'openrc' |> - } - } - + warning('The keystone::roles::admin class has been replaced with keystone::bootstrap class') } diff --git a/releasenotes/notes/add-keystone-bootstrap-class-43e975beae3ec88b.yaml b/releasenotes/notes/add-keystone-bootstrap-class-43e975beae3ec88b.yaml new file mode 100644 index 000000000..04b27af2f --- /dev/null +++ b/releasenotes/notes/add-keystone-bootstrap-class-43e975beae3ec88b.yaml @@ -0,0 +1,19 @@ +--- +features: + - | + Added keystone::bootstrap class. +upgrade: + - | + Now that the keystone::endpoint and keystone::roles::admin classes is deprecated + and has no effect deployments must define the new keystone::bootstrap class with + the proper data that was earlier passed to those classes. Please go through the + parameters in keystone::bootstrap carefully and define the class. + - | + If you are using a multi-domain setup where you previously relied on keystone::endpoint + and/or keystone::roles::admin to create your domains and domain scoped admin accounts + the keystone::bootstrap does not do this and you need to ensure this is managed in your + deployment using the keystone provider resources. +deprecations: + - | + The keystone::endpoint and keystone::roles::admin classes is now deprecated + and has no effect. Please read the upgrade notes carefully! diff --git a/spec/acceptance/keystone_federation_identity_provider_spec.rb b/spec/acceptance/keystone_federation_identity_provider_spec.rb index e74d7dc89..018dc78a3 100644 --- a/spec/acceptance/keystone_federation_identity_provider_spec.rb +++ b/spec/acceptance/keystone_federation_identity_provider_spec.rb @@ -21,7 +21,6 @@ describe 'keystone server running with Apache/WSGI as Identity Provider' do internal_url => 'http://127.0.0.1:1234', } # v3 admin - # we don't use ::keystone::roles::admin but still create resources manually: keystone_domain { 'admin_domain': ensure => present, enabled => true, diff --git a/spec/acceptance/keystone_federation_shibboleth_spec.rb b/spec/acceptance/keystone_federation_shibboleth_spec.rb index bf57f1fe6..e817de700 100644 --- a/spec/acceptance/keystone_federation_shibboleth_spec.rb +++ b/spec/acceptance/keystone_federation_shibboleth_spec.rb @@ -21,7 +21,6 @@ describe 'keystone server running with Apache/WSGI as Service Provider with Shib internal_url => 'http://127.0.0.1:1234', } # v3 admin - # we don't use ::keystone::roles::admin but still create resources manually: keystone_domain { 'admin_domain': ensure => present, enabled => true, diff --git a/spec/acceptance/keystone_wsgi_apache_spec.rb b/spec/acceptance/keystone_wsgi_apache_spec.rb index 0716a4c0b..3f020b89e 100644 --- a/spec/acceptance/keystone_wsgi_apache_spec.rb +++ b/spec/acceptance/keystone_wsgi_apache_spec.rb @@ -21,7 +21,6 @@ describe 'keystone server running with Apache/WSGI with resources' do internal_url => 'http://127.0.0.1:1234', } # v3 admin - # we don't use ::keystone::roles::admin but still create resources manually: keystone_domain { 'admin_domain': ensure => present, enabled => true, diff --git a/spec/classes/keystone_bootstrap_spec.rb b/spec/classes/keystone_bootstrap_spec.rb new file mode 100644 index 000000000..1ee578169 --- /dev/null +++ b/spec/classes/keystone_bootstrap_spec.rb @@ -0,0 +1,229 @@ +require 'spec_helper' + +describe 'keystone::bootstrap' do + shared_examples 'keystone::bootstrap' do + context 'with required parameters' do + let :params do + { + :password => 'secret' + } + end + + it { is_expected.to contain_class('keystone::deps') } + + it { is_expected.to contain_exec('keystone bootstrap').with( + :command => 'keystone-manage bootstrap', + :environment => [ + "OS_BOOTSTRAP_USERNAME=admin", + "OS_BOOTSTRAP_PASSWORD=secret", + "OS_BOOTSTRAP_PROJECT_NAME=admin", + "OS_BOOTSTRAP_ROLE_NAME=admin", + "OS_BOOTSTRAP_SERVICE_NAME=keystone", + "OS_BOOTSTRAP_ADMIN_URL=http://127.0.0.1:5000", + "OS_BOOTSTRAP_PUBLIC_URL=http://127.0.0.1:5000", + "OS_BOOTSTRAP_INTERNAL_URL=http://127.0.0.1:5000", + "OS_BOOTSTRAP_REGION_ID=RegionOne", + ], + :user => platform_params[:keystone_user], + :path => '/usr/bin', + :refreshonly => true, + :subscribe => 'Anchor[keystone::dbsync::end]', + :notify => 'Anchor[keystone::service::begin]', + :tag => 'keystone-bootstrap', + )} + + it { is_expected.to contain_keystone_role('admin').with_ensure('present') } + + it { is_expected.to contain_keystone_user('admin').with( + :ensure => 'present', + :enabled => true, + :email => 'admin@localhost', + :password => 'secret', + )} + + it { is_expected.to contain_keystone_tenant('services').with( + :ensure => 'present', + :enabled => true, + )} + + it { is_expected.to contain_keystone_tenant('admin').with( + :ensure => 'present', + :enabled => true, + )} + + it { is_expected.to contain_keystone_user_role('admin@admin').with( + :ensure => 'present', + :roles => ['admin'], + )} + + it { is_expected.to contain_keystone_service('keystone::identity').with_ensure('present') } + + it { is_expected.to contain_keystone_endpoint('RegionOne/keystone::identity').with( + :ensure => 'present', + :public_url => 'http://127.0.0.1:5000', + :admin_url => 'http://127.0.0.1:5000', + :internal_url => 'http://127.0.0.1:5000', + )} + + it { is_expected.to contain_file('/etc/keystone/puppet.conf').with( + :ensure => 'present', + :replace => false, + :content => '', + :owner => 'root', + :group => 'root', + :mode => '0600', + :require => 'Anchor[keystone::install::end]', + )} + + it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( + :username => 'admin', + :password => 'secret', + :auth_url => 'http://127.0.0.1:5000', + :project_name => 'admin', + :region_name => 'RegionOne', + :interface => 'public', + )} + end + + context 'with specified parameters' do + let :params do + { + :password => 'secret', + :username => 'user', + :email => 'some@email', + :project_name => 'adminproj', + :service_project_name => 'serviceproj', + :role_name => 'adminrole', + :service_name => 'servicename', + :admin_url => 'http://admin:1234', + :public_url => 'http://public:4321', + :internal_url => 'http://internal:1342', + :region => 'RegionTwo', + :interface => 'admin' + } + end + + it { is_expected.to contain_class('keystone::deps') } + + it { is_expected.to contain_exec('keystone bootstrap').with( + :command => 'keystone-manage bootstrap', + :environment => [ + "OS_BOOTSTRAP_USERNAME=user", + "OS_BOOTSTRAP_PASSWORD=secret", + "OS_BOOTSTRAP_PROJECT_NAME=adminproj", + "OS_BOOTSTRAP_ROLE_NAME=adminrole", + "OS_BOOTSTRAP_SERVICE_NAME=servicename", + "OS_BOOTSTRAP_ADMIN_URL=http://admin:1234", + "OS_BOOTSTRAP_PUBLIC_URL=http://public:4321", + "OS_BOOTSTRAP_INTERNAL_URL=http://internal:1342", + "OS_BOOTSTRAP_REGION_ID=RegionTwo", + ], + :user => platform_params[:keystone_user], + :path => '/usr/bin', + :refreshonly => true, + :subscribe => 'Anchor[keystone::dbsync::end]', + :notify => 'Anchor[keystone::service::begin]', + :tag => 'keystone-bootstrap', + )} + + it { is_expected.to contain_keystone_role('adminrole').with_ensure('present') } + + it { is_expected.to contain_keystone_user('user').with( + :ensure => 'present', + :enabled => true, + :email => 'some@email', + :password => 'secret', + )} + + it { is_expected.to contain_keystone_tenant('serviceproj').with( + :ensure => 'present', + :enabled => true, + )} + + it { is_expected.to contain_keystone_tenant('adminproj').with( + :ensure => 'present', + :enabled => true, + )} + + it { is_expected.to contain_keystone_user_role('user@adminproj').with( + :ensure => 'present', + :roles => ['adminrole'], + )} + + it { is_expected.to contain_keystone_service('servicename::identity').with_ensure('present') } + + it { is_expected.to contain_keystone_endpoint('RegionTwo/servicename::identity').with( + :ensure => 'present', + :public_url => 'http://public:4321', + :admin_url => 'http://admin:1234', + :internal_url => 'http://internal:1342', + )} + + it { is_expected.to contain_file('/etc/keystone/puppet.conf').with( + :ensure => 'present', + :replace => false, + :content => '', + :owner => 'root', + :group => 'root', + :mode => '0600', + :require => 'Anchor[keystone::install::end]', + )} + + it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( + :username => 'user', + :password => 'secret', + :auth_url => 'http://admin:1234', + :project_name => 'adminproj', + :region_name => 'RegionTwo', + :interface => 'admin', + )} + end + + context 'when setting keystone_user param in keystone' do + let :params do + { + :password => 'secret' + } + end + + let :pre_condition do + "class { '::keystone': + keystone_user => 'some', + }" + end + + it { is_expected.to contain_exec('keystone bootstrap').with_user('some') } + end + + context 'when setting interface to internal' do + let :params do + { + :password => 'secret', + :internal_url => 'http://internal:1234', + :interface => 'internal', + } + end + + it { is_expected.to contain_keystone__resource__authtoken('keystone_puppet_config').with( + :auth_url => 'http://internal:1234', + :interface => 'internal', + )} + end + end + + on_supported_os({ + :supported_os => OSDefaults.get_supported_os + }).each do |os,facts| + context "on #{os}" do + let(:facts) do + facts.merge!(OSDefaults.get_facts()) + end + + let(:platform_params) do + { :keystone_user => 'keystone' } + end + + it_behaves_like 'keystone::bootstrap' + end + end +end diff --git a/spec/classes/keystone_endpoint_spec.rb b/spec/classes/keystone_endpoint_spec.rb deleted file mode 100644 index 0931475f5..000000000 --- a/spec/classes/keystone_endpoint_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'spec_helper' - -describe 'keystone::endpoint' do - - it { is_expected.to contain_keystone_service('keystone::identity').with( - :ensure => 'present', - :description => 'OpenStack Identity Service' - )} - - describe 'with default parameters' do - it { is_expected.to contain_keystone_endpoint('RegionOne/keystone::identity').with( - :ensure => 'present', - :public_url => 'http://127.0.0.1:5000', - :admin_url => 'http://127.0.0.1:5000', - :internal_url => 'http://127.0.0.1:5000', - :region => 'RegionOne' - )} - end - - describe 'with overridden parameters' do - - let :params do - { :version => 'v42.6', - :public_url => 'https://identity.some.tld/the/main/endpoint', - :admin_url => 'https://identity-int.some.tld/some/admin/endpoint', - :internal_url => 'https://identity-int.some.tld/some/internal/endpoint', - :region => 'East' - } - end - - it { is_expected.to contain_keystone_endpoint('East/keystone::identity').with( - :ensure => 'present', - :public_url => 'https://identity.some.tld/the/main/endpoint/v42.6', - :admin_url => 'https://identity-int.some.tld/some/admin/endpoint/v42.6', - :internal_url => 'https://identity-int.some.tld/some/internal/endpoint/v42.6', - :region => 'East' - )} - end - - describe 'with unset version to test backward compatibility' do - let :params do - { :version => 'unset' } - end - - it { is_expected.to contain_keystone_endpoint('RegionOne/keystone::identity').with( - :ensure => 'present', - :public_url => 'http://127.0.0.1:5000/v2.0', - :admin_url => 'http://127.0.0.1:5000/v2.0', - :internal_url => 'http://127.0.0.1:5000/v2.0' - )} - end - - describe 'without internal_url parameter' do - - let :params do - { :public_url => 'https://identity.some.tld/the/main/endpoint' } - end - - it 'internal_url should default to public_url' do - is_expected.to contain_keystone_endpoint('RegionOne/keystone::identity').with( - :ensure => 'present', - :public_url => 'https://identity.some.tld/the/main/endpoint', - :internal_url => 'https://identity.some.tld/the/main/endpoint' - ) - end - end - - describe 'with domain parameters' do - - let :params do - { :user_domain => 'userdomain', - :project_domain => 'projectdomain', - :default_domain => 'defaultdomain' } - end - - it { is_expected.to contain_keystone__resource__service_identity('keystone').with( - :user_domain => 'userdomain', - :project_domain => 'projectdomain', - :default_domain => 'defaultdomain' - )} - end -end diff --git a/spec/classes/keystone_init_spec.rb b/spec/classes/keystone_init_spec.rb index ef1036a8a..8a0c4d287 100644 --- a/spec/classes/keystone_init_spec.rb +++ b/spec/classes/keystone_init_spec.rb @@ -19,8 +19,6 @@ describe 'keystone' do end default_params = { - 'admin_token' => 'service_token', - 'admin_password' => 'special_password', 'package_ensure' => 'present', 'client_package_ensure' => 'present', 'public_bind_host' => '0.0.0.0', @@ -63,8 +61,6 @@ describe 'keystone' do 'client_package_ensure' => 'latest', 'public_bind_host' => '0.0.0.0', 'public_port' => '5001', - 'admin_token' => 'service_token_override', - 'admin_password' => 'admin_openstack_password', 'catalog_type' => 'template', 'token_provider' => 'uuid', 'password_hash_algorithm' => 'pbkdf2_sha512', @@ -125,17 +121,6 @@ describe 'keystone' do end end - it 'should bootstrap $enable_bootstrap is true' do - if param_hash['enable_bootstrap'] - is_expected.to contain_exec('keystone-manage bootstrap').with( - :command => 'keystone-manage bootstrap', - :environment => 'OS_BOOTSTRAP_PASSWORD=service_password', - :user => param_hash['keystone_user'], - :refreshonly => true - ) - end - end - it 'passes purge to resource' do is_expected.to contain_resources('keystone_config').with({ :purge => false @@ -151,10 +136,6 @@ describe 'keystone' do end end - it 'should contain correct admin_token config' do - is_expected.to contain_keystone_config('DEFAULT/admin_token').with_value(param_hash['admin_token']).with_secret(true) - end - it 'should contain correct mysql config' do is_expected.to contain_class('keystone::db') end @@ -288,7 +269,6 @@ describe 'keystone' do describe 'when ipv6 loopback is set' do let :params do { - :admin_token => 'service_token', :public_bind_host => '::0' } end @@ -298,7 +278,6 @@ describe 'keystone' do describe 'when ipv4 address is set' do let :params do { - :admin_token => 'service_token', :public_bind_host => '192.168.0.1', :public_port => '15000' } @@ -309,7 +288,6 @@ describe 'keystone' do describe 'when unenclosed ipv6 address is set' do let :params do { - :admin_token => 'service_token', :public_bind_host => '2001:db8::1' } end @@ -319,7 +297,6 @@ describe 'keystone' do describe 'when enclosed ipv6 address is set' do let :params do { - :admin_token => 'service_token', :public_bind_host => '[2001:db8::1]' } end @@ -335,8 +312,7 @@ describe 'keystone' do describe 'with disabled service managing' do let :params do - { :admin_token => 'service_token', - :manage_service => false, + { :manage_service => false, :enabled => false } end @@ -354,7 +330,6 @@ describe 'keystone' do describe 'when configuring as UUID' do let :params do { - 'admin_token' => 'service_token', 'token_provider' => 'keystone.token.providers.uuid.Provider' } end @@ -362,8 +337,7 @@ describe 'keystone' do describe 'with invalid catalog_type' do let :params do - { :admin_token => 'service_token', - :catalog_type => 'invalid' } + { :catalog_type => 'invalid' } end it { should raise_error(Puppet::Error) } @@ -371,8 +345,7 @@ describe 'keystone' do describe 'when configuring catalog driver' do let :params do - { :admin_token => 'service_token', - :catalog_driver => 'alien' } + { :catalog_driver => 'alien' } end it { is_expected.to contain_keystone_config('catalog/driver').with_value(params[:catalog_driver]) } @@ -382,7 +355,6 @@ describe 'keystone' do describe 'when configuring token expiration' do let :params do { - 'admin_token' => 'service_token', 'token_expiration' => '42', } end @@ -392,9 +364,7 @@ describe 'keystone' do describe 'when not configuring token expiration' do let :params do - { - 'admin_token' => 'service_token', - } + {} end it { is_expected.to contain_keystone_config("token/expiration").with_value('3600') } @@ -403,29 +373,16 @@ describe 'keystone' do describe 'when sync_db is set to false' do let :params do { - 'admin_token' => 'service_token', - 'sync_db' => false, + 'sync_db' => false, } end it { is_expected.not_to contain_exec('keystone-manage db_sync') } end - describe 'when enable_bootstrap is set to false' do - let :params do - { - 'admin_token' => 'service_token', - 'enable_bootstrap' => false, - } - end - - it { is_expected.not_to contain_exec('keystone-manage bootstrap') } - end - describe 'configure memcache servers if set' do let :params do { - 'admin_token' => 'service_token', 'cache_backend' => 'dogpile.cache.memcached', 'cache_backend_argument' => ['url:SERVER1:12211'], 'cache_memcache_servers' => 'SERVER1:11211,SERVER2:11211,[fd12:3456:789a:1::1]:11211', @@ -454,7 +411,6 @@ describe 'keystone' do describe 'configure cache memcache servers if set' do let :params do { - 'admin_token' => 'service_token', 'cache_backend' => 'dogpile.cache.memcached', 'cache_backend_argument' => ['url:SERVER3:12211'], 'cache_memcache_servers' => [ 'SERVER1:11211', 'SERVER2:11211', '[fd12:3456:789a:1::1]:11211' ], @@ -487,7 +443,6 @@ describe 'keystone' do describe 'configure cache enabled if set' do let :params do { - 'admin_token' => 'service_token', 'cache_backend' => 'dogpile.cache.memcached', 'cache_backend_argument' => ['url:SERVER3:12211'], 'cache_enabled' => true, @@ -550,7 +505,6 @@ describe 'keystone' do describe 'when enabling SSL' do let :params do { - 'admin_token' => 'service_token', 'enable_ssl' => true, 'public_endpoint' => 'https://localhost:5000', } @@ -567,8 +521,7 @@ describe 'keystone' do describe 'when disabling SSL' do let :params do { - 'admin_token' => 'service_token', - 'enable_ssl' => false, + 'enable_ssl'=> false, } end it {is_expected.to contain_keystone_config('ssl/enable').with_value(false)} @@ -706,7 +659,6 @@ describe 'keystone' do describe 'setting default template catalog' do let :params do { - :admin_token => 'service_token', :catalog_type => 'template' } end @@ -718,7 +670,6 @@ describe 'keystone' do describe 'setting another template catalog' do let :params do { - :admin_token => 'service_token', :catalog_type => 'template', :catalog_template_file => '/some/template_file' } diff --git a/spec/classes/keystone_roles_admin_spec.rb b/spec/classes/keystone_roles_admin_spec.rb deleted file mode 100644 index b54137aa2..000000000 --- a/spec/classes/keystone_roles_admin_spec.rb +++ /dev/null @@ -1,262 +0,0 @@ -require 'spec_helper' -describe 'keystone::roles::admin' do - let :pre_condition do - "class { 'keystone': - admin_token => 'dummy', - admin_password => 'ChangeMe' }" - end - - let :facts do - @default_facts.merge({ - :osfamily => 'Debian', - :operatingsystem => 'Debian', - :operatingsystemrelease => '7.0', - :os_workers => 1, - :os => { :name => 'Debian', :family => 'Debian', :release => { :major => '7', :minor => '0' } }, - }) - end - - describe 'with only the required params set' do - let :params do - { - :password => 'ChangeMe' - } - end - - it { is_expected.to contain_keystone_tenant('services').with( - :ensure => 'present', - :enabled => true, - :description => 'Tenant for the openstack services' - )} - it { is_expected.to contain_keystone_tenant('openstack').with( - :ensure => 'present', - :enabled => true, - :description => 'admin tenant' - )} - it { is_expected.to contain_keystone_user('admin').with( - :ensure => 'present', - :enabled => true, - :email => 'admin@localhost', - :password => 'ChangeMe', - )} - it { is_expected.to contain_keystone_role('admin').with_ensure('present') } - it { is_expected.to contain_keystone_user_role('admin@openstack').with( - :roles => ['admin'], - :ensure => 'present', - :user_domain => nil, - :project_domain => nil, - )} - - end - - describe 'when overriding optional params' do - let :pre_condition do - "class { 'keystone': - admin_token => 'dummy', - admin_password => 'foo' }" - end - - let :params do - { - :admin => 'admin', - :email => 'foo@baz', - :password => 'foo', - :admin_tenant => 'admin', - :admin_roles => ['admin', 'heat_stack_owner'], - :service_tenant => 'foobar', - :admin_tenant_desc => 'admin something else', - :service_tenant_desc => 'foobar description', - } - end - - it { is_expected.to contain_keystone_tenant('foobar').with( - :ensure => 'present', - :enabled => true, - :description => 'foobar description' - )} - it { is_expected.to contain_keystone_tenant('admin').with( - :ensure => 'present', - :enabled => true, - :description => 'admin something else' - )} - it { is_expected.to contain_keystone_user('admin').with( - :ensure => 'present', - :enabled => true, - :email => 'foo@baz', - :password => 'foo', - )} - it { is_expected.to contain_keystone_user_role('admin@admin').with( - :roles => ['admin', 'heat_stack_owner'], - :ensure => 'present', - :user_domain => nil, - :project_domain => nil, - )} - - end - - describe 'when disabling user configuration' do - before do - let :params do - { - :configure_user => false - } - end - - it { is_expected.to_not contain_keystone_user('keystone') } - it { is_expected.to contain_keystone_user_role('keystone@openstack') } - end - end - - describe 'when disabling user and role configuration' do - before do - let :params do - { - :configure_user => false, - :configure_user_role => false - } - end - - it { is_expected.to_not contain_keystone_user('keystone') } - it { is_expected.to_not contain_keystone_user_role('keystone@openstack') } - end - end - - describe 'when specifying admin_user_domain and admin_project_domain' do - let :params do - { - :email => 'foo@bar', - :password => 'ChangeMe', - :admin_tenant => 'admin_tenant', - :admin_user_domain => 'admin_user_domain', - :admin_project_domain => 'admin_project_domain', - } - end - it { is_expected.to contain_keystone_user('admin').with( - :domain => 'admin_user_domain', - )} - it { is_expected.to contain_keystone_tenant('admin_tenant').with(:domain => 'admin_project_domain') } - it { is_expected.to contain_keystone_domain('admin_user_domain') } - it { is_expected.to contain_keystone_domain('admin_project_domain') } - it { is_expected.to contain_keystone_user_role('admin@admin_tenant').with( - :roles => ['admin'], - :ensure => 'present', - :user_domain => 'admin_user_domain', - :project_domain => 'admin_project_domain', - )} - - end - - describe 'when specifying admin_user_domain and admin_project_domain' do - let :params do - { - :email => 'foo@bar', - :password => 'ChangeMe', - :admin_tenant => 'admin_tenant::admin_project_domain', - :admin_user_domain => 'admin_user_domain', - :admin_project_domain => 'admin_project_domain', - } - end - it { is_expected.to contain_keystone_user('admin').with( - :domain => 'admin_user_domain', - )} - it { is_expected.to contain_keystone_tenant('admin_tenant::admin_project_domain').with(:domain => 'admin_project_domain') } - it { is_expected.to contain_keystone_domain('admin_user_domain') } - it { is_expected.to contain_keystone_domain('admin_project_domain') } - it { is_expected.to contain_keystone_user_role('admin@admin_tenant::admin_project_domain').with( - :roles => ['admin'], - :ensure => 'present', - :user_domain => 'admin_user_domain', - :project_domain => 'admin_project_domain', - )} - - end - - describe 'when specifying a service domain' do - let :params do - { - :email => 'foo@bar', - :password => 'ChangeMe', - :service_tenant => 'service_project', - :service_project_domain => 'service_domain' - } - end - it { is_expected.to contain_keystone_tenant('service_project').with(:domain => 'service_domain') } - it { is_expected.to contain_keystone_domain('service_domain') } - - end - - describe 'when specifying a service domain and service tenant domain' do - let :params do - { - :email => 'foo@bar', - :password => 'ChangeMe', - :service_tenant => 'service_project::service_domain', - :service_project_domain => 'service_domain' - } - end - it { is_expected.to contain_keystone_tenant('service_project::service_domain').with(:domain => 'service_domain') } - it { is_expected.to contain_keystone_domain('service_domain') } - - end - - describe 'when admin_user_domain and admin_project_domain are equal' do - let :params do - { - :email => 'foo@bar', - :password => 'ChangeMe', - :admin_user_domain => 'admin_domain', - :admin_project_domain => 'admin_domain', - } - end - it { is_expected.to contain_keystone_domain('admin_domain') } - end - - describe 'when specifying a target admin domain' do - let :params do - { - :email => 'foo@bar', - :password => 'ChangeMe', - :admin_user_domain => 'admin_domain', - :admin_project_domain => 'admin_domain', - :target_admin_domain => 'admin_domain_target' - } - end - let :pre_condition do - ["class { 'keystone': - admin_token => 'dummy', - admin_password => 'ChangeMe' }", - "file { '/root/openrc': tag => ['openrc']}"] - end - it { is_expected.to contain_keystone_domain('admin_domain_target') } - it { is_expected.to contain_keystone_user_role('admin@::admin_domain_target') - .with( - :roles => ['admin'], - :ensure => 'present', - :user_domain => 'admin_domain', - ) - .that_comes_before('File[/root/openrc]') - } - end - - describe 'when admin_password and password do not match' do - let :pre_condition do - "class { 'keystone': - admin_token => 'dummy', - admin_password => 'foo' }" - end - let :params do - { - :email => 'foo@bar', - :password => 'bar', - :service_tenant => 'services' - } - end - it { is_expected.to contain_keystone_role('admin').with_ensure('present') } - it { is_expected.to contain_keystone_user_role('admin@openstack').with( - :roles => ['admin'], - :ensure => 'present', - :user_domain => nil, - :project_domain => nil, - )} - end -end diff --git a/spec/unit/provider/keystone_puppet_config/ini_setting_spec.rb b/spec/unit/provider/keystone_puppet_config/ini_setting_spec.rb new file mode 100644 index 000000000..945e8da7b --- /dev/null +++ b/spec/unit/provider/keystone_puppet_config/ini_setting_spec.rb @@ -0,0 +1,57 @@ +# these tests are a little concerning b/c they are hacking around the +# modulepath, so these tests will not catch issues that may eventually arise +# related to loading these plugins. +# I could not, for the life of me, figure out how to programatcally set the modulepath +$LOAD_PATH.push( + File.join( + File.dirname(__FILE__), + '..', + '..', + '..', + 'fixtures', + 'modules', + 'inifile', + 'lib') +) + +require 'spec_helper' + +provider_class = Puppet::Type.type(:keystone_puppet_config).provider(:ini_setting) + +describe provider_class do + it 'should default to the default setting when no other one is specified' do + resource = Puppet::Type::Keystone_puppet_config.new( + {:name => 'DEFAULT/foo', :value => 'bar'} + ) + provider = provider_class.new(resource) + expect(provider.section).to eq('DEFAULT') + expect(provider.setting).to eq('foo') + end + + it 'should allow setting to be set explicitly' do + resource = Puppet::Type::Keystone_puppet_config.new( + {:name => 'dude/foo', :value => 'bar'} + ) + provider = provider_class.new(resource) + expect(provider.section).to eq('dude') + expect(provider.setting).to eq('foo') + end + + it 'should ensure absent when is specified as a value' do + resource = Puppet::Type::Keystone_puppet_config.new( + {:name => 'dude/foo', :value => ''} + ) + provider = provider_class.new(resource) + provider.exists? + expect(resource[:ensure]).to eq :absent + end + + it 'should ensure absent when value matches ensure_absent_val' do + resource = Puppet::Type::Keystone_puppet_config.new( + {:name => 'dude/foo', :value => 'foo', :ensure_absent_val => 'foo' } + ) + provider = provider_class.new(resource) + provider.exists? + expect(resource[:ensure]).to eq :absent + end +end diff --git a/spec/unit/provider/keystone_spec.rb b/spec/unit/provider/keystone_spec.rb index d86b8a307..399311ac5 100644 --- a/spec/unit/provider/keystone_spec.rb +++ b/spec/unit/provider/keystone_spec.rb @@ -126,44 +126,13 @@ id="the_user_id" end end - describe '#get_public_endpoint' do - it 'should return nothing if there is no keystone config file' do - expect(klass.get_public_endpoint).to be_nil - end - - it 'should return nothing if the keystone config file does not have a DEFAULT section' do - mock = {} - 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.get_public_endpoint).to be_nil - end - - it 'should fail if the keystone config file does not contain public endpoint' do - mock = {'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.get_public_endpoint).to be_nil - end - - it 'should use the public_endpoint from keystone config file with no trailing slash' do - mock = {'DEFAULT' => {'public_endpoint' => 'https://keystone.example.com/'}} - 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.get_public_endpoint).to eq('https://keystone.example.com') - end - end - describe '#get_auth_url' do - it 'should return nothing when OS_AUTH_URL is no defined in either the environment or the openrc file and there is no keystone configuration file' 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) - File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(false) - expect(klass.get_auth_url).to be_nil + 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 @@ -180,33 +149,14 @@ id="the_user_id" expect(klass.get_auth_url).to eq('http://127.0.0.1:5001') end - it 'should use public_endpoint when nothing else is available' do + it 'should use auth_endpoint when nothing else is available' do ENV.clear mock = 'http://127.0.0.1:5001' - klass.expects(:public_endpoint).returns(mock) + klass.expects(:auth_endpoint).returns(mock) expect(klass.get_auth_url).to eq('http://127.0.0.1:5001') end end - describe '#get_service_url when retrieving the security token' do - it 'should return nothing when OS_ENDPOINT is not defined in environment' do - ENV.clear - expect(klass.get_service_url).to be_nil - end - - it 'should return the OS_ENDPOINT from the environment' do - ENV['OS_ENDPOINT'] = 'http://127.0.0.1:5001/v3' - expect(klass.get_service_url).to eq('http://127.0.0.1:5001/v3') - end - - it 'should use public_endpoint with the API version number' do - ENV.clear - mock = 'http://127.0.0.1:5001' - klass.expects(:public_endpoint).twice.returns(mock) - expect(klass.get_service_url).to eq('http://127.0.0.1:5001/v3') - end - end - describe '#set_domain_for_name' do it 'should raise an error if the domain is not provided' do expect do @@ -248,37 +198,6 @@ id="other_domain_id" end end - describe 'when retrieving the security token' do - it 'should return nothing if there is no keystone config file' do - File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(false) - expect(klass.get_admin_token).to be_nil - end - - it 'should return nothing if the keystone config file does not have a DEFAULT section' do - mock = {} - 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.get_admin_token).to be_nil - end - - it 'should fail if the keystone config file does not contain an admin token' do - mock = {'DEFAULT' => {'not_a_token' => 'foo'}} - 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.get_admin_token).to be_nil - end - - it 'should parse the admin token if it is in the config file' do - mock = {'DEFAULT' => {'admin_token' => 'foo'}} - 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.get_admin_token).to eq('foo') - end - end - describe 'when using domains' do before(:each) do set_env diff --git a/spec/unit/type/keystone_puppet_config_spec.rb b/spec/unit/type/keystone_puppet_config_spec.rb new file mode 100644 index 000000000..e910472f0 --- /dev/null +++ b/spec/unit/type/keystone_puppet_config_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +# this hack is required for now to ensure that the path is set up correctly +# to retrieve the parent provider +$LOAD_PATH.push( + File.join( + File.dirname(__FILE__), + '..', + '..', + 'fixtures', + 'modules', + 'inifile', + 'lib') +) + +require 'puppet' +require 'puppet/type/keystone_puppet_config' + +describe 'Puppet::Type.type(:keystone_puppet_config)' do + before :each do + @keystone_puppet_config = Puppet::Type.type(:keystone_puppet_config).new(:name => 'DEFAULT/foo', :value => 'bar') + end + + it 'should require a name' do + expect { + Puppet::Type.type(:keystone_puppet_config).new({}) + }.to raise_error(Puppet::Error, 'Title or name must be provided') + end + + it 'should not expect a name with whitespace' do + expect { + Puppet::Type.type(:keystone_puppet_config).new(:name => 'f oo') + }.to raise_error(Puppet::Error, /Parameter name failed/) + end + + it 'should fail when there is no section' do + expect { + Puppet::Type.type(:keystone_puppet_config).new(:name => 'foo') + }.to raise_error(Puppet::Error, /Parameter name failed/) + end + + it 'should not require a value when ensure is absent' do + Puppet::Type.type(:keystone_puppet_config).new(:name => 'DEFAULT/foo', :ensure => :absent) + end + + it 'should accept a valid value' do + @keystone_puppet_config[:value] = 'bar' + expect(@keystone_puppet_config[:value]).to eq('bar') + end + + it 'should not accept a value with whitespace' do + @keystone_puppet_config[:value] = 'b ar' + expect(@keystone_puppet_config[:value]).to eq('b ar') + end + + it 'should accept valid ensure values' do + @keystone_puppet_config[:ensure] = :present + expect(@keystone_puppet_config[:ensure]).to eq(:present) + @keystone_puppet_config[:ensure] = :absent + expect(@keystone_puppet_config[:ensure]).to eq(:absent) + end + + it 'should not accept invalid ensure values' do + expect { + @keystone_puppet_config[:ensure] = :latest + }.to raise_error(Puppet::Error, /Invalid value/) + end + + it 'should autorequire the config file' do + catalog = Puppet::Resource::Catalog.new + config_file = Puppet::Type.type(:file).new(:name => '/etc/keystone/puppet.conf') + catalog.add_resource config_file, @keystone_puppet_config + dependency = @keystone_puppet_config.autorequire + expect(dependency.size).to eq(1) + expect(dependency[0].target).to eq(@keystone_puppet_config) + expect(dependency[0].source).to eq(config_file) + end +end