Используйте rego для сравнения значений до и после из списка входов
Когда я запускаю следующее, я могу сравнить значения параметра 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)
}
2 ответа
Как сказал Патрик Ист, есть пара проблем. Я подумал, что разобью каждую из них и предоставлю немного больше деталей. Вот последняя версия, которую я придумал: https://play.openpolicyagent.org/p/rl1p43N5HR
1-я проблема: найдите поле с помощью parameter
вар
Выражение res.change.after.parameter
ищет поле с именем "parameter"
в объекте, на который ссылается res.change.after
. Другими словами, синтаксисfoo.bar
сахар для foo["bar"]
. Выражение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)
}
2-я проблема: сделать modifies_instance_class
генерировать одно значение для каждого ключа
Вторая проблема заключается в том, что правило будет генерировать конфликтующие значения для modifies_instance_class
документ. Правила формыp[x] = y { ... }
создать документ JSON с именем p
где x
а также 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
.
3-я проблема: что делать, если поле параметра отсутствует до или после?
Есть еще одна проблема. В приведенных выше примерах мы предполагали, что поиск поля, на которое указываетparameter
var всегда будет возвращать значение. Что делать, если это не так? Что делать, если у одного из объектов "до" или "после" отсутствует поле?
В этом случае ссылка res.change.after[parameter]
или res.change.before[parameter]
будет неопределенным. Если какое-либо значение не определено, выражениеres.change.after[parameter] != res.change.after[parameter]
также не определено ( https://www.openpolicyagent.org/docs/latest/). Если выражение не определено,
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]
}
Примечание: в этом случае мы должны использовать вспомогательную функцию, потому что хотим выразить логическое ИЛИ: https://www.openpolicyagent.org/docs/latest/.
Пара вещей:
Первый выпуск использует res.change.after.parameter
таким образом вызовет проблемы. Это использование ключа с именем "параметр", а не переменной. Вам нужно будет сделать что-то вродеres.change.after[parameter]
. Это должно избежать первой ошибки.. Но выявляет следующую (и более серьезную) проблему:
eval_conflict_error: object keys must be unique
Проблема в том, как вы их объединяете, где у вас есть my_params[parameter]
и считая modifies
. Это даст вамnum
отличные от parameter
для каждого resource_type
(т.е. некоторый тип ресурса x
может иметь количество 0
за instance_class
и 2
за identifier
), что сложно из-за того, как вы структурируете результаты как карту resource_type
к num
(поскольку для каждого потенциально может быть>1 числа)
Если вас интересует только счетчик, вы можете сделать еще одно понимание, которое проверяет каждый параметр для каждого ресурса (возможно, не лучший вариант... но работает). Пример этой работы находится здесь: 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)
}
Обратите внимание, что этот пример не очень хорош для предоставления какой-либо обратной связи о том, что изменилось, поэтому в качестве помощника он может быть полезен, но его оценка может варьироваться.