1

当我运行以下命令时,我可以比较参数 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)
}
4

2 回答 2

3

正如 Patrick East 所提到的,有几个问题。我想我会分解每一个并提供更多细节。这是我想出的最终版本:https ://play.openpolicyagent.org/p/rl1p43N5HR

parameter第一个问题:使用var查找字段

该表达式res.change.after.parameter查找"parameter"在引用的对象中命名的字段res.change.after。换句话说,语法foo.barfoo["bar"]. 该表达式从 value 中foo["bar"]选择字段"bar"(字符串 'bar')foo。要解决此问题,请更改res.change.after.parameterres.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 文档,名为pwherexy是键和值(分别)。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_typeparameterallmodifies和的值num。暂时忽略allmodifies_变量。

在这种情况下,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.

第三个问题:如果之前或之后缺少参数字段怎么办?

还有一个问题需要处理。在上面的示例中,我们假设查找parametervar 引用的字段将始终返回一个值。如果不是这种情况怎么办?如果之前或之后的对象之一缺少该字段怎么办?

在这种情况下,引用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 。

于 2020-01-14T21:43:26.357 回答
0

几件事:

第一个问题是以res.change.after.parameter这种方式使用会导致问题。那是使用一个名为“参数”的键而不是变量。你必须做类似的事情res.change.after[parameter]。那应该避免第一个错误。但是暴露了下一个(和更大的)问题:

eval_conflict_error: object keys must be unique

问题在于您将它们组合在您拥有的地方my_params[parameter]并计算modifies. 它将为您提供numparameterfor each不同的内容resource_type(即,某些资源类型x可能有一个0forinstance_class和一个2for ),这对于您将结果构建为toidentifier的映射的方式很棘手(因为可能存在> 1个每个)resource_typenum

如果你只关心计数,你可以做另一个理解来检查每个资源的每个参数(也许不是最好的选择..但有效)。该工作的一个示例在这里: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 := [p | p := my_params[_]; res.change.after[p] != res.change.before[p]]  
        count(changed_params) > 0
    ]
    num := count(modifies)
}

请注意,此示例不适用于提供有关更改内容的任何类型的反馈因此作为助手,它可能很有用,但它的里程可能会有所不同。

于 2020-01-10T21:08:44.577 回答