Browse Source

Add keystone domain specific configuration.

Implements blueprint keystone-domain-configuration

Adds a provider able to configure multiple domains and two parameters in
keystone class to setup a working multi-domains configuration.

The keystone_config type has been refactored into a mixin to be shared
by keystone_config and keystone_domain_config.

The provider, even though it is inheriting from openstack_config (and
not keystone_config because it hard code the path), has required more
new code.  The problem is that we have several configuration files to
work with (one per domain) which is unusual.

The self.prefetch method is required to check the current catalog.  If
it's changing the Keystone_config[identity/domain_config_dir] we take it
directly into account without the need for another run.

Keystone_config[identity/domain_config_dir] configuration and the
associated directory are autorequired.

Change-Id: I5e4b298460ee592640af59ac9dcbefa3daf98098
tags/8.0.0b1
Sofer Athlan-Guyot 3 years ago
parent
commit
07f19bd38a

+ 40
- 0
examples/v3_domain_configuration.pp View File

@@ -0,0 +1,40 @@
1
+# Example using v3 domain configuration.  This setup a directory where
2
+# the domain configurations will be and adjust the keystone.
3
+# For the rest of the configuration check v3_basic.pp.
4
+#
5
+
6
+Exec { logoutput => 'on_failure' }
7
+
8
+class { '::mysql::server': }
9
+class { '::keystone::db::mysql':
10
+  password => 'keystone',
11
+}
12
+class { '::keystone':
13
+  verbose             => true,
14
+  debug               => true,
15
+  database_connection => 'mysql://keystone:keystone@192.168.1.1/keystone',
16
+  admin_token         => 'admin_token',
17
+  enabled             => true,
18
+  # The domain configuration setup at keystone level
19
+  using_domain_config => true,
20
+}
21
+class { '::keystone::roles::admin':
22
+  email    => 'test@example.tld',
23
+  password => 'a_big_secret',
24
+}
25
+class { '::keystone::endpoint':
26
+  public_url => 'http://192.168.1.1:5000/',
27
+  admin_url  => 'http://192.168.1.1:35357/',
28
+}
29
+
30
+# Creates the /etc/keystone/domains/keystone.my_domain.conf file and
31
+# notifies keystone service
32
+keystone_domain_config {
33
+  'my_domain::ldap/url':                 value => 'ldap://ldapservice.my_org.com';
34
+  'my_domain::ldap/user':                value => 'cn=Manager,dc=openstack,dc=org';
35
+  'my_domain::ldap/password':            value => 'mysecret';
36
+  'my_domain::ldap/suffix':              value => 'dc=openstack,dc=org';
37
+  'my_domain::ldap/group_tree_dn':       value => 'ou=UserGroups,dc=openstack,dc=org';
38
+  'my_domain::ldap/user_tree_dn':        value => 'ou=Users,dc=openstack,dc=org';
39
+  'my_domain::ldap/user_mail_attribute': value => 'mail';
40
+}

+ 108
- 0
lib/puppet/provider/keystone_domain_config/openstack.rb View File

@@ -0,0 +1,108 @@
1
+Puppet::Type.type(:keystone_domain_config).provide(
2
+  :openstack,
3
+  :parent => Puppet::Type.type(:openstack_config).provider(:ini_setting)
4
+) do
5
+
6
+  class Puppet::Error::OpenstackMissingDomainName < Puppet::Error; end
7
+  class Puppet::Error::OpenstackMissingDomainDir < Puppet::Error; end
8
+
9
+  # return the first which is defined:
10
+  #  1. the value defined in the catalog (@base_dir)
11
+  #  2. the value defined in the keystone.conf file
12
+  #  3. the default value '/etc/keystone/domains'
13
+  def self.base_dir
14
+    return @base_dir if @base_dir
15
+    base_dir = Puppet::Resource.indirection
16
+      .find('Keystone_config/identity/domain_config_dir')[:value]
17
+    if base_dir == :absent
18
+      '/etc/keystone/domains'
19
+    else
20
+      base_dir
21
+    end
22
+  end
23
+
24
+  def self.find_domain_conf(catalog)
25
+    catalog.resources.find do |r|
26
+      # better than is_a? here because symbol
27
+      # Puppet::Type::Keystone_config may not be defined.
28
+      r.class.to_s == 'Puppet::Type::Keystone_config' &&
29
+        r.name == 'identity/domain_config_dir'
30
+    end
31
+  end
32
+
33
+  # Use the prefetch hook to check if the keystone_config
34
+  # identity/domain_config_dir is changed in the same catalog.  This
35
+  # avoid to have to run puppet twice to get the right domain config
36
+  # file changed.  Note, prefetch is the only time we can have acces
37
+  # to the catalog from the provider.
38
+  def self.prefetch(resources)
39
+    catalog = resources.values.first.catalog
40
+    resource_dir = find_domain_conf(catalog)
41
+    @base_dir = resource_dir.nil? ? nil : resource_dir[:value]
42
+  end
43
+
44
+  def self.base_dir_exists?
45
+    base_dir_resource = Puppet::Resource.indirection
46
+      .find("file/#{base_dir}")[:ensure]
47
+    base_dir_resource == :directory ? true : false
48
+  end
49
+
50
+  def create
51
+    unless self.class.base_dir_exists?
52
+      raise(Puppet::Error::OpenstackMissingDomainDir,
53
+            "You must create the #{self.class.base_dir} directory " \
54
+              'for keystone domain configuration.')
55
+    end
56
+    super
57
+  end
58
+
59
+  # Do not provide self.file_path method.  We need to create instance
60
+  # with different file paths, so we cannot have the same path for all
61
+  # the instances.  This force us to redefine the instances class
62
+  # method.
63
+  def self.instances
64
+    resources = []
65
+    Dir.glob(File.join(base_dir,'keystone.*.conf')).each do |domain_conf_file|
66
+      domain = domain_conf_file.gsub(/^.*\/keystone\.(.*)\.conf$/, '\1')
67
+      ini_file = Puppet::Util::IniFile.new(domain_conf_file, '=')
68
+      ini_file.section_names.each do |section_name|
69
+        ini_file.get_settings(section_name).each do |setting, value|
70
+          resources.push(
71
+            new(
72
+              :name   => "#{domain}::#{section_name}/#{setting}",
73
+              :value  => value,
74
+              :ensure => :present
75
+            )
76
+          )
77
+        end
78
+      end
79
+    end
80
+    resources
81
+  end
82
+
83
+  def path
84
+    File.join(self.class.base_dir, 'keystone.' + domain + '.conf')
85
+  end
86
+
87
+  # This avoid to have only one file for all the instances.
88
+  alias_method :file_path, :path
89
+
90
+  def domain
91
+    if !@domain.nil?
92
+      @domain
93
+    else
94
+      result = name.partition('::')
95
+      if (result[1] == '' && result[2] == '') || result[0] == ''
96
+        raise(Puppet::Error::OpenstackMissingDomainName,
97
+              'You must provide a domain name in the name of the resource ' \
98
+                '<domain_name>::<section>/<key>.  It cannot be empty.')
99
+      else
100
+        @domain = result[0]
101
+      end
102
+    end
103
+  end
104
+
105
+  def section
106
+    @section ||= super.sub(domain + '::', '')
107
+  end
108
+end

+ 8
- 49
lib/puppet/type/keystone_config.rb View File

@@ -1,53 +1,12 @@
1 1
 Puppet::Type.newtype(:keystone_config) do
2 2
 
3
-  ensurable
4
-
5
-  newparam(:name, :namevar => true) do
6
-    desc 'Section/setting name to manage from keystone.conf'
7
-    newvalues(/\S+\/\S+/)
8
-  end
9
-
10
-  newproperty(:value) do
11
-    desc 'The value of the setting to be defined.'
12
-    munge do |value|
13
-      value = value.to_s.strip
14
-      value.capitalize! if value =~ /^(true|false)$/i
15
-      value
16
-    end
17
-    newvalues(/^[\S ]*$/)
18
-
19
-    def is_to_s( currentvalue )
20
-      if resource.secret?
21
-        return '[old secret redacted]'
22
-      else
23
-        return currentvalue
24
-      end
25
-    end
26
-
27
-    def should_to_s( newvalue )
28
-      if resource.secret?
29
-        return '[new secret redacted]'
30
-      else
31
-        return newvalue
32
-      end
33
-    end
34
-  end
35
-
36
-  newparam(:secret, :boolean => true) do
37
-    desc 'Whether to hide the value from Puppet logs. Defaults to `false`.'
38
-
39
-    newvalues(:true, :false)
40
-
41
-    defaultto false
42
-  end
43
-
44
-  newparam(:ensure_absent_val) do
45
-    desc 'A value that is specified as the value property will behave as if ensure => absent was specified'
46
-    defaultto('<SERVICE DEFAULT>')
47
-  end
48
-
49
-  autorequire(:package) do
50
-    'keystone'
51
-  end
3
+  require File.expand_path(File.join(
4
+                    File.dirname(__FILE__), '..', '..',
5
+                    'puppet_x', 'keystone_config', 'ini_setting'))
6
+  # mixin, shared with keystone_domain_config until this one moves on
7
+  # to openstack cli
8
+  extend PuppetX::KeystoneConfig::IniSetting
9
+
10
+  create_parameters
52 11
 
53 12
 end

+ 36
- 0
lib/puppet/type/keystone_domain_config.rb View File

@@ -0,0 +1,36 @@
1
+Puppet::Type.newtype(:keystone_domain_config) do
2
+
3
+  require File.expand_path(File.join(
4
+                    File.dirname(__FILE__), '..', '..',
5
+                    'puppet_x', 'keystone_config', 'ini_setting'))
6
+  # mixin, shared with keystone_domain_config until this one moves on
7
+  # to openstack cli
8
+  extend PuppetX::KeystoneConfig::IniSetting
9
+
10
+  def initialize(*args)
11
+    super
12
+    # latest version of puppet got autonotify, but 3.8.2 doesn't so
13
+    # use this.
14
+    keystone_service = 'Service[keystone]'
15
+    self[:notify] = [keystone_service] if !catalog.nil? &&
16
+      catalog.resource(keystone_service)
17
+  end
18
+
19
+  create_parameters
20
+
21
+  # if one declare the domain directory as a resource, this will
22
+  # create a soft dependancy with it.
23
+  autorequire(:file) do
24
+    currently_defined = provider.class.find_domain_conf(catalog)
25
+    # we use the catalog and fall back to provider.self.base_dir (see
26
+    # its comment).  Note at this time the @base_dir in
27
+    # provider.class.base_dir will always be false as self.prefetch
28
+    # hasn't run yet.
29
+    currently_defined.nil? ? [provider.class.base_dir] : [currently_defined[:value]]
30
+  end
31
+
32
+  # if the keystone configuration is changed we require it
33
+  autorequire(:keystone_config) do
34
+    ['identity/domain_config_dir']
35
+  end
36
+end

+ 61
- 0
lib/puppet_x/keystone_config/ini_setting.rb View File

@@ -0,0 +1,61 @@
1
+module PuppetX
2
+  module KeystoneConfig
3
+    # Mixin for shared code between keystone_config and
4
+    # keystone_domain_config.  This can be reincluded directly to
5
+    # keystone_config when openstackcli supports domain configuration and
6
+    # keystone_domain_config is refactored.
7
+    module IniSetting
8
+      def create_parameters
9
+        ensurable
10
+
11
+        newparam(:name, :namevar => true) do
12
+          desc 'Section/setting name to manage from keystone.conf'
13
+          newvalues(/\S+\/\S+/)
14
+        end
15
+
16
+        newproperty(:value) do
17
+          desc 'The value of the setting to be defined.'
18
+          munge do |value|
19
+            value = value.to_s.strip
20
+            value.capitalize! if value =~ /^(true|false)$/i
21
+            value
22
+          end
23
+          newvalues(/^[\S ]*$/)
24
+
25
+          def is_to_s( currentvalue )
26
+            if resource.secret?
27
+              return '[old secret redacted]'
28
+            else
29
+              return currentvalue
30
+            end
31
+          end
32
+
33
+          def should_to_s( newvalue )
34
+            if resource.secret?
35
+              return '[new secret redacted]'
36
+            else
37
+              return newvalue
38
+            end
39
+          end
40
+        end
41
+
42
+        newparam(:secret, :boolean => true) do
43
+          desc 'Whether to hide the value from Puppet logs. Defaults to `false`.'
44
+
45
+          newvalues(:true, :false)
46
+
47
+          defaultto false
48
+        end
49
+
50
+        newparam(:ensure_absent_val) do
51
+          desc 'A value that is specified as the value property will behave as if ensure => absent was specified'
52
+          defaultto('<SERVICE DEFAULT>')
53
+        end
54
+
55
+        autorequire(:package) do
56
+          'keystone'
57
+        end
58
+      end
59
+    end
60
+  end
61
+end

+ 47
- 0
manifests/init.pp View File

@@ -462,6 +462,18 @@
462 462
 #   Policy backend driver. (string value)
463 463
 #   Defaults to $::os_service_default.
464 464
 #
465
+# [*using_domain_config*]
466
+#   (optional) Eases the use of the keystone_domain_config resource type.
467
+#   It ensures that a directory for holding the domain configuration is present
468
+#   and the associated configuration in keystone.conf is set up right.
469
+#   Defaults to false
470
+#
471
+# [*domain_config_directory*]
472
+#   (optional) Specify a domain configuration directory.
473
+#   For this to work the using_domain_config must be set to true.  Raise an
474
+#   error if it's not the case.
475
+#   Defaults to '/etc/keystone/domains'
476
+#
465 477
 # == Dependencies
466 478
 #  None
467 479
 #
@@ -581,6 +593,8 @@ class keystone(
581 593
   $memcache_pool_maxsize              = $::os_service_default,
582 594
   $memcache_pool_unused_timeout       = $::os_service_default,
583 595
   $policy_driver                      = $::os_service_default,
596
+  $using_domain_config                = false,
597
+  $domain_config_directory            = '/etc/keystone/domains',
584 598
   # DEPRECATED PARAMETERS
585 599
   $admin_workers                      = max($::processorcount, 2),
586 600
   $public_workers                     = max($::processorcount, 2),
@@ -971,6 +985,39 @@ class keystone(
971 985
       }
972 986
     }
973 987
   }
988
+  if $domain_config_directory != '/etc/keystone/domains' and !$using_domain_config {
989
+    fail('You must activate domain configuration using "using_domain_config" parameter to keystone class.')
990
+  }
991
+
992
+  if $using_domain_config {
993
+    validate_absolute_path($domain_config_directory)
994
+    # Better than ensure resource.  We don't want to conflict with any
995
+    # user definition even if they don't match exactly our parameters.
996
+    # The error catching mechanism in the provider will remind them if
997
+    # they did something silly, like defining a file rather than a
998
+    # directory.  For the permission it's their choice.
999
+    if (!defined(File[$domain_config_directory])) {
1000
+      file { $domain_config_directory:
1001
+        ensure => directory,
1002
+        owner  => 'keystone',
1003
+        group  => 'keystone',
1004
+        mode   => '0750',
1005
+        } -> File['/etc/keystone/keystone.conf']
1006
+    }
1007
+    # Here we want the creation to fail if the user has created those
1008
+    # resources with different values. That means that the user
1009
+    # wrongly uses using_domain_config parameter.
1010
+    ensure_resource(
1011
+      'keystone_config',
1012
+      'identity/domain_specific_drivers_enabled',
1013
+      {'value' => true}
1014
+    )
1015
+    ensure_resource(
1016
+      'keystone_config',
1017
+      'identity/domain_config_dir',
1018
+      {'value' => $domain_config_directory}
1019
+    )
1020
+  }
974 1021
   anchor { 'keystone_started':
975 1022
     require => Service[$service_name]
976 1023
   }

+ 123
- 0
spec/acceptance/basic_keystone_spec.rb View File

@@ -279,4 +279,127 @@ describe 'basic keystone server with resources' do
279 279
       end
280 280
     end
281 281
   end
282
+
283
+  context '#keystone_domain_config' do
284
+    # make sure everything is clean before playing the manifest
285
+    shared_examples 'clean_domain_configuration', :clean_domain_cfg => true do
286
+      before(:context) do
287
+        hosts.each do |host|
288
+          on host, 'rm -rf /etc/keystone/domains >/dev/null 2>&1'
289
+          on host, 'rm -rf /tmp/keystone.*.conf >/dev/null 2>&1'
290
+        end
291
+      end
292
+    end
293
+
294
+    context 'one domain configuration', :clean_domain_cfg => true  do
295
+      context 'simple use case' do
296
+        it_behaves_like 'puppet_apply_success', <<-EOM
297
+          file { '/etc/keystone/domains': ensure => directory }
298
+          keystone_domain_config { 'services::ldap/url':
299
+            value => 'http://auth.com/1',
300
+          }
301
+        EOM
302
+
303
+        context '/etc/keystone/domains/keystone.services.conf' do
304
+          # the idiom
305
+
306
+          # note: cannot use neither instance variable nor let on
307
+          # parameter for shared_example
308
+          it_behaves_like 'a_valid_configuration', <<-EOC
309
+
310
+[ldap]
311
+url=http://auth.com/1
312
+EOC
313
+        end
314
+      end
315
+
316
+      context 'with a non default identity/domain_config_dir' do
317
+        it_behaves_like 'puppet_apply_success', <<-EOM
318
+        keystone_config { 'identity/domain_config_dir': value => '/tmp' }
319
+        keystone_domain_config { 'services::ldap/url':
320
+          value => 'http://auth.com/1',
321
+        }
322
+        EOM
323
+
324
+        context '/tmp/keystone.services.conf' do
325
+          it_behaves_like 'a_valid_configuration', <<-EOC
326
+
327
+[ldap]
328
+url=http://auth.com/1
329
+EOC
330
+        end
331
+      end
332
+    end
333
+
334
+    context 'with a multiple configurations', :clean_domain_cfg => true do
335
+      it_behaves_like 'puppet_apply_success', <<-EOM
336
+      file { '/etc/keystone/domains': ensure => directory }
337
+      keystone_config { 'identity/domain_config_dir': value => '/etc/keystone/domains' }
338
+      keystone_domain_config { 'services::ldap/url':
339
+        value => 'http://auth.com/1',
340
+      }
341
+      keystone_domain_config { 'services::http/url':
342
+        value => 'http://auth.com/2',
343
+      }
344
+      keystone_domain_config { 'external::ldap/url':
345
+        value => 'http://ext-auth.com/1',
346
+      }
347
+      EOM
348
+
349
+      describe command('puppet resource keystone_domain_config') do
350
+        its(:exit_status) { is_expected.to eq(0) }
351
+        its(:stdout) { is_expected.to eq(<<EOO) }
352
+keystone_domain_config { 'external::ldap/url':
353
+  ensure => 'present',
354
+  value  => 'http://ext-auth.com/1',
355
+}
356
+keystone_domain_config { 'services::http/url':
357
+  ensure => 'present',
358
+  value  => 'http://auth.com/2',
359
+}
360
+keystone_domain_config { 'services::ldap/url':
361
+  ensure => 'present',
362
+  value  => 'http://auth.com/1',
363
+}
364
+EOO
365
+      end
366
+
367
+      describe '/etc/keystone/domains/keystone.services.conf' do
368
+        it_behaves_like 'a_valid_configuration', <<EOC
369
+
370
+[http]
371
+url=http://auth.com/2
372
+
373
+[ldap]
374
+url=http://auth.com/1
375
+EOC
376
+      end
377
+      describe '/etc/keystone/domains/keystone.external.conf' do
378
+        it_behaves_like 'a_valid_configuration', <<EOC
379
+
380
+[ldap]
381
+url=http://ext-auth.com/1
382
+EOC
383
+      end
384
+    end
385
+
386
+    context 'checking that the purge is working' do
387
+      it_behaves_like 'puppet_apply_success', <<-EOM
388
+      resources { 'keystone_domain_config': purge => true }
389
+      keystone_domain_config { 'services::ldap/url':
390
+        value => 'http://auth.com/1',
391
+      }
392
+      EOM
393
+
394
+      context '/etc/keystone/domains/keystone.services.conf' do
395
+        it_behaves_like 'a_valid_configuration', <<-EOC
396
+
397
+[http]
398
+
399
+[ldap]
400
+url=http://auth.com/1
401
+EOC
402
+      end
403
+    end
404
+  end
282 405
 end

+ 124
- 0
spec/acceptance/keystone_wsgi_apache_spec.rb View File

@@ -215,6 +215,7 @@ describe 'keystone server running with Apache/WSGI with resources' do
215 215
       apply_manifest(pp, :catch_changes => true)
216 216
     end
217 217
   end
218
+
218 219
   describe 'composite namevar for keystone_service and keystone_endpoint' do
219 220
     let(:pp) do
220 221
       <<-EOM
@@ -259,4 +260,127 @@ describe 'keystone server running with Apache/WSGI with resources' do
259 260
       end
260 261
     end
261 262
   end
263
+
264
+  context '#keystone_domain_config' do
265
+    # make sure everything is clean before playing the manifest
266
+    shared_examples 'clean_domain_configuration', :clean_domain_cfg => true do
267
+      before(:context) do
268
+        hosts.each do |host|
269
+          on host, 'rm -rf /etc/keystone/domains >/dev/null 2>&1'
270
+          on host, 'rm -rf /tmp/keystone.*.conf >/dev/null 2>&1'
271
+        end
272
+      end
273
+    end
274
+
275
+    context 'one domain configuration', :clean_domain_cfg => true  do
276
+      context 'simple use case' do
277
+        it_behaves_like 'puppet_apply_success', <<-EOM
278
+          file { '/etc/keystone/domains': ensure => directory }
279
+          keystone_domain_config { 'services::ldap/url':
280
+            value => 'http://auth.com/1',
281
+          }
282
+        EOM
283
+
284
+        context '/etc/keystone/domains/keystone.services.conf' do
285
+          # the idiom
286
+
287
+          # note: cannot use neither instance variable nor let on
288
+          # parameter for shared_example
289
+          it_behaves_like 'a_valid_configuration', <<-EOC
290
+
291
+[ldap]
292
+url=http://auth.com/1
293
+EOC
294
+        end
295
+      end
296
+
297
+      context 'with a non default identity/domain_config_dir' do
298
+        it_behaves_like 'puppet_apply_success', <<-EOM
299
+        keystone_config { 'identity/domain_config_dir': value => '/tmp' }
300
+        keystone_domain_config { 'services::ldap/url':
301
+          value => 'http://auth.com/1',
302
+        }
303
+        EOM
304
+
305
+        context '/tmp/keystone.services.conf' do
306
+          it_behaves_like 'a_valid_configuration', <<-EOC
307
+
308
+[ldap]
309
+url=http://auth.com/1
310
+EOC
311
+        end
312
+      end
313
+    end
314
+
315
+    context 'with a multiple configurations', :clean_domain_cfg => true do
316
+      it_behaves_like 'puppet_apply_success', <<-EOM
317
+      file { '/etc/keystone/domains': ensure => directory }
318
+      keystone_config { 'identity/domain_config_dir': value => '/etc/keystone/domains' }
319
+      keystone_domain_config { 'services::ldap/url':
320
+        value => 'http://auth.com/1',
321
+      }
322
+      keystone_domain_config { 'services::http/url':
323
+        value => 'http://auth.com/2',
324
+      }
325
+      keystone_domain_config { 'external::ldap/url':
326
+        value => 'http://ext-auth.com/1',
327
+      }
328
+      EOM
329
+
330
+      describe command('puppet resource keystone_domain_config') do
331
+        its(:exit_status) { is_expected.to eq(0) }
332
+        its(:stdout) { is_expected.to eq(<<EOO) }
333
+keystone_domain_config { 'external::ldap/url':
334
+  ensure => 'present',
335
+  value  => 'http://ext-auth.com/1',
336
+}
337
+keystone_domain_config { 'services::http/url':
338
+  ensure => 'present',
339
+  value  => 'http://auth.com/2',
340
+}
341
+keystone_domain_config { 'services::ldap/url':
342
+  ensure => 'present',
343
+  value  => 'http://auth.com/1',
344
+}
345
+EOO
346
+      end
347
+
348
+      describe '/etc/keystone/domains/keystone.services.conf' do
349
+        it_behaves_like 'a_valid_configuration', <<EOC
350
+
351
+[http]
352
+url=http://auth.com/2
353
+
354
+[ldap]
355
+url=http://auth.com/1
356
+EOC
357
+      end
358
+      describe '/etc/keystone/domains/keystone.external.conf' do
359
+        it_behaves_like 'a_valid_configuration', <<EOC
360
+
361
+[ldap]
362
+url=http://ext-auth.com/1
363
+EOC
364
+      end
365
+    end
366
+
367
+    context 'checking that the purge is working' do
368
+      it_behaves_like 'puppet_apply_success', <<-EOM
369
+      resources { 'keystone_domain_config': purge => true }
370
+      keystone_domain_config { 'services::ldap/url':
371
+        value => 'http://auth.com/1',
372
+      }
373
+      EOM
374
+
375
+      context '/etc/keystone/domains/keystone.services.conf' do
376
+        it_behaves_like 'a_valid_configuration', <<-EOC
377
+
378
+[http]
379
+
380
+[ldap]
381
+url=http://auth.com/1
382
+EOC
383
+      end
384
+    end
385
+  end
262 386
 end

+ 62
- 0
spec/classes/keystone_spec.rb View File

@@ -106,6 +106,7 @@ describe 'keystone' do
106 106
       'rabbit_heartbeat_timeout_threshold'  => '60',
107 107
       'rabbit_heartbeat_rate'               => '10',
108 108
       'default_domain'                      => 'other_domain',
109
+      'using_domain_config'                 => false
109 110
     }
110 111
 
111 112
   httpd_params = {'service_name' => 'httpd'}.merge(default_params)
@@ -1007,4 +1008,65 @@ describe 'keystone' do
1007 1008
     it_configures 'when configuring default domain'
1008 1009
   end
1009 1010
 
1011
+  describe "when configuring using_domain_config" do
1012
+    describe 'with default config' do
1013
+      let :params do
1014
+        default_params
1015
+      end
1016
+      it { is_expected.to_not contain_file('/etc/keystone/domains') }
1017
+    end
1018
+    describe 'when using domain config' do
1019
+      let :params do
1020
+        default_params.merge({
1021
+          'using_domain_config'=> true,
1022
+        })
1023
+      end
1024
+      it { is_expected.to contain_file('/etc/keystone/domains').with(
1025
+        'ensure' => "directory",
1026
+      ) }
1027
+      it { is_expected
1028
+          .to contain_keystone_config('identity/domain_specific_drivers_enabled')
1029
+          .with('value' => true,
1030
+      ) }
1031
+      it { is_expected
1032
+          .to contain_keystone_config('identity/domain_config_dir')
1033
+          .with('value' => '/etc/keystone/domains',
1034
+      ) }
1035
+    end
1036
+    describe 'when using domain config and a wrong directory' do
1037
+      let :params do
1038
+        default_params.merge({
1039
+          'using_domain_config'=> true,
1040
+          'domain_config_directory' => 'this/is/not/an/absolute/path'
1041
+        })
1042
+      end
1043
+      it 'should raise an error' do
1044
+        expect { should contain_file('/etc/keystone/domains') }
1045
+          .to raise_error(Puppet::Error, %r(this/is/not/an/absolute/path" is not))
1046
+      end
1047
+    end
1048
+    describe 'when setting domain directory and not using domain config' do
1049
+      let :params do
1050
+        default_params.merge({
1051
+          'using_domain_config'=> false,
1052
+          'domain_config_directory' => '/this/is/an/absolute/path'
1053
+        })
1054
+      end
1055
+      it 'should raise an error' do
1056
+        expect { should contain_file('/etc/keystone/domains') }
1057
+          .to raise_error(Puppet::Error, %r(You must activate domain))
1058
+      end
1059
+    end
1060
+    describe 'when setting domain directory and using domain config' do
1061
+      let :params do
1062
+        default_params.merge({
1063
+          'using_domain_config'=> true,
1064
+          'domain_config_directory' => '/this/is/an/absolute/path'
1065
+        })
1066
+      end
1067
+      it { is_expected.to contain_file('/this/is/an/absolute/path').with(
1068
+        'ensure' => "directory",
1069
+      ) }
1070
+    end
1071
+  end
1010 1072
 end

+ 34
- 0
spec/shared_examples_acceptance.rb View File

@@ -0,0 +1,34 @@
1
+# Test a normal puppet run with idempotency.
2
+shared_examples_for 'puppet_apply_success' do |manifest|
3
+  it 'should apply the manifest without error' do
4
+    apply_manifest(manifest, :catch_failures => true)
5
+  end
6
+  it 'should be idempotent' do
7
+    apply_manifest(manifest, :catch_changes => true)
8
+  end
9
+end
10
+
11
+# Check that a file exists and its content match the one given as
12
+# argument.  The argument can be a multiline string or an array of
13
+# regexp.
14
+#
15
+# To use it encapsulate it in a context whose name is the file to
16
+# test.
17
+shared_examples 'a_valid_configuration' do |config_content|
18
+  let(:configuration_file) do |example|
19
+    # see the idiom it leads to later in this file
20
+    example.metadata[:example_group][:parent_example_group][:description]
21
+  end
22
+  subject { file(configuration_file) }
23
+  it { is_expected.to be_file }
24
+  it { is_expected.to exist }
25
+  content = nil
26
+  if config_content.is_a?(Array)
27
+    content = config_content
28
+  else
29
+    content = config_content.split("\n").map { |l| Regexp.quote(l) }
30
+  end
31
+  it 'content should be valid' do
32
+    expect(subject.content).to include_regexp(content)
33
+  end
34
+end

+ 1
- 0
spec/spec_helper_acceptance.rb View File

@@ -1,5 +1,6 @@
1 1
 require 'beaker-rspec'
2 2
 require 'beaker/puppet_install_helper'
3
+require 'shared_examples_acceptance'
3 4
 
4 5
 run_puppet_install_helper
5 6
 

+ 139
- 0
spec/unit/provider/keystone_domain_config/openstack_spec.rb View File

@@ -0,0 +1,139 @@
1
+#
2
+# these tests are a little concerning b/c they are hacking around the
3
+# modulepath, so these tests will not catch issues that may eventually arise
4
+# related to loading these plugins.
5
+# I could not, for the life of me, figure out how to programatcally set the modulepath
6
+$LOAD_PATH.push(
7
+  File.join(
8
+    File.dirname(__FILE__),
9
+    '..',
10
+    '..',
11
+    '..',
12
+    'fixtures',
13
+    'modules',
14
+    'inifile',
15
+    'lib')
16
+)
17
+require 'spec_helper'
18
+provider_class = Puppet::Type.type(:keystone_domain_config).provider(:openstack)
19
+
20
+describe provider_class do
21
+
22
+  include PuppetlabsSpec::Files
23
+  let(:tmpfile) { tmpfilename('keystone.conf') }
24
+
25
+  context '#interface' do
26
+    it 'should configure a domain file if the name has :: delimiter' do
27
+      resource = Puppet::Type::Keystone_domain_config.new(
28
+        { :name => 'bar::dude/foo', :value => 'blahh' }
29
+      )
30
+      provider = provider_class.new(resource)
31
+      expect(provider.path).to eq('/etc/keystone/domains/keystone.bar.conf')
32
+      expect(provider.section).to eq('dude')
33
+      expect(provider.setting).to eq('foo')
34
+    end
35
+
36
+    it "should raise an error if the configuration directory doesn't exist" do
37
+      resource = Puppet::Type::Keystone_domain_config.new(
38
+        { :name => 'bar::dude/foo', :value => 'blahh' }
39
+      )
40
+      expect { provider_class.new(resource).create }
41
+        .to raise_error(Puppet::Error::OpenstackMissingDomainDir)
42
+    end
43
+  end
44
+
45
+  context '#create' do
46
+    before(:example) do
47
+      # This is created just to get access to the ini_file.
48
+      config = Puppet::Type::Keystone_config.new({
49
+        :name  => 'identity/domain_config_dir',
50
+        :value => '/tmp'
51
+      })
52
+      config_provider = Puppet::Type.type(:keystone_config)
53
+        .provider(:ini_setting)
54
+      keystone_config = config_provider.new(config)
55
+      keystone_config.class.expects(:file_path).at_least_once.returns(tmpfile)
56
+      keystone_config.create
57
+
58
+      @domain = Puppet::Type::Keystone_domain_config.new(
59
+        { :name => 'bar::dude/foo', :value => 'blahh' }
60
+      )
61
+      @domain_provider = provider_class.new(@domain)
62
+    end
63
+
64
+    after(:example) do
65
+      Dir.glob('/tmp/keystone.*.conf').each do |tmp_conf|
66
+        File.delete(tmp_conf)
67
+      end
68
+    end
69
+
70
+    context 'correct name definition' do
71
+      it 'should adjust the domain path if it is modified with Keystone_config' do
72
+        expect(@domain_provider.file_path)
73
+          .to eq('/tmp/keystone.bar.conf')
74
+      end
75
+
76
+      it 'should fill a domain configuration correctly' do
77
+        expect { @domain_provider.create }.not_to raise_error
78
+        expect(File).to exist('/tmp/keystone.bar.conf')
79
+        expect(File.read('/tmp/keystone.bar.conf'))
80
+          .to eq('
81
+[dude]
82
+foo=blahh
83
+')
84
+      end
85
+
86
+      it 'should fill multiple domain configurations correctly' do
87
+        baz_domain = Puppet::Type::Keystone_domain_config.new(
88
+          { :name => 'baz::duck/go', :value => 'where' }
89
+        )
90
+        baz_domain_provider = provider_class.new(baz_domain)
91
+
92
+        expect { @domain_provider.create }.not_to raise_error
93
+        expect { baz_domain_provider.create }.not_to raise_error
94
+
95
+        expect(File).to exist('/tmp/keystone.bar.conf')
96
+        expect(File).to exist('/tmp/keystone.baz.conf')
97
+
98
+        expect(File.read('/tmp/keystone.bar.conf'))
99
+          .to eq('
100
+[dude]
101
+foo=blahh
102
+')
103
+
104
+        expect(File.read('/tmp/keystone.baz.conf'))
105
+          .to eq('
106
+[duck]
107
+go=where
108
+')
109
+      end
110
+
111
+      it 'should find the instance' do
112
+        @domain_provider.create
113
+        instances = @domain_provider.class.instances
114
+        expect(instances.count).to eq(1)
115
+        expect(
116
+          instances[0].instance_variable_get('@property_hash')[:name]
117
+        ).to eq('bar::dude/foo')
118
+      end
119
+    end
120
+
121
+    context 'invalid name definition' do
122
+      it 'should raise an error if no domain is given' do
123
+        resource = Puppet::Type::Keystone_domain_config.new(
124
+          { :name => 'dude/foo', :value => 'blahh' }
125
+        )
126
+        expect { provider_class.new(resource).create }
127
+          .to raise_error(Puppet::Error::OpenstackMissingDomainName)
128
+      end
129
+
130
+      it 'should raise an error if an empty domain is given' do
131
+        resource = Puppet::Type::Keystone_domain_config.new(
132
+          { :name => '::dude/foo', :value => 'blahh' }
133
+        )
134
+        expect { provider_class.new(resource).create }
135
+          .to raise_error(Puppet::Error::OpenstackMissingDomainName)
136
+      end
137
+    end
138
+  end
139
+end

+ 35
- 0
spec/unit/type/keystone_domain_config_spec.rb View File

@@ -0,0 +1,35 @@
1
+require 'puppet'
2
+require 'puppet/type/keystone_domain_config'
3
+
4
+describe 'Puppet::Type.type(:keystone_domain_config)' do
5
+  let(:keystone_domain_config) do
6
+    Puppet::Type.type(:keystone_domain_config)
7
+      .new(:name => 'service::DEFAULT/foo', :value => 'bar')
8
+  end
9
+
10
+  let(:catalog) { Puppet::Resource::Catalog.new }
11
+
12
+  it 'should autorequire the directory holding the configurations' do
13
+    directory = Puppet::Type.type(:file).new(
14
+      :name   => '/etc/keystone/domains',
15
+      :ensure => 'directory'
16
+    )
17
+    catalog.add_resource directory, keystone_domain_config
18
+    dependency = keystone_domain_config.autorequire
19
+    expect(dependency.size).to eq(1)
20
+    expect(dependency[0].target).to eq(keystone_domain_config)
21
+    expect(dependency[0].source).to eq(directory)
22
+  end
23
+
24
+  it 'should autorequire the keystone_config identity/domain_config_dir' do
25
+    keystone_config = Puppet::Type.type(:keystone_config).new(
26
+      :name   => 'identity/domain_config_dir',
27
+      :value  => '/tmp'
28
+    )
29
+    catalog.add_resource keystone_config, keystone_domain_config
30
+    dependency = keystone_domain_config.autorequire
31
+    expect(dependency.size).to eq(1)
32
+    expect(dependency[0].target).to eq(keystone_domain_config)
33
+    expect(dependency[0].source).to eq(keystone_config)
34
+  end
35
+end

Loading…
Cancel
Save