|
37 | 37 | desc 'Match conditions for this group'
|
38 | 38 | def should
|
39 | 39 | case @resource[:purge_behavior]
|
40 |
| - when :rule, :all |
41 |
| - super |
42 |
| - else |
| 40 | + # only do something special when we are asked to merge, otherwise, fall back to the default |
| 41 | + when :none |
| 42 | + # rule grammar |
| 43 | + # condition : [ {bool} {condition}+ ] | [ "not" {condition} ] | {operation} |
| 44 | + # bool : "and" | "or" |
| 45 | + # operation : [ {operator} {fact-path} {value} ] |
| 46 | + # operator : "=" | "~" | ">" | ">=" | "<" | "<=" | "<:" |
| 47 | + # fact-path : {field-name} | [ {path-type} {field-name} {path-component}+ ] |
| 48 | + # path-type : "trusted" | "fact" |
| 49 | + # path-component : field-name | number |
| 50 | + # field-name : string |
| 51 | + # value : string |
| 52 | + |
43 | 53 | a = @resource.property(:rule).retrieve || {}
|
44 | 54 | b = shouldorig
|
45 |
| - aorig = a.map(&:clone) |
46 |
| - atmp = a.map(&:clone) |
47 |
| - # check if the node classifer has any rules defined before attempting merge. |
48 |
| - if a.length >= 2 |
49 |
| - if (a[0] == 'or') && (a[1][0] == 'or') || (a[1][0] == 'and') |
50 |
| - # Merging both rules and pinned nodes |
51 |
| - if (b[0] == 'or') && (b[1][0] == 'or') || (b[1][0] == 'and') |
52 |
| - # b has rules to merge |
53 |
| - rules = (atmp[1] + b[1].drop(1)).uniq |
54 |
| - atmp[1] = rules |
55 |
| - pinned = (b[2, b.length] + atmp[2, atmp.length]).uniq |
56 |
| - merged = (atmp + pinned).uniq |
57 |
| - elsif ((b[0] == 'and') || (b[0] == 'or')) && PuppetX::Node_manager::Common.factcheck(b) |
58 |
| - # b only has rules to merge |
59 |
| - rules = (atmp[1] + b.drop(1)).uniq |
60 |
| - atmp[1] = rules |
61 |
| - merged = atmp |
62 |
| - else |
63 |
| - pinned = (b[1, b.length] + atmp[2, atmp.length]).uniq |
64 |
| - merged = (atmp + pinned).uniq |
65 |
| - end |
66 |
| - elsif (b[0] == 'or') && (b[1][0] == 'or') || (b[1][0] == 'and') |
67 |
| - # Merging both rules and pinned nodes |
68 |
| - rules = b[1] # no rules to merge on a side |
69 |
| - pinned = (b[2, b.length] + a[1, a.length]).uniq |
70 |
| - merged = (b + pinned).uniq |
71 |
| - elsif ((a[0] == 'and') || (a[0] == 'or')) && PuppetX::Node_manager::Common.factcheck(a) |
72 |
| - # a only has fact rules |
73 |
| - if (b[0] == 'or') && !PuppetX::Node_manager::Common.factcheck(b) |
74 |
| - # b only has pinned nodes |
75 |
| - rules = atmp |
76 |
| - temp = ['or'] |
77 |
| - temp[1] = atmp |
78 |
| - merged = (temp + b[1, b.length]).uniq |
79 |
| - else |
80 |
| - # b only has rules |
81 |
| - merged = (a + b.drop(1)).uniq |
82 |
| - end |
83 |
| - elsif (a[0] == 'or') && (a[1][1] == 'name') |
84 |
| - # a only has pinned nodes |
85 |
| - if (b[0] == 'or') && !PuppetX::Node_manager::Common.factcheck(b) |
86 |
| - # b only has pinned nodes |
87 |
| - merged = (b + a.drop(1)).uniq |
88 |
| - else |
89 |
| - # b only has rules |
90 |
| - temp = ['or'] |
91 |
| - temp[1] = b |
92 |
| - merged = (temp + atmp[1, atmp.length]).uniq |
| 55 | + |
| 56 | + # extract all pinned nodes if any |
| 57 | + # pinned nodes are in the form ['=', 'name', <hostname>] |
| 58 | + apinned = [] |
| 59 | + a_without_pinned = a |
| 60 | + if (a[0] == 'or') |
| 61 | + apinned = a.select {|item| (item[0] == '=') && (item[1] == 'name')} |
| 62 | + a_without_pinned = a.select { |item| (item[0] != '=') || (item[1] != 'name') } |
| 63 | + end |
| 64 | + bpinned = [] |
| 65 | + b_without_pinned = b |
| 66 | + merged = [] |
| 67 | + if (a == [""]) |
| 68 | + # ensure rules are unique at the top level and remove any empty rule sets |
| 69 | + return b.uniq.select { |item| (item != ['or'] && item != ['and'] )} |
| 70 | + elsif (b == [""]) |
| 71 | + # ensure rules are unique at the top level and remove any empty rule sets |
| 72 | + return a.uniq.select { |item| (item != ['or'] && item != ['and'] )} |
| 73 | + end |
| 74 | + |
| 75 | + if (b[0] == 'or') |
| 76 | + bpinned = b.select {|item| (item[0] == '=') && (item[1] == 'name')} |
| 77 | + b_without_pinned = b.select { |item| (item[0] != '=') || (item[1] != 'name') } |
| 78 | + end |
| 79 | + |
| 80 | + if (((a[0] == 'and') || (a[0] == 'or')) && a[0] == b[0]) |
| 81 | + # if a and b start with the same 'and' or 'or' clause, we can just combine them |
| 82 | + if (a[0] == 'or') |
| 83 | + merged = (['or'] + a_without_pinned.drop(1) + b_without_pinned.drop(1) + apinned + bpinned).uniq |
| 84 | + else |
| 85 | + # must both be 'and' clauses |
| 86 | + if (apinned.length > 0 || bpinned.length > 0) |
| 87 | + # we have pinned nodes |
| 88 | + merged = (['or'] + [a_without_pinned + b_without_pinned.drop(1)] + apinned + bpinned).uniq |
| 89 | + else |
| 90 | + # no pinned nodes and one top level 'and' clause, just combine them. |
| 91 | + merged = a_without_pinned + b_without_pinned.drop(1) |
93 | 92 | end
|
94 |
| - else |
95 |
| - # default fall back. |
96 |
| - merged = (b + a.drop(1)).uniq |
97 | 93 | end
|
98 |
| - if merged == aorig |
99 |
| - # values are the same, returning orginal value" |
100 |
| - aorig |
| 94 | + else |
| 95 | + # first clause of a and b aren't equal |
| 96 | + # a first clause is one of and/or/not/operator |
| 97 | + # b first clause is one of and/or/not/operator |
| 98 | + # if a starts with `and` and b starts with `or`, create a top level `or` clause, nest a under it and append the rest of b |
| 99 | + if (a_without_pinned[0] == 'and' && b_without_pinned[0] == 'or') |
| 100 | + # special case of a only having one subclause |
| 101 | + if (a_without_pinned.length == 2) |
| 102 | + merged = (['or'] + a_without_pinned[1] + b_without_pinned.drop(1) + apinned + bpinned) |
| 103 | + else |
| 104 | + merged = (['or'] + [a_without_pinned] + b_without_pinned.drop(1) + apinned + bpinned) |
| 105 | + end |
101 | 106 | else
|
102 |
| - merged |
| 107 | + if (a_without_pinned[0] == 'or') |
| 108 | + merged = (a_without_pinned + [b_without_pinned] + apinned + bpinned).uniq |
| 109 | + else |
| 110 | + # if b starts with 'or', we want to be sure to drop that. |
| 111 | + if (b_without_pinned[0] == 'or') |
| 112 | + merged = (['or'] + [a_without_pinned] + b_without_pinned.drop(1) + apinned + bpinned) |
| 113 | + else |
| 114 | + merged = (['or'] + [a_without_pinned] + [b_without_pinned] + apinned + bpinned) |
| 115 | + end |
| 116 | + end |
103 | 117 | end
|
104 |
| - else |
105 |
| - b |
106 | 118 | end
|
| 119 | + # ensure rules are unique at the top level and remove any empty rule sets |
| 120 | + merged.uniq.select { |item| (item != ['or'] && item != ['and'] )} |
| 121 | + else |
| 122 | + super |
107 | 123 | end
|
108 | 124 | end
|
109 | 125 |
|
|
0 commit comments