Skip to content

Commit b2eb4ae

Browse files
committed
Add set quality comparison to ignore filter
Add new syntax for supporting set equality checks for parameters with arrays of values where order is not important.
1 parent 8d4b413 commit b2eb4ae

File tree

5 files changed

+139
-1
lines changed

5 files changed

+139
-1
lines changed

doc/advanced-ignores.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,13 @@ File[/tmp/foo] =>
222222
In this case, the very important line was removed from the catalog, and you want to know about this. Ignoring `File[/tmp/foo]::parameters::content` would have suppressed this (because all changes to that attribute are ignored). Also ignoring `File[/tmp/foo]::parameters::content=~>This is the line in the new catalog that I do not care about$` would have also suppressed this (because the regular expression was matched for *one* of the lines). However, the two examples with `=&>` in this section would *not* have suppressed this change, because it is no longer the case that *all* changes in the file matched the regular expression.
223223

224224
:warning: All lines are stripped of leading and trailing spaces before the regular expression match is tried. This stripping of whitespace is done *only* for this comparison stage, and does not affect the display of any results.
225+
226+
#### Ignoring attributes which have identical elements but in arbitrary order
227+
228+
You can ignore attributes where both the values in both the old and new catalogs are arrays and the arrays
229+
contain identical elements but in arbitrary order. Basically, you can ignore a parameter where the values
230+
have set equality.
231+
232+
To ignore any parameters named `foo` with values having set equality, you would use:
233+
234+
--ignore 'My::Custom::Resource[*]::parameters::foo=s>='

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,8 @@ def attr_match_rule?(rule, attrib, old_val, new_val)
340340
# =-> Attribute must have been removed and equal this
341341
# =~> Change must match regexp (one line of change matching is sufficient)
342342
# =&> Change must match regexp (all lines of change MUST match regexp)
343-
if rule_attr =~ /\A(.+?)(=[\-\+~&]?>)(.+)/m
343+
# =s> Change must be array and contain identical elements, ignoring order
344+
if rule_attr =~ /\A(.+?)(=[\-\+~&s]?>)(.+)/m
344345
rule_attr = Regexp.last_match(1)
345346
operator = Regexp.last_match(2)
346347
value = Regexp.last_match(3)
@@ -361,6 +362,9 @@ def attr_match_rule?(rule, attrib, old_val, new_val)
361362
raise RegexpError, "Invalid ignore regexp for #{key}: #{exc.message}"
362363
end
363364
matcher = ->(x, y) { regexp_operator_match?(operator, my_regex, x, y) }
365+
elsif operator == '=s>'
366+
raise ArgumentError, "Invalid ignore option for =s>, must be '='" unless value == '='
367+
matcher = ->(x, y) { x.is_a?(Array) && y.is_a?(Array) && Set.new(x) == Set.new(y) }
364368
end
365369
end
366370

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"document_type": "Catalog",
3+
"data": {
4+
"tags": ["settings"],
5+
"name": "my.rspec.node",
6+
"version": "production",
7+
"environment": "production",
8+
"resources": [
9+
{
10+
"type": "Myres",
11+
"title": "res1",
12+
"file": "/environments/production/modules/foo/manifests/init.pp",
13+
"line": 10,
14+
"exported": false,
15+
"parameters": {
16+
"set1": ["one", "two", "three"],
17+
"set2": ["a", "b"]
18+
}
19+
}
20+
],
21+
"classes": [
22+
"settings"
23+
]
24+
},
25+
"metadata": {
26+
"api_version": 1
27+
}
28+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"document_type": "Catalog",
3+
"data": {
4+
"tags": ["settings"],
5+
"name": "my.rspec.node",
6+
"version": "production",
7+
"environment": "production",
8+
"resources": [
9+
{
10+
"type": "Myres",
11+
"title": "res1",
12+
"file": "/environments/production/modules/foo/manifests/init.pp",
13+
"line": 10,
14+
"exported": false,
15+
"parameters": {
16+
"set1": ["three", "two", "one"],
17+
"set2": ["a", "b", "c"],
18+
"set3": [1, 2, 3]
19+
}
20+
}
21+
],
22+
"classes": [
23+
"settings"
24+
]
25+
},
26+
"metadata": {
27+
"api_version": 1
28+
}
29+
}

spec/octocatalog-diff/tests/catalog-diff/differ_spec.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,73 @@
12341234
end
12351235
end
12361236

1237+
context 'ignoring changes in sets' do
1238+
describe '#ignore' do
1239+
before(:all) do
1240+
@c1 = OctocatalogDiff::Catalog.create(json: OctocatalogDiff::Spec.fixture_read('catalogs/ignore-parameter-set-1.json'))
1241+
@c2 = OctocatalogDiff::Catalog.create(json: OctocatalogDiff::Spec.fixture_read('catalogs/ignore-parameter-set-2.json'))
1242+
@set1 = [
1243+
'!',
1244+
"Myres\fres1\fparameters\fset1",
1245+
%w(one two three),
1246+
%w(three two one)
1247+
]
1248+
@set2 = [
1249+
'!',
1250+
"Myres\fres1\fparameters\fset2",
1251+
%w(a b),
1252+
%w(a b c)
1253+
]
1254+
@set3 = [
1255+
'!',
1256+
"Myres\fres1\fparameters\fset3",
1257+
nil,
1258+
[1, 2, 3]
1259+
]
1260+
end
1261+
1262+
it 'should not filter out a change when attribute does not match' do
1263+
opts = {}
1264+
testobj = OctocatalogDiff::CatalogDiff::Differ.new(opts, @c1, @c2)
1265+
testobj.ignore(type: 'Myres', title: 'res1', attr: "parameters\fmode")
1266+
result = testobj.diff
1267+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set1)).to eq(true)
1268+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set2)).to eq(true)
1269+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set3)).to eq(true)
1270+
end
1271+
1272+
it 'should filter out a change when two arrays have set equality' do
1273+
opts = {}
1274+
testobj = OctocatalogDiff::CatalogDiff::Differ.new(opts, @c1, @c2)
1275+
testobj.ignore(type: 'Myres', title: 'res1', attr: "parameters\fset1=s>=")
1276+
result = testobj.diff
1277+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set1)).to eq(false)
1278+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set2)).to eq(true)
1279+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set3)).to eq(true)
1280+
end
1281+
1282+
it 'should not filter out a change when two arrays are not equivalent sets' do
1283+
opts = {}
1284+
testobj = OctocatalogDiff::CatalogDiff::Differ.new(opts, @c1, @c2)
1285+
testobj.ignore(type: 'Myres', title: 'res1', attr: "parameters\fset2=s>=")
1286+
result = testobj.diff
1287+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set1)).to eq(true)
1288+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set2)).to eq(true)
1289+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set3)).to eq(true)
1290+
end
1291+
1292+
it 'should not filter out a change when one array is not specified' do
1293+
opts = {}
1294+
testobj = OctocatalogDiff::CatalogDiff::Differ.new(opts, @c1, @c2)
1295+
testobj.ignore(type: 'Myres', title: 'res1', attr: "parameters\fset3=s>=")
1296+
result = testobj.diff
1297+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set1)).to eq(true)
1298+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set2)).to eq(true)
1299+
expect(OctocatalogDiff::Spec.array_contains_partial_array?(result, @set3)).to eq(true)
1300+
end
1301+
end
1302+
end
1303+
12371304
describe '#ignore_match?' do
12381305
let(:resource) { { type: 'Apple', title: 'delicious', attr: "parameters\fcolor" } }
12391306
let(:testobj) { described_class.allocate }

0 commit comments

Comments
 (0)