英文:
Use rego to compare before and after values from list of inputs
问题
当我运行以下代码时,我可以比较参数instance_class的值并计算差异的数量:
modifies_instance_class[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [res | res:= all[_]; res.change.after.instance_class != res.change.before.instance_class]
num := count(modifies)
}
然而,我想要能够使用相同的代码块来比较包含在我的参数列表my_params中的不同参数值。我尝试了以下方法,但这并不起作用:
my_params = {"instance_class", "engine_version", "identifier"}
modifies_instance_class[resource_type] = num {
some resource_type
some 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)
}
英文:
When I run the following, I can compare the values for parameter instance_class and count the number of discrepancies:
modifies_instance_class[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [res | res:= all[_]; res.change.after.instance_class != res.change.before.instance_class]
num := count(modifies)
}
However, I'd like to be able to use the same block of code to compare different parameters values contained in my list my_params. I've tried the following but this doesn't work.
my_params = {"instance_class", "engine_version", "identifier"}
modifies_instance_class[resource_type] = num {
some resource_type
some 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)
}
答案1
得分: 3
以下是翻译好的部分:
-
第一个问题:使用
parameter
变量查找字段表达式
res.change.after.parameter
查找名为"parameter"
的字段,该字段在res.change.after
引用的对象中。换句话说,语法foo.bar
实际上等同于foo["bar"]
。表达式foo["bar"]
选择了值foo
中的字段"bar"
(字符串 'bar')。要修复这个问题,将res.change.after.parameter
更改为res.change.after[parameter]
。现在parameter
引用了变量。 -
第二个问题:使
modifies_instance_class
为每个键生成一个值第二个问题是规则将为
modifies_instance_class
文档生成冲突的值。形如p[x] = y { ... }
的规则将生成一个名为p
的 JSON 文档,其中x
和y
分别是键和值。OPA 必须将相同键的冲突值视为运行时错误。为了解决这个问题,我们可以将
my_params[parameter]
表达式简单地移动到数组推导式的主体中。 -
第三个问题:如果参数字段在之前或之后丢失怎么办?
还有一个问题要处理。在上面的示例中,我们假设查找
parameter
变量引用的字段将始终返回一个值。如果情况不是这样怎么办?如果之前或之后的对象中缺少该字段怎么办?在这种情况下,引用
res.change.after[parameter]
或res.change.before[parameter]
将是 未定义 的。如果任一值未定义,则表达式res.change.after[parameter] != res.change.after[parameter]
也将是未定义的(https://www.openpolicyagent.org/docs/latest/#expressions-logical-and)。如果表达式未定义,OPA 无法断定其是否为真,因此结果不包括在结果中(或在本例中,不包括在推导式计算的数组中)。根据数据的性质,这可能(或可能不)重要。为了处理这个问题,我们可以扩展检查以处理字段在一侧(或两侧)未定义的情况。
请注意:在这种情况下,我们必须使用辅助函数,因为我们要表示逻辑 OR:https://www.openpolicyagent.org/docs/latest/#logical-or。
英文:
As Patrick East mentioned, there are a couple issues. I thought I'd break down each one and provide a bit more detail. Here's the final version that I came up with: https://play.openpolicyagent.org/p/rl1p43N5HR
1st problem: lookup the field using the parameter
var
The expression res.change.after.parameter
looks up the field named "parameter"
in the the object referenced by res.change.after
. In other words, the syntax foo.bar
is sugar for foo["bar"]
. The expression foo["bar"]
selects the field "bar"
(string 'bar') from the value foo
. To fix this, change res.change.after.parameter
to res.change.after[parameter]
. Now parameter
refers to the variable.
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)
}
2nd problem: make modifies_instance_class
generate one value per key
The second problem is that rule will generate conflicting values for the modifies_instance_class
document. Rules of the form p[x] = y { ... }
generate a JSON document named p
where x
and y
are the keys and values (respectively). OPA has to treat conflicting values for the same key as runtime errors.
For example, imagine OPA contains the following data:
resource_types = {"servers"}
resources = {
"servers": [{
"after": {"instance_class": "ic1", "identifier": "id1"},
"before": {"instance_class": "ic2", "identifier": "id1"}
}]
}
The data indicates there is one resource_type
("servers"
) and one server. The data for the server indicates the instance_class
field changed.
When OPA evaluates the rule, it will search for variable assignments that satisfy all of the expressions in the rule body:
# 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)
In this case,
OPA finds values for resource_type
, parameter
, all
, modifies
, and num
. Ignore the all
, modifies
, and _
variables for now.
In this case, OPA will find two sets of variable assignments:
{resource_type: "servers", parameter: "instance_class", num: 1}
{resource_type: "servers", parameter: "identifier", num: 0}
The problem is that the rule generates a mapping from resource_type
to num
. In this case, that produces {"servers": 1}
and {"servers": 0}
. This is the conflict. Which document is correct?
To resolve this we can simply move the my_params[parameter]
expression into the body of the array comprehension.
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)
}
With this change, OPA will only find one set of variable assignments:
{resource_type: "servers", num: 1}
> Note, if OPA were to find other values for resource_type
that would be fine because those would be different keys in the document produced by modifies_instance_class
.
3rd problem: what if the parameter field is missing before or after?
There is one more problem to deal with. In the examples above, we assumed that looking up the field referred to by the parameter
var would always return a value. What if this is not the case? What if one of the before or after objects are missing the field?
In this case, the reference res.change.after[parameter]
or res.change.before[parameter]
would be undefined. If either value is undefined, the expression res.change.after[parameter] != res.change.after[parameter]
is also undefined (https://www.openpolicyagent.org/docs/latest/#expressions-logical-and). If the expression is undefined, OPA cannot assert that it's true, so the result is not included in the result (or in this case, the array computed by the comprehension.)
Depending on the nature of the data, this may (or may not) matter. To deal with this we can extend the check to deal with the case where the field is undefined on one side (or both).
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]
}
> Note: In this case we have to use a helper function because want to express logical OR: https://www.openpolicyagent.org/docs/latest/#logical-or.
答案2
得分: 0
First issue is using res.change.after.parameter
in this way will cause problems. That is using a key named "parameter" and not the variable. You'll have to do something like res.change.after[parameter]
. That should avoid the first error.. But exposes the next (and bigger) issue:
第一个问题是以这种方式使用res.change.after.parameter
会引发问题。这是使用名为 "parameter" 而不是变量的键。你需要做类似 res.change.after[parameter]
这样的操作。这应该可以避免第一个错误,但会显现出下一个(更大的)问题:
eval_conflict_error: object keys must be unique
问题出在你组合它们的方式上,你有 my_params[parameter]
并且在计算 modifies
时考虑了 modifies
的数量。这将为每个 resource_type
(即,某些资源类型 x
可能对 instance_class
的 num
为 0
,对 identifier
的 num
为 2
)给出一个与 parameter
不同的 num
,这与你将结果构建为 resource_type
到 num
的映射方式有些棘手(因为每个资源类型可能有超过一个 num
)。
如果你只关心计数,你可以执行另一个推导,检查每个资源的每个参数(也许不是最佳选项,但可以工作)。一个示例可以在这里找到:https://play.openpolicyagent.org/p/5T5TntBygd
请注意,这个示例不太适合提供关于 什么 发生了变化的任何反馈,因此作为辅助工具可能有用,但效果可能因情况而异。
英文:
Couple of things:
First issue is using res.change.after.parameter
in this way will cause problems. That is using a key named "parameter" and not the variable. You'll have to do something like res.change.after[parameter]
. That should avoid the first error.. But exposes the next (and bigger) issue:
eval_conflict_error: object keys must be unique
The issue being the way you are combining them where you have my_params[parameter]
and counting the modifies
. It is going to give you a num
distinct to the parameter
for each resource_type
(ie, some resource type x
may have a num of 0
for instance_class
and a 2
for identifier
), which is tricky with the way you are structuring the results as a map of resource_type
to num
(since there are potentially >1 num for each)
If you only care about the count you could do another comprehension that checks each parameter for each resource (maybe not the best option.. but works). An example of that working is here: https://play.openpolicyagent.org/p/5T5TntBygd
modifies_instance_class[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [res |
res := all[_] # For each resource
# Check if one of the params we care about changed
changed_params := ; res.change.after
!= res.change.before
]
count(changed_params) > 0
]
num := count(modifies)
}
Note that this example isn't great for providing any kind of feedback about what changed, so as a helper it might be useful but mileage on it may vary.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论