diff --git a/lib/octocatalog-diff/catalog.rb b/lib/octocatalog-diff/catalog.rb index 356b5949..ce5c5cc8 100644 --- a/lib/octocatalog-diff/catalog.rb +++ b/lib/octocatalog-diff/catalog.rb @@ -304,17 +304,32 @@ def resources_missing_from_catalog(resources_to_check) unless res =~ /\A([\w:]+)\[(.+)\]\z/ raise ArgumentError, "Resource #{res} is not in the expected format" end - resource(type: Regexp.last_match(1), title: Regexp.last_match(2)).nil? + + type = Regexp.last_match(1) + title = normalized_title(Regexp.last_match(2), type) + resource(type: type, title: title).nil? end end + # Private method: Given a title string, normalize it according to the rules + # used by puppet 4.10.x for file resource title normalization: + # https://github.com/puppetlabs/puppet/blob/4.10.x/lib/puppet/type/file.rb#L42 + def normalized_title(title_string, type) + return title_string if type != 'File' + + matches = title_string.match(%r{^(?/|.+:/|.*[^/])/*\Z}m) + matches[:normalized_path] || title_string + end + # Private method: Build the resource hash to be used used for O(1) lookups by type and title. # This method is called the first time the resource hash is accessed. def build_resource_hash @resource_hash = {} resources.each do |resource| @resource_hash[resource['type']] ||= {} - @resource_hash[resource['type']][resource['title']] = resource + + title = normalized_title(resource['title'], resource['type']) + @resource_hash[resource['type']][title] = resource if resource.key?('parameters') && resource['parameters'].key?('alias') @resource_hash[resource['type']][resource['parameters']['alias']] = resource diff --git a/spec/octocatalog-diff/fixtures/repos/reference-validation/hieradata/roles/working-file.yaml b/spec/octocatalog-diff/fixtures/repos/reference-validation/hieradata/roles/working-file.yaml new file mode 100644 index 00000000..deec3152 --- /dev/null +++ b/spec/octocatalog-diff/fixtures/repos/reference-validation/hieradata/roles/working-file.yaml @@ -0,0 +1,3 @@ +--- + classes: + - test::file_tests diff --git a/spec/octocatalog-diff/fixtures/repos/reference-validation/modules/test/manifests/file_tests.pp b/spec/octocatalog-diff/fixtures/repos/reference-validation/modules/test/manifests/file_tests.pp new file mode 100644 index 00000000..9375be82 --- /dev/null +++ b/spec/octocatalog-diff/fixtures/repos/reference-validation/modules/test/manifests/file_tests.pp @@ -0,0 +1,10 @@ +class test::file_tests { + file { '/foo': + ensure => directory, + } + + file { '/bar': + ensure => directory, + require => File['/foo/'], + } +} diff --git a/spec/octocatalog-diff/integration/reference_validation_spec.rb b/spec/octocatalog-diff/integration/reference_validation_spec.rb index b35bc668..f4ea3de5 100644 --- a/spec/octocatalog-diff/integration/reference_validation_spec.rb +++ b/spec/octocatalog-diff/integration/reference_validation_spec.rb @@ -93,6 +93,26 @@ def self.catalog_contains_resource(result, type, title) end end + context 'with valid files that have trailing slashes' do + before(:all) do + @result = OctocatalogDiff::Spec.reference_validation_catalog('working-file', %w(require)) + end + + it 'should succeed' do + expect(@result.exitcode).to eq(0) + end + + it 'should not raise any exceptions' do + expect(@result.exception).to be_nil, OctocatalogDiff::Integration.format_exception(@result) + end + + it 'should contain representative resources' do + pending 'Catalog failed' unless @result.exitcode.zero? + expect(OctocatalogDiff::Spec.catalog_contains_resource(@result, 'File', '/foo')).to eq(true) + expect(OctocatalogDiff::Spec.catalog_contains_resource(@result, 'File', '/bar')).to eq(true) + end + end + context 'with broken subscribe' do before(:all) do @result = OctocatalogDiff::Spec.reference_validation_catalog('broken-subscribe', %w(subscribe)) diff --git a/spec/octocatalog-diff/tests/catalog_spec.rb b/spec/octocatalog-diff/tests/catalog_spec.rb index 84db4dd8..34dc598b 100644 --- a/spec/octocatalog-diff/tests/catalog_spec.rb +++ b/spec/octocatalog-diff/tests/catalog_spec.rb @@ -621,6 +621,14 @@ 'alias' => 'the exec', 'command' => '/bin/true' } + }, + { + 'type' => 'File', + 'title' => '/foo/' + }, + { + 'type' => 'File', + 'title' => '/bar' } ] described_object = described_class.allocate @@ -636,5 +644,14 @@ it 'should contain the entry for the aliased resource' do expect(@resource_hash['Exec']['the exec']).to be_a_kind_of(Hash) end + + it 'should normalize trailing slashes on file resources' do + expect(@resource_hash['File']['/foo']).to be_a_kind_of(Hash) + expect(@resource_hash['File']['/foo/']).to eq(nil) + end + + it 'should not otherwise touch file resources that do not need to be normalized' do + expect(@resource_hash['File']['/bar']).to be_a_kind_of(Hash) + end end end