diff --git a/README.md b/README.md index 560328436..c81ae8dab 100644 --- a/README.md +++ b/README.md @@ -97,8 +97,31 @@ keystone_service { 'nova': description => 'Openstack Compute Service', } +``` + +Services can also be written with the type as a suffix: + +```puppet +keystone_service { 'nova::type': + ensure => present, + description => 'Openstack Compute Service', +} + + # Setup nova keystone endpoint keystone_endpoint { 'example-1-west/nova': + ensure => present, + type => 'compute', + public_url => "http://127.0.0.1:8774/v2/%(tenant_id)s", + admin_url => "http://127.0.0.1:8774/v2/%(tenant_id)s", + internal_url => "http://127.0.0.1:8774/v2/%(tenant_id)s", +} +``` + +Endpoints can also be written with the type as a suffix: + +```puppet +keystone_endpoint { 'example-1-west/nova::compute': ensure => present, public_url => "http://127.0.0.1:8774/v2/%(tenant_id)s", admin_url => "http://127.0.0.1:8774/v2/%(tenant_id)s", @@ -106,6 +129,11 @@ keystone_endpoint { 'example-1-west/nova': } ``` +Defining a endpoint without the type is supported in Liberty release +for backward compatibility, but will be dropped in Mitaka, as this can +lead to corruption of the endpoint database if omitted. See (this +bug)[https://bugs.launchpad.net/puppet-keystone/+bug/1506996] + **Setting up a database for keystone** A keystone database can be configured separately from the keystone services. diff --git a/lib/puppet/provider/keystone_endpoint/openstack.rb b/lib/puppet/provider/keystone_endpoint/openstack.rb index 83c5dc654..410d49291 100644 --- a/lib/puppet/provider/keystone_endpoint/openstack.rb +++ b/lib/puppet/provider/keystone_endpoint/openstack.rb @@ -7,6 +7,10 @@ Puppet::Type.type(:keystone_endpoint).provide( desc "Provider to manage keystone endpoints." + include PuppetX::Keystone::CompositeNamevar::Helpers + + @endpoints = nil + @services = nil @credentials = Puppet::Provider::Openstack::CredentialsV3.new def initialize(value={}) @@ -15,16 +19,42 @@ Puppet::Type.type(:keystone_endpoint).provide( end def create - region, name = resource[:name].split('/') + # Reset the cache. + self.class.services = nil + name = resource[:name] + region = resource[:region] + type = resource[:type] + type = self.class.type_from_service(name) unless set?(:type) + @property_hash[:type] = type + services = self.class.services.find_all { |s| s[:name] == name } + service = services.find { |s| s[:type] == type } + + if service.nil? && services.count == 1 + # For backward comptatibility, match the service by name only. + name = services[0][:id] + else + # Math the service by id. + name = service[:id] if service + end ids = [] + + created = false [:admin_url, :internal_url, :public_url].each do |scope| if resource[scope] - ids << endpoint_create(name, region, scope.to_s.sub(/_url$/,''), - resource[scope])[:id] + created = true + ids << endpoint_create(name, region, scope.to_s.sub(/_url$/, ''), + resource[scope])[:id] end end - @property_hash[:id] = ids.join(',') - @property_hash[:ensure] = :present + if created + @property_hash[:id] = ids.join(',') + @property_hash[:ensure] = :present + else + warning('Specifying a keystone_endpoint without an ' \ + 'admin_url/public_url/internal_url ' \ + "won't create the endpoint at all, despite what Puppet is saying.") + @property_hash[:ensure] = :absent + end end def destroy @@ -53,21 +83,22 @@ Puppet::Type.type(:keystone_endpoint).provide( @property_flush[:admin_url] = value end - def region=(value) - raise(Puppet::Error, "Updating the endpoint's region is not currently supported.") + def region=(_) + fail(Puppet::Error, "Updating the endpoint's region is not currently supported.") end def self.instances - names=[] - list=[] - endpoints = request('endpoint', 'list') + names = [] + list = [] endpoints.each do |current| - name = "#{current[:region]}/#{current[:service_name]}" + name = transform_name(current[:region], current[:service_name], current[:service_type]) unless names.include?(name) names << name endpoint = { :name => name, current[:interface].to_sym => current } endpoints.each do |ep_osc| - if (ep_osc[:id] != current[:id]) && (ep_osc[:service_name] == current[:service_name]) + if (ep_osc[:id] != current[:id]) && + (ep_osc[:service_name] == current[:service_name]) && + (ep_osc[:service_type] == current[:service_type]) endpoint.merge!(ep_osc[:interface].to_sym => ep_osc) end end @@ -88,11 +119,11 @@ Puppet::Type.type(:keystone_endpoint).provide( end def self.prefetch(resources) - endpoints = instances - resources.keys.each do |name| - if provider = endpoints.find{ |endpoint| endpoint.name == name } - resources[name].provider = provider - end + prefetch_composite(resources) do |sorted_namevars| + name = sorted_namevars[0] + region = sorted_namevars[1] + type = sorted_namevars[2] + transform_name(region, name, type) end end @@ -118,4 +149,74 @@ Puppet::Type.type(:keystone_endpoint).provide( properties = [name, interface, url, '--region', region] self.class.request('endpoint', 'create', properties) end + + private + + def self.endpoints + return @endpoints unless @endpoints.nil? + @endpoints = request('endpoint', 'list') + end + + def self.endpoints=(value) + @endpoints = value + end + + def self.services + return @services unless @services.nil? + @services = request('service', 'list') + end + + def self.services=(value) + @services = value + end + + def self.endpoint_from_region_name(region, name) + endpoints.find_all { |e| e[:region] == region && e[:service_name] == name } + .map { |e| e[:service_type] }.uniq + end + + def self.type_from_service(name) + types = services.find_all { |s| s[:name] == name }.map { |e| e[:type] }.uniq + if types.count == 1 + types[0] + else + # We don't fail here as it can happen during a ensure => absent. + PuppetX::Keystone::CompositeNamevar::Unset + end + end + + def self.service_type(services, region, name) + nbr_of_services = services.count + err_msg = ["endpoint matching #{region}/#{name}:"] + type = nil + + case + when nbr_of_services == 1 + type = services[0] + when nbr_of_services > 1 + err_msg += [endpoint_from_region_name(region, name).join(' ')] + when nbr_of_services < 1 + # Then we try to get the type by service name. + type = type_from_service(name) + end + + if !type.nil? + type + else + fail(Puppet::Error, 'Cannot get the correct endpoint type: ' \ + "#{err_msg.join(' ')}") + end + end + + def self.transform_name(region, name, type) + if type == PuppetX::Keystone::CompositeNamevar::Unset + type = service_type(endpoint_from_region_name(region, name), region, name) + end + if type == PuppetX::Keystone::CompositeNamevar::Unset + Puppet.debug("Could not find the type for endpoint #{region}/#{name}") + "#{region}/#{name}" + else + "#{region}/#{name}::#{type}" + end + end end diff --git a/lib/puppet/type/keystone_endpoint.rb b/lib/puppet/type/keystone_endpoint.rb index 55d69f427..e7d8ee309 100644 --- a/lib/puppet/type/keystone_endpoint.rb +++ b/lib/puppet/type/keystone_endpoint.rb @@ -1,34 +1,43 @@ # LP#1408531 File.expand_path('../..', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) } File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) } +require 'puppet_x/keystone/composite_namevar' +require 'puppet_x/keystone/type' Puppet::Type.newtype(:keystone_endpoint) do desc 'Type for managing keystone endpoints.' + include PuppetX::Keystone::CompositeNamevar::Helpers ensurable - newparam(:name, :namevar => true) do - newvalues(/\S+\/\S+/) - end + newparam(:name, :namevar => true) newproperty(:id) do - validate do |v| - raise(Puppet::Error, 'This is a read only property') + include PuppetX::Keystone::Type::ReadOnly + end + + newparam(:region) do + isnamevar + include PuppetX::Keystone::Type::Required + end + + newparam(:type) do + isnamevar + defaultto do + deprecation_msg = 'Support for a endpoint without the type ' \ + 'set is deprecated in Liberty. ' \ + 'It will be dropped in Mitaka.' + warning(deprecation_msg) + PuppetX::Keystone::CompositeNamevar::Unset end end - newproperty(:region) do - end + newproperty(:public_url) - newproperty(:public_url) do - end + newproperty(:internal_url) - newproperty(:internal_url) do - end - - newproperty(:admin_url) do - end + newproperty(:admin_url) # we should not do anything until the keystone service is started autorequire(:anchor) do @@ -36,7 +45,54 @@ Puppet::Type.newtype(:keystone_endpoint) do end autorequire(:keystone_service) do - (region, service_name) = self[:name].split('/') - [service_name] + if parameter_set?(:type) + "#{name}::#{self[:type]}" + else + title = catalog.resources + .find_all { |e| e.type == :keystone_service && e[:name] == name } + .map { |e| e.title }.uniq + if title.count == 1 + title + else + warning("Couldn't find the type of the domain to require using #{name}") + name + end + end + end + + def self.title_patterns + name = PuppetX::Keystone::CompositeNamevar.not_two_colon_regex + type = Regexp.new(/.+/) + region = Regexp.new(/[^\/]+/) + [ + [ + /^(#{region})\/(#{name})::(#{type})$/, + [ + [:region], + [:name], + [:type] + ] + ], + [ + /^(#{region})\/(#{name})$/, + [ + [:region], + [:name] + ] + ], + [ + /^(#{name})::(#{type})$/, + [ + [:name], + [:type] + ] + ], + [ + /^(#{name})$/, + [ + [:name] + ] + ] + ] end end diff --git a/lib/puppet_x/keystone/type/required.rb b/lib/puppet_x/keystone/type/required.rb index d8f3538aa..13ae26f40 100644 --- a/lib/puppet_x/keystone/type/required.rb +++ b/lib/puppet_x/keystone/type/required.rb @@ -5,8 +5,13 @@ module PuppetX def self.included(klass) klass.class_eval do defaultto do + custom = '' + if respond_to?(:required_custom_message) + custom = send(:required_custom_message) + end fail(Puppet::ResourceError, - "Parameter #{name} failed on " \ + "#{custom}" \ + "Parameter #{name} failed on " \ "#{resource.class.to_s.split('::')[-1]}[#{resource.name}]: " \ 'Required parameter.') end diff --git a/spec/acceptance/basic_keystone_spec.rb b/spec/acceptance/basic_keystone_spec.rb index bf2d93933..3adcc9ff2 100644 --- a/spec/acceptance/basic_keystone_spec.rb +++ b/spec/acceptance/basic_keystone_spec.rb @@ -234,16 +234,49 @@ describe 'basic keystone server with resources' do end end end - describe 'composite namevar for keystone_service' do + + describe 'composite namevar for keystone_service and keystone_endpoint' do let(:pp) do <<-EOM keystone_service { 'service_1::type_1': ensure => present } keystone_service { 'service_1': type => 'type_2', ensure => present } + keystone_endpoint { 'RegionOne/service_1::type_2': + ensure => present, + public_url => 'http://public_service1_type2', + internal_url => 'http://internal_service1_type2', + admin_url => 'http://admin_service1_type2' + } + keystone_endpoint { 'service_1': + ensure => present, + region => 'RegionOne', + type => 'type_1', + public_url => 'http://public_url/', + internal_url => 'http://public_url/', + admin_url => 'http://public_url/' + } EOM end it 'should be possible to create two services different only by their type' do apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) end + describe 'puppet service are created' do + it 'for service' do + shell('puppet resource keystone_service') do |result| + expect(result.stdout) + .to include_regexp([/keystone_service { 'service_1::type_1':/, + /keystone_service { 'service_1::type_2':/]) + end + end + end + describe 'puppet endpoints are created' do + it 'for service' do + shell('puppet resource keystone_endpoint') do |result| + expect(result.stdout) + .to include_regexp([/keystone_endpoint { 'RegionOne\/service_1::type_1':/, + /keystone_endpoint { 'RegionOne\/service_1::type_2':/]) + end + end + end end end diff --git a/spec/acceptance/keystone_wsgi_apache_spec.rb b/spec/acceptance/keystone_wsgi_apache_spec.rb index ecaafc6ac..23f59c4bc 100644 --- a/spec/acceptance/keystone_wsgi_apache_spec.rb +++ b/spec/acceptance/keystone_wsgi_apache_spec.rb @@ -215,4 +215,48 @@ describe 'keystone server running with Apache/WSGI with resources' do apply_manifest(pp, :catch_changes => true) end end + describe 'composite namevar for keystone_service and keystone_endpoint' do + let(:pp) do + <<-EOM + keystone_service { 'service_1::type_1': ensure => present } + keystone_service { 'service_1': type => 'type_2', ensure => present } + keystone_endpoint { 'RegionOne/service_1::type_2': + ensure => present, + public_url => 'http://public_service1_type2', + internal_url => 'http://internal_service1_type2', + admin_url => 'http://admin_service1_type2' + } + keystone_endpoint { 'service_1': + ensure => present, + region => 'RegionOne', + type => 'type_1', + public_url => 'http://public_url/', + internal_url => 'http://public_url/', + admin_url => 'http://public_url/' + } + EOM + end + it 'should be possible to create two services different only by their type' do + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + describe 'puppet service are created' do + it 'for service' do + shell('puppet resource keystone_service') do |result| + expect(result.stdout) + .to include_regexp([/keystone_service { 'service_1::type_1':/, + /keystone_service { 'service_1::type_2':/]) + end + end + end + describe 'puppet endpoints are created' do + it 'for service' do + shell('puppet resource keystone_endpoint') do |result| + expect(result.stdout) + .to include_regexp([/keystone_endpoint { 'RegionOne\/service_1::type_1':/, + /keystone_endpoint { 'RegionOne\/service_1::type_2':/]) + end + end + end + end end diff --git a/spec/unit/provider/keystone_endpoint/openstack_spec.rb b/spec/unit/provider/keystone_endpoint/openstack_spec.rb index ffa5b14a0..9bc38922d 100644 --- a/spec/unit/provider/keystone_endpoint/openstack_spec.rb +++ b/spec/unit/provider/keystone_endpoint/openstack_spec.rb @@ -2,9 +2,7 @@ require 'puppet' require 'spec_helper' require 'puppet/provider/keystone_endpoint/openstack' -provider_class = Puppet::Type.type(:keystone_endpoint).provider(:openstack) - -describe provider_class do +describe Puppet::Type.type(:keystone_endpoint).provider(:openstack) do let(:set_env) do ENV['OS_USERNAME'] = 'test' @@ -17,11 +15,11 @@ describe provider_class do let(:endpoint_attrs) do { - :name => 'region/endpoint', + :title => 'region/endpoint', :ensure => 'present', :public_url => 'http://127.0.0.1:5000', :internal_url => 'http://127.0.0.1:5001', - :admin_url => 'http://127.0.0.1:5002', + :admin_url => 'http://127.0.0.1:5002' } end @@ -30,47 +28,79 @@ describe provider_class do end let(:provider) do - provider_class.new(resource) + described_class.new(resource) end before(:each) do set_env + described_class.endpoints = nil + described_class.services = nil end describe '#create' do - it 'creates an endpoint' do - provider.class.expects(:openstack) - .with('endpoint', 'create', '--format', 'shell', ['endpoint', 'admin', 'http://127.0.0.1:5002', '--region', 'region']) + before(:each) do + described_class.expects(:openstack) + .with('endpoint', 'create', '--format', 'shell', + ['service_id1', 'admin', 'http://127.0.0.1:5002', '--region', 'region']) .returns('admin_url="http://127.0.0.1:5002" id="endpoint1_id" region="region" ') - provider.class.expects(:openstack) - .with('endpoint', 'create', '--format', 'shell', ['endpoint', 'internal', 'http://127.0.0.1:5001', '--region', 'region']) + described_class.expects(:openstack) + .with('endpoint', 'create', '--format', 'shell', + ['service_id1', 'internal', 'http://127.0.0.1:5001', '--region', 'region']) .returns('internal_url="http://127.0.0.1:5001" id="endpoint2_id" region="region" ') - provider.class.expects(:openstack) - .with('endpoint', 'create', '--format', 'shell', ['endpoint', 'public', 'http://127.0.0.1:5000', '--region', 'region']) + described_class.expects(:openstack) + .with('endpoint', 'create', '--format', 'shell', + ['service_id1', 'public', 'http://127.0.0.1:5000', '--region', 'region']) .returns('public_url="http://127.0.0.1:5000" id="endpoint3_id" region="region" ') - provider.create - expect(provider.exists?).to be_truthy - expect(provider.id).to eq('endpoint1_id,endpoint2_id,endpoint3_id') + described_class.expects(:openstack) + .with('service', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Name","Type" +"service_id1","endpoint","type_one" +') + end + context 'without the type' do + it 'creates an endpoint' do + provider.create + expect(provider.exists?).to be_truthy + expect(provider.id).to eq('endpoint1_id,endpoint2_id,endpoint3_id') + end + end + context 'with the type' do + let(:endpoint_attrs) do + { + :title => 'region/endpoint', + :ensure => 'present', + :public_url => 'http://127.0.0.1:5000', + :internal_url => 'http://127.0.0.1:5001', + :admin_url => 'http://127.0.0.1:5002', + :type => 'type_one' + } + end + + it 'creates an endpoint' do + provider.create + expect(provider.exists?).to be_truthy + expect(provider.id).to eq('endpoint1_id,endpoint2_id,endpoint3_id') + end end end describe '#destroy' do it 'destroys an endpoint' do provider.instance_variable_get('@property_hash')[:id] = 'endpoint1_id,endpoint2_id,endpoint3_id' - provider.class.expects(:openstack) + described_class.expects(:openstack) .with('endpoint', 'delete', 'endpoint1_id') - provider.class.expects(:openstack) + described_class.expects(:openstack) .with('endpoint', 'delete', 'endpoint2_id') - provider.class.expects(:openstack) + described_class.expects(:openstack) .with('endpoint', 'delete', 'endpoint3_id') provider.destroy expect(provider.exists?).to be_falsey @@ -80,7 +110,7 @@ region="region" describe '#exists' do context 'when tenant does not exist' do subject(:response) do - response = provider.exists? + provider.exists? end it { is_expected.to be_falsey } @@ -89,16 +119,130 @@ region="region" describe '#instances' do it 'finds every tenant' do - provider.class.expects(:openstack) + described_class.expects(:openstack) .with('endpoint', 'list', '--quiet', '--format', 'csv', []) .returns('"ID","Region","Service Name","Service Type","Enabled","Interface","URL" "endpoint1_id","RegionOne","keystone","identity",True,"admin","http://127.0.0.1:5002" "endpoint2_id","RegionOne","keystone","identity",True,"internal","https://127.0.0.1:5001" "endpoint3_id","RegionOne","keystone","identity",True,"public","https://127.0.0.1:5000" ') - instances = Puppet::Type::Keystone_endpoint::ProviderOpenstack.instances + instances = described_class.instances expect(instances.count).to eq(1) end end + + describe '#prefetch' do + context 'working: fq or nfq and matching resource' do + before(:each) do + described_class.expects(:openstack) + .with('endpoint', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Region","Service Name","Service Type","Enabled","Interface","URL" +"endpoint1_id","RegionOne","keystone","identity",True,"admin","http://127.0.0.1:5002" +"endpoint2_id","RegionOne","keystone","identity",True,"internal","https://127.0.0.1:5001" +"endpoint3_id","RegionOne","keystone","identity",True,"public","https://127.0.0.1:5000" +') + end + context '#fq resource in title' do + let(:resources) do + [Puppet::Type.type(:keystone_endpoint).new(:title => 'RegionOne/keystone::identity', :ensure => :present), + Puppet::Type.type(:keystone_endpoint).new(:title => 'RegionOne/keystone::identityv3', :ensure => :present)] + end + include_examples 'prefetch the resources' + end + context '#fq resource' do + let(:resources) do + [Puppet::Type.type(:keystone_endpoint).new(:title => 'keystone', :region => 'RegionOne', :type => 'identity', :ensure => :present), + Puppet::Type.type(:keystone_endpoint).new(:title => 'RegionOne/keystone::identityv3', :ensure => :present)] + end + include_examples 'prefetch the resources' + end + context '#nfq resource in title matching existing endpoint' do + let(:resources) do + [Puppet::Type.type(:keystone_endpoint).new(:title => 'RegionOne/keystone', :ensure => :present), + Puppet::Type.type(:keystone_endpoint).new(:title => 'RegionOne/keystone::identityv3', :ensure => :present)] + end + include_examples 'prefetch the resources' + end + context '#nfq resource matching existing endpoint' do + let(:resources) do + [Puppet::Type.type(:keystone_endpoint).new(:title => 'keystone', :region => 'RegionOne', :ensure => :present), + Puppet::Type.type(:keystone_endpoint).new(:title => 'RegionOne/keystone::identityv3', :ensure => :present)] + end + include_examples 'prefetch the resources' + end + end + + context 'not working' do + context 'too many type' do + before(:each) do + described_class.expects(:openstack) + .with('endpoint', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Region","Service Name","Service Type","Enabled","Interface","URL" +"endpoint1_id","RegionOne","keystone","identity",True,"admin","http://127.0.0.1:5002" +"endpoint2_id","RegionOne","keystone","identity",True,"internal","https://127.0.0.1:5001" +"endpoint3_id","RegionOne","keystone","identity",True,"public","https://127.0.0.1:5000" +"endpoint4_id","RegionOne","keystone","identityv3",True,"admin","http://127.0.0.1:5002" +"endpoint5_id","RegionOne","keystone","identityv3",True,"internal","https://127.0.0.1:5001" +"endpoint6_id","RegionOne","keystone","identityv3",True,"public","https://127.0.0.1:5000" +') + end + it "should fail as it's not possible to get the right type here" do + existing = Puppet::Type.type(:keystone_endpoint) + .new(:title => 'RegionOne/keystone', :ensure => :present) + resource = mock + r = [] + r << existing + + catalog = Puppet::Resource::Catalog.new + r.each { |res| catalog.add_resource(res) } + m_value = mock + m_first = mock + resource.expects(:values).returns(m_value) + m_value.expects(:first).returns(m_first) + m_first.expects(:catalog).returns(catalog) + m_first.expects(:class).returns(described_class.resource_type) + expect { described_class.prefetch(resource) } + .to raise_error(Puppet::Error, + /endpoint matching RegionOne\/keystone: identity identityv3/) + end + end + end + + context 'not any type but existing service' do + before(:each) do + described_class.expects(:openstack) + .with('endpoint', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Region","Service Name","Service Type","Enabled","Interface","URL" +"endpoint1_id","RegionOne","keystone","identity",True,"admin","http://127.0.0.1:5002" +"endpoint2_id","RegionOne","keystone","identity",True,"internal","https://127.0.0.1:5001" +"endpoint3_id","RegionOne","keystone","identity",True,"public","https://127.0.0.1:5000" +') + described_class.expects(:openstack) + .with('service', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Name","Type" +"service1_id","keystonev3","identity" +') + end + it 'should be successful' do + existing = Puppet::Type.type(:keystone_endpoint) + .new(:title => 'RegionOne/keystonev3', :ensure => :present) + resource = mock + r = [] + r << existing + + catalog = Puppet::Resource::Catalog.new + r.each { |res| catalog.add_resource(res) } + m_value = mock + m_first = mock + resource.expects(:values).returns(m_value) + m_value.expects(:first).returns(m_first) + m_first.expects(:catalog).returns(catalog) + m_first.expects(:class).returns(described_class.resource_type) + + expect { described_class.prefetch(resource) }.not_to raise_error + expect(existing.provider.ensure).to eq(:absent) + end + end + end end end diff --git a/spec/unit/type/keystone_endpoint_spec.rb b/spec/unit/type/keystone_endpoint_spec.rb index a3667c546..d13d0bb41 100644 --- a/spec/unit/type/keystone_endpoint_spec.rb +++ b/spec/unit/type/keystone_endpoint_spec.rb @@ -1,9 +1,60 @@ +require 'spec_helper' +require 'puppet' +require 'puppet/type/keystone_endpoint' + describe Puppet::Type.type(:keystone_endpoint) do - it 'should fail when the namevar does not contain a region' do - expect do - Puppet::Type.type(:keystone_endpoint).new(:name => 'foo') - end.to raise_error(Puppet::Error, /Invalid value/) + describe 'region_one/endpoint_name::type_one' do + include_examples 'parse title correctly', + :name => 'endpoint_name', + :region => 'region_one', + :type => 'type_one' end + describe 'new_endpoint_without_region::type' do + include_examples 'croak on the required parameter', + 'Parameter region failed on Keystone_endpoint[new_endpoint_without_region]:' + end + + describe '#autorequire' do + let(:service_one) do + Puppet::Type.type(:keystone_service).new(:title => 'service_one', :type => 'type_one') + end + + let(:service_two) do + Puppet::Type.type(:keystone_service).new(:title => 'service_one::type_two') + end + + let(:service_three) do + Puppet::Type.type(:keystone_service).new(:title => 'service_two::type_one') + end + + context 'domain autorequire from title' do + let(:endpoint) do + Puppet::Type.type(:keystone_endpoint).new(:title => 'region_one/service_one::type_one') + end + describe 'should require the correct domain' do + let(:resources) { [endpoint, service_one, service_two] } + include_examples 'autorequire the correct resources' + end + end + context 'domain autorequire from title without type (to be removed at Mitaka)' do + let(:endpoint) do + Puppet::Type.type(:keystone_endpoint).new(:title => 'region_one/service_one') + end + describe 'should require the correct domain' do + let(:resources) { [endpoint, service_one, service_two] } + include_examples 'autorequire the correct resources' + end + end + context 'domain autorequire from title without type on fq service name (to be removed at Mitaka)' do + let(:endpoint) do + Puppet::Type.type(:keystone_endpoint).new(:title => 'region_one/service_two') + end + describe 'should require the correct domain' do + let(:resources) { [endpoint, service_three, service_one] } + include_examples 'autorequire the correct resources' + end + end + end end