使用rego比较输入列表中的前后值

huangapple go评论66阅读模式
英文:

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

以下是翻译好的部分:

  1. 第一个问题:使用 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 引用了变量。

  2. 第二个问题:使 modifies_instance_class 为每个键生成一个值

    第二个问题是规则将为 modifies_instance_class 文档生成冲突的值。形如 p[x] = y { ... } 的规则将生成一个名为 p 的 JSON 文档,其中 xy 分别是键和值。OPA 必须将相同键的冲突值视为运行时错误。

    为了解决这个问题,我们可以将 my_params[parameter] 表达式简单地移动到数组推导式的主体中。

  3. 第三个问题:如果参数字段在之前或之后丢失怎么办?

    还有一个问题要处理。在上面的示例中,我们假设查找 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_classnum0,对 identifiernum2)给出一个与 parameter 不同的 num,这与你将结果构建为 resource_typenum 的映射方式有些棘手(因为每个资源类型可能有超过一个 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.

huangapple
  • 本文由 发表于 2020年1月3日 21:20:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/59579311.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定