正如 Patrick East 所提到的,有几个问题。我想我会分解每一个并提供更多细节。这是我想出的最终版本:https ://play.openpolicyagent.org/p/rl1p43N5HR
parameter
第一个问题:使用var查找字段
该表达式res.change.after.parameter
查找"parameter"
在引用的对象中命名的字段res.change.after
。换句话说,语法foo.bar
是foo["bar"]
. 该表达式从 value 中foo["bar"]
选择字段"bar"
(字符串 'bar')foo
。要解决此问题,请更改res.change.after.parameter
为res.change.after[parameter]
. 现在parameter
指的是变量。
modifies_instance_class_V1[resource_type] = num {
some resource_type, parameter
resource_types[resource_type]
my_params[parameter]
all := resources[resource_type]
modifies := [res | res:= all[_]; res.change.after[parameter] != res.change.before[parameter]]
num := count(modifies)
}
第二个问题:modifies_instance_class
每个键生成一个值
第二个问题是规则会为modifies_instance_class
文档生成冲突的值。表单规则p[x] = y { ... }
生成一个 JSON 文档,名为p
wherex
和y
是键和值(分别)。OPA 必须将同一键的冲突值视为运行时错误。
例如,假设 OPA 包含以下数据:
resource_types = {"servers"}
resources = {
"servers": [{
"after": {"instance_class": "ic1", "identifier": "id1"},
"before": {"instance_class": "ic2", "identifier": "id1"}
}]
}
数据表明有一台resource_type
( "servers"
) 和一台服务器。服务器的数据表明该instance_class
字段已更改。
当 OPA 评估规则时,它将搜索满足规则正文中所有表达式的变量分配:
# This is the rule body from above.
some resource_type, parameter
resource_types[resource_type]
my_params[parameter]
all := resources[resource_type]
modifies := [res | res:= all[_]; res.change.after[parameter] != res.change.before[parameter]]
num := count(modifies)
在这种情况下,OPA 会找到resource_type
、parameter
、all
、modifies
和的值num
。暂时忽略all
、modifies
和_
变量。
在这种情况下,OPA 会找到两组变量赋值:
{resource_type: "servers", parameter: "instance_class", num: 1}
{resource_type: "servers", parameter: "identifier", num: 0}
问题是该规则生成从resource_type
到的映射num
。在这种情况下,这会产生{"servers": 1}
和{"servers": 0}
。这就是冲突。哪个文件是正确的?
为了解决这个问题,我们可以简单地将my_params[parameter]
表达式移动到数组理解的主体中。
modifies_instance_class_V2[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [[res, parameter] | # NOTE: this generates an array of resource/parameter tuples now.
some parameter
my_params[parameter]
res := all[_]
res.change.after[parameter] != res.change.before[parameter]
]
num := count(modifies)
}
通过此更改,OPA 将仅找到一组变量分配:
{resource_type: "servers", num: 1}
请注意,如果 OPA 要为此找到其他值resource_type
会很好,因为这些将是由modifies_instance_class
.
第三个问题:如果之前或之后缺少参数字段怎么办?
还有一个问题需要处理。在上面的示例中,我们假设查找parameter
var 引用的字段将始终返回一个值。如果不是这种情况怎么办?如果之前或之后的对象之一缺少该字段怎么办?
在这种情况下,引用res.change.after[parameter]
orres.change.before[parameter]
将是undefined。如果任一值未定义,则表达式res.change.after[parameter] != res.change.after[parameter]
也未定义(https://www.openpolicyagent.org/docs/latest/#expressions-logical-and)。如果表达式未定义,OPA 无法断言它为真,因此结果不包含在结果中(或者在这种情况下,是由推导计算的数组。)
根据数据的性质,这可能(或可能不)重要。为了解决这个问题,我们可以扩展检查以处理一侧(或两侧)未定义字段的情况。
modifies_instance_class_V3[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [[res, parameter] |
some parameter
my_params[parameter]
res := all[_]
not same_or_both_undefined(res.change.after, res.change.before, parameter)
]
num := count(modifies)
}
same_or_both_undefined(a, b, k) = true {
a[k] == b[k]
}
same_or_both_undefined(a, b, k) = true {
not a[k]
not b[k]
}
注意:在这种情况下,我们必须使用辅助函数,因为要表达逻辑 OR:https ://www.openpolicyagent.org/docs/latest/#logical-or 。