Skip to content

Commit 5cddc78

Browse files
author
Anthony Riley II
authored
Merge branch 'master' into bugfix_crl
2 parents d73b92d + 5071b97 commit 5cddc78

File tree

14 files changed

+515
-7
lines changed

14 files changed

+515
-7
lines changed

doc/advanced-compare-file-text.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ However, since applying the catalog could change the content of a file on the ta
2121
This feature is available only when the catalogs are being compiled from local code. This feature is not available, and will be automatically disabled, when pulling catalogs from PuppetDB or a Puppet server.
2222

2323
Note: In Puppet >= 4.4 there is an option in Puppet itself called "static catalogs" which if enabled will cause the checksum of the file to be included in the catalog. However, the `octocatalog-diff` feature described here is still useful because it can be used to display a "diff" of the change rather than just displaying a "diff" of a checksum.
24+
2425
## Command line options
2526

2627
### `--compare-file-text` and `--no-compare-file-text`
@@ -39,6 +40,12 @@ If this feature is disabled by default in a configuration file, add `--compare-f
3940

4041
Note that the feature will be automatically disabled, regardless of configuration or command line options, if catalogs are being pulled from PuppetDB or a Puppet server.
4142

43+
### `--compare-file-text=force`
44+
45+
To force the option to be on even in situations when it would be auto-disabled, set the command line argument `--compare-file-text=force`. When the Puppet source code is available, e.g. when compiling a catalog with `--catalog-only`, this will adjust the resulting catalog.
46+
47+
If the Puppet source code is not available, forcing the feature on anyway may end up causing an exception. Use this option at your own risk.
48+
4249
### `--compare-file-text-ignore-tags`
4350

4451
To disable this feature for specific `file` resources, set a tag on the resources for which the comparison is undesired. For example:

doc/advanced-filter.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Here is the list of available filters and an explanation of each:
2424

2525
#### Description
2626

27-
When the `AbsentFile` filter is enabled, if any file is `ensure => absent` in the *new* catalog, then changes to any other parameters will be suppressed.
27+
When the `AbsentFile` filter is enabled, if any file is `ensure => absent` in the _new_ catalog, then changes to any other parameters will be suppressed.
2828

2929
Consider that a file resource is declared as follows in two catalogs:
3030

@@ -71,6 +71,25 @@ Wouldn't it be nice if the meaningless information didn't appear, and all you sa
7171
+ absent
7272
```
7373

74+
## Equivalent Array (not considering datatypes)
75+
76+
#### Usage
77+
78+
```
79+
--filters EquivalentArrayNoDatatypes
80+
```
81+
82+
#### Description
83+
84+
In an array, ignore changes where the old and new arrays are "equivalent" as described below. This is useful when octocatalog-diff is comparing changes between a catalog with stringified values and a catalog with non-stringified values.
85+
86+
The following are considered equivalent when this filter is engaged:
87+
88+
- Stringified integers (`[0, 1]` and `['0', '1']`)
89+
- Stringified floats (`[0.0, 1.0]` and `['0.0', '1.0']`)
90+
- Numerically-equal integers and floats (`[0, 1]` and `[0.0, 1.0]`)
91+
- Symbols and corresponding strings (`[:foo, :bar]` and `[':foo', ':bar']`)
92+
7493
## JSON
7594

7695
#### Usage
@@ -105,7 +124,7 @@ New: { "notify": [ "Service[foo]" ] }
105124
This filter will suppress differences for the value of a parameter when:
106125

107126
- The value in one catalog is an object, AND
108-
- The value in the other catalog is an array containing *only* that same object
127+
- The value in the other catalog is an array containing _only_ that same object
109128

110129
## YAML
111130

lib/octocatalog-diff/catalog-diff/differ.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ def hashdiff_initial(catalog1_in, catalog2_in)
558558

559559
# Added a new key that points to some kind of data structure that we know how
560560
# to handle.
561-
classes = [String, Integer, Float, TrueClass, FalseClass, Array, Hash]
561+
classes = [String, Integer, Float, TrueClass, FalseClass, Array, Hash, NilClass]
562562
if obj[1] =~ /^(.+)\f([^\f]+)$/ && OctocatalogDiff::Util::Util.object_is_any_of?(obj[2], classes)
563563
hashdiff_add_remove.add(obj[1])
564564
next

lib/octocatalog-diff/catalog-diff/filter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require_relative '../api/v1/diff'
22
require_relative 'filter/absent_file'
33
require_relative 'filter/compilation_dir'
4+
require_relative 'filter/equivalent_array_no_datatypes'
45
require_relative 'filter/json'
56
require_relative 'filter/single_item_array'
67
require_relative 'filter/yaml'
@@ -14,7 +15,7 @@ class Filter
1415
attr_accessor :logger
1516

1617
# List the available filters here (by class name) for use in the validator method.
17-
AVAILABLE_FILTERS = %w(AbsentFile CompilationDir JSON SingleItemArray YAML).freeze
18+
AVAILABLE_FILTERS = %w(AbsentFile CompilationDir EquivalentArrayNoDatatypes JSON SingleItemArray YAML).freeze
1819

1920
# Public: Determine whether a particular filter exists. This can be used to validate
2021
# a user-submitted filter.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../filter'
4+
5+
module OctocatalogDiff
6+
module CatalogDiff
7+
class Filter
8+
# Filter out changes in parameters where the elements of an array are the
9+
# same values but different data types. For example, this would filter out
10+
# the following diffs:
11+
# Exec[some command] =>
12+
# parameters =>
13+
# returns =>
14+
# - ["0", "1"]
15+
# + [0, 1]
16+
class EquivalentArrayNoDatatypes < OctocatalogDiff::CatalogDiff::Filter
17+
# Public: Implement the filter for arrays that have the same elements
18+
# but possibly different data types.
19+
#
20+
# @param diff [OctocatalogDiff::API::V1::Diff] Difference
21+
# @param _options [Hash] Additional options (there are none for this filter)
22+
# @return [Boolean] true if this should be filtered out, false otherwise
23+
def filtered?(diff, _options = {})
24+
# Skip additions or removals - focus only on changes
25+
return false unless diff.change?
26+
old_value = diff.old_value
27+
new_value = diff.new_value
28+
29+
# Skip unless both the old and new values are arrays.
30+
return false unless old_value.is_a?(Array) && new_value.is_a?(Array)
31+
32+
# Avoid generating comparable values if the arrays are a different
33+
# size, because there's no possible way that they are equivalent.
34+
return false unless old_value.size == new_value.size
35+
36+
# Generate and then compare the comparable arrays.
37+
old_value.map { |x| comparable_value(x) } == new_value.map { |x| comparable_value(x) }
38+
end
39+
40+
# Private: Get a more easily comparable value for an array element.
41+
# Integers, floats, and strings that look like integers or floats become
42+
# floats, and symbols are converted to string representation.
43+
#
44+
# @param input [any] Value to convert
45+
# @return [any] "Comparable" value
46+
def comparable_value(input)
47+
# Any string that looks like a number is converted to a float.
48+
if input.is_a?(String) && input =~ /\A-?(([0-9]*\.[0-9]+)|([0-9]+))\z/
49+
return input.to_f
50+
end
51+
52+
# Any number is converted to a float
53+
return input.to_f if input.is_a?(Integer) || input.is_a?(Float)
54+
55+
# Symbols are converted to ":xxx" strings.
56+
return ":#{input}" if input.is_a?(Symbol)
57+
58+
# Everything else is unconverted.
59+
input
60+
end
61+
end
62+
end
63+
end
64+
end

lib/octocatalog-diff/cli/options/compare_file_text.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,30 @@
44
# the 'source' attribute and populate the 'content' attribute with the text of the file.
55
# This allows for a diff of the content, rather than a diff of the location, which is
66
# what is most often desired.
7+
#
8+
# This has historically been a binary option, so --compare-file-text with no argument will
9+
# set this to `true` and --no-compare-file-text will set this to `false`. Note that
10+
# --no-compare-file-text does not accept an argument.
11+
#
12+
# File text comparison will be auto-disabled in circumstances other than compiling and
13+
# comparing two catalogs. To force file text comparison to be enabled at other times,
14+
# set --compare-file-text=force. This allows the content of the file to be substituted
15+
# in to --catalog-only compilations, for example.
16+
#
717
# @param parser [OptionParser object] The OptionParser argument
818
# @param options [Hash] Options hash being constructed; this is modified in this method.
919
OctocatalogDiff::Cli::Options::Option.newoption(:compare_file_text) do
1020
has_weight 210
1121

1222
def parse(parser, options)
13-
parser.on('--[no-]compare-file-text', 'Compare text, not source location, of file resources') do |x|
14-
options[:compare_file_text] = x
23+
parser.on('--[no-]compare-file-text[=force]', 'Compare text, not source location, of file resources') do |x|
24+
if x == 'force'
25+
options[:compare_file_text] = :force
26+
elsif x == true || x == false
27+
options[:compare_file_text] = x
28+
else
29+
raise OptionParser::NeedlessArgument("needless argument: --compare-file-text=#{x}")
30+
end
1531
end
1632
end
1733
end

lib/octocatalog-diff/util/catalogs.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ def build_catalog_parallelizer
7171
if @options.fetch(:compare_file_text, false)
7272
result.each do |_key, builder_obj|
7373
next if builder_obj.convert_file_resources(true)
74+
75+
if @options[:compare_file_text] == :force
76+
@logger.debug "--compare-file-text is force-enabled even though it is not supported by #{builder_obj.builder}"
77+
next
78+
end
79+
7480
@logger.debug "Disabling --compare-file-text; not supported by #{builder_obj.builder}"
7581
@options[:compare_file_text] = false
7682
catalog_tasks.map! do |x|
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"document_type": "Catalog",
3+
"data": {
4+
"tags": [
5+
"settings"
6+
],
7+
"name": "my.rspec.node",
8+
"version": "production",
9+
"environment": "production",
10+
"resources": [
11+
{
12+
"type": "Exec",
13+
"title": "run-my-command 1",
14+
"file": "/environments/production/modules/foo/manifests/init.pp",
15+
"line": 10,
16+
"exported": false,
17+
"parameters": {
18+
"path": "/usr/bin",
19+
"command": "id",
20+
"returns": "0"
21+
}
22+
},
23+
{
24+
"type": "Exec",
25+
"title": "run-my-command 2",
26+
"file": "/environments/production/modules/foo/manifests/init.pp",
27+
"line": 10,
28+
"exported": false,
29+
"parameters": {
30+
"path": "/usr/bin",
31+
"command": "id",
32+
"returns": ["0", "1"]
33+
}
34+
},
35+
{
36+
"type": "Exec",
37+
"title": "run-my-command 3",
38+
"file": "/environments/production/modules/foo/manifests/init.pp",
39+
"line": 10,
40+
"exported": false,
41+
"parameters": {
42+
"path": "/usr/bin",
43+
"command": "id",
44+
"returns": ["0", "1", "2"]
45+
}
46+
}
47+
],
48+
"classes": [
49+
"settings"
50+
]
51+
},
52+
"metadata": {
53+
"api_version": 1
54+
}
55+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"document_type": "Catalog",
3+
"data": {
4+
"tags": [
5+
"settings"
6+
],
7+
"name": "my.rspec.node",
8+
"version": "production",
9+
"environment": "production",
10+
"resources": [
11+
{
12+
"type": "Exec",
13+
"title": "run-my-command 1",
14+
"file": "/environments/production/modules/foo/manifests/init.pp",
15+
"line": 10,
16+
"exported": false,
17+
"parameters": {
18+
"path": "/usr/bin",
19+
"command": "id",
20+
"returns": 0
21+
}
22+
},
23+
{
24+
"type": "Exec",
25+
"title": "run-my-command 2",
26+
"file": "/environments/production/modules/foo/manifests/init.pp",
27+
"line": 10,
28+
"exported": false,
29+
"parameters": {
30+
"path": "/usr/bin",
31+
"command": "id",
32+
"returns": [0, 1]
33+
}
34+
},
35+
{
36+
"type": "Exec",
37+
"title": "run-my-command 3",
38+
"file": "/environments/production/modules/foo/manifests/init.pp",
39+
"line": 10,
40+
"exported": false,
41+
"parameters": {
42+
"path": "/usr/bin",
43+
"command": "id",
44+
"returns": [0, 1, 2, 3]
45+
}
46+
}
47+
],
48+
"classes": [
49+
"settings"
50+
]
51+
},
52+
"metadata": {
53+
"api_version": 1
54+
}
55+
}

spec/octocatalog-diff/integration/convert_file_resources_spec.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,71 @@
147147
end
148148
end
149149

150+
context 'with option auto-disabled' do
151+
before(:all) do
152+
@result = OctocatalogDiff::Integration.integration_cli(
153+
[
154+
'--fact-file', OctocatalogDiff::Spec.fixture_path('facts/facts.yaml'),
155+
'--catalog-only',
156+
'-n', 'rspec-node.github.net',
157+
'--compare-file-text',
158+
'--basedir', OctocatalogDiff::Spec.fixture_path('repos/convert-resources/new'),
159+
'--puppet-binary', OctocatalogDiff::Spec::PUPPET_BINARY,
160+
'--debug'
161+
]
162+
)
163+
end
164+
165+
it 'should compile the catalog' do
166+
expect(@result[:exitcode]).not_to eq(-1), OctocatalogDiff::Integration.format_exception(@result)
167+
expect(@result[:exitcode]).to eq(0), "Runtime error: #{@result[:logs]}"
168+
end
169+
170+
it 'should indicate that the option was disabled' do
171+
expect(@result[:stderr]).to match(/Disabling --compare-file-text; not supported by OctocatalogDiff::Catalog::Noop/)
172+
end
173+
174+
it 'should not have converted resources in the catalog' do
175+
catalog = OctocatalogDiff::Catalog::JSON.new(json: @result[:stdout])
176+
resource = catalog.resource(type: 'File', title: '/tmp/foo2')
177+
expect(resource).to be_a_kind_of(Hash)
178+
expect(resource['parameters']).to eq('source' => 'puppet:///modules/test/foo-old')
179+
end
180+
end
181+
182+
context 'with option force-enabled' do
183+
before(:all) do
184+
@result = OctocatalogDiff::Integration.integration_cli(
185+
[
186+
'--fact-file', OctocatalogDiff::Spec.fixture_path('facts/facts.yaml'),
187+
'--catalog-only',
188+
'-n', 'rspec-node.github.net',
189+
'--compare-file-text=force',
190+
'--basedir', OctocatalogDiff::Spec.fixture_path('repos/convert-resources/new'),
191+
'--puppet-binary', OctocatalogDiff::Spec::PUPPET_BINARY,
192+
'--debug'
193+
]
194+
)
195+
end
196+
197+
it 'should compile the catalog' do
198+
expect(@result[:exitcode]).not_to eq(-1), OctocatalogDiff::Integration.format_exception(@result)
199+
expect(@result[:exitcode]).to eq(0), "Runtime error: #{@result[:logs]}"
200+
end
201+
202+
it 'should indicate that the option was force-enabled' do
203+
rexp = /--compare-file-text is force-enabled even though it is not supported by OctocatalogDiff::Catalog::Noop/
204+
expect(@result[:stderr]).to match(rexp)
205+
end
206+
207+
it 'should have converted resources in the catalog' do
208+
catalog = OctocatalogDiff::Catalog::JSON.new(json: @result[:stdout])
209+
resource = catalog.resource(type: 'File', title: '/tmp/foo2')
210+
expect(resource).to be_a_kind_of(Hash)
211+
expect(resource['parameters']).to eq('content' => "content of foo-old\n")
212+
end
213+
end
214+
150215
context 'with broken reference in to-catalog' do
151216
it 'should fail' do
152217
result = OctocatalogDiff::Integration.integration(

0 commit comments

Comments
 (0)