|
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