Golang Go语言中 OPA 进阶-函数与虚拟文档要分清
Golang Go语言中 OPA 进阶-函数与虚拟文档要分清
本文来讲讲OPA
常用的函数(function
)和虚拟文档(virtual document
),以及他们使用的场景。
我们将以实现判断配置文件为例展开。
用到的输入(配置文件列表)为:
// input.json
// posix 为常见路径格式
// traditional-mac 为 mac 的一种文件路径格式
{
"files": [
{
"type": "posix",
"path": "/Users/newbmiao/Documents/1.yaml"
},
{
"type": "posix",
"path": "/Users/newbmiao/Documents/2.yaml"
},
{
"type": "traditional-mac",
"path": "Macintosh HD:Users:newbmiao:Documents:3.yml"
},
{
"type": "traditional-mac",
"path": "Macintosh HD:Users:newbmiao:Documents:3.json"
}
]
}
函数
函数基本每个语言都会有,要说OPA
里有啥特别的地方,那就是他也实现同名函数,类似“函数重载”,但简化了许多。
他的特点是:
- 默认函数返回值为 true/false
- 可以指定函数返回值
- 可以存在同名函数, 但参数数目不能变
- 相同输入(参数)必须获得相同输出(返回值)
举例来说,如果实现判断文件是否是配置文件后缀:
下边is_config_file
就是不指定返回值
条件满足则返回 true,三个实现只要满足一个就为 true
is_config_file(str) {
contains(str, ".yaml")
}
is_config_file(str) {
contains(str, “.yml”)
}
is_config_file(str) {
contains(str, “.json”)
}
当然也可以使用else
关键字合并到一个函数:
is_config_file2(str) {
contains(str, ".yaml")
}
else {
contains(str, “.yml”)
}
else {
contains(str, “.json”)
}
那如果指定返回值怎么重载呢?
以实现不同路径格式的文件名为例:
getFileName(type, str) = x {
type = "posix"
str = trim(str)
tmp := split(str, "/")
x := tmp[minus(count(tmp), 1)]
}
getFileName(type, str) = x {
type = “traditional-mac”
str = trim(str)
tmp := split(str, “:”)
x := tmp[minus(count(tmp), 1)]
}
你会发现两个函数输入和返回不是相同的么?
其实不是,仔细看他们内都做了 type = {"traditional-mac"|"posix"}
的判断
这实际类似函数声明为 getFileName("posix", str) = x {}
, 所以输入是不同的
如果你省略了type
的判断,会报错的:
eval_conflict_error: functions must not produce multiple outputs for same inputs
到此,将函数组合一下就可以实现判定是否为配置:
import input.files
is_config {
file := files[_]
x := getFileName(file.type, file.path)
is_config_file(x)
}
Tips: 关于
OPA
的内置函数可以查看文档:built-in-functions
虚拟文档
虚拟文档是生成的文档(或者称为集合)
他的特点是:
- 作为集合,输入和输出必须是有限的
- 可以遍历
- 可以查询
举例来说,同样是获取文件名:
import input.files
getFileNames[x] {
file := files[_]
file.type = “posix”
file.path = trim(file.path)
tmp := split(file.path, “/”)
x := tmp[minus(count(tmp), 1)]
}
getFileNames[x] {
file := files[_]
file.type = “traditional-mac”
file.path = trim(file.path)
tmp := split(file.path, “:”)
x := tmp[minus(count(tmp), 1)]
}
这里 getFileNames[x] {}
等价于 getFileNames[x] = x {}
就是x
作为了集合的一个结果,填充到getFileNames
这个虚拟文档中,其key
也是x
的值。
有点抽象是不?
别怕,他可以查询,我们查询看下他的结果就好理解了
cd syntax
opa eval -f values -i input.json -d . "data.example_virtual_doc.getFileNames"
[
[
"1.yaml",
"2.yaml",
"3.yml",
"3.json"
]
]
然后遍历虚拟文档,结合判定is_config_file
就可实现判定:
import data.example_func.is_config_file
import input.files
is_config {
is_config_file(getFileNames[_])
}
这里遍历getFileNames[_]
,随即作为每一个输入参数传给is_config_file
进行判断,是不是很方便!
适用场景
具体到他们的适用场景,其实比较好区分,重点只要关注两点:
- 是否需要遍历?
- 输入输出是否是有限的?
如果有一个答案是肯定的,那么虚拟文档就更适合。
OPA
的函数和虚拟文档很简单、很清秀是不是?
今天就到这里,下一篇我们看看他更优雅的推导式(Comprehensions
)。
本文代码详见:NewbMiao/opa-koans
文章首发公众号:newbmiao
推荐阅读:OPA 系列
更多关于Golang Go语言中 OPA 进阶-函数与虚拟文档要分清的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang Go语言中 OPA 进阶-函数与虚拟文档要分清的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Golang中,OPA(Open Policy Agent)是一个强大的策略引擎,它允许开发者以声明性的方式定义和执行策略。在OPA进阶使用中,函数与虚拟文档是两个核心概念,需要清晰区分。
函数
OPA中的函数与其他编程语言中的函数类似,但具有一些独特特性。例如,可以存在同名函数,但参数数目不能变,相同输入必须获得相同输出。这在实现如判断文件后缀等操作时非常有用。此外,OPA还支持函数重载的简化形式,通过不同参数类型或数量来区分函数。
虚拟文档
虚拟文档是OPA中的一个重要概念,它表示生成的文档或集合。作为集合,虚拟文档的输入和输出必须是有限的,可以遍历和查询。这在处理如配置文件列表等场景时非常有用。通过定义虚拟文档,可以方便地遍历和查询集合中的元素,结合函数实现复杂的策略逻辑。
总结
在OPA进阶使用中,函数和虚拟文档是实现复杂策略逻辑的基础。函数提供了灵活的操作能力,而虚拟文档则提供了对集合数据的遍历和查询能力。开发者需要清晰理解这两个概念,并根据实际需求灵活运用它们来实现高效的策略逻辑。