如何优雅的使用opa进行开发

opa

opa(命令行工具)现在迭代比较快,为了性能和一些丰富的内置函数,推荐使用最新版

1
2
3
4
5
6
7
8
# mac
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_darwin_amd64
chmod +x opa
mv opa /usr/local/bin/opa


# other
go install github.com/open-policy-agent/opa@latest

查看命令帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ ./opa -h
An open source project to policy-enable your service.

Usage:
E:\goproject\gopath\bin\opa.exe [command]

Available Commands:
bench Benchmark a Rego query
build Build an OPA bundle
check Check Rego source files
completion Generate the autocompletion script for the specified shell
deps Analyze Rego query dependencies
eval Evaluate a Rego query
exec Execute against input files
fmt Format Rego source files
help Help about any command
inspect Inspect OPA bundle(s)
parse Parse Rego source file
run Start OPA in interactive or server mode
sign Generate an OPA bundle signature
test Execute Rego test cases
version Print the version of OPA

Flags:
-h, --help help for E:\goproject\gopath\bin\opa.exe

Use "E:\goproject\gopath\bin\opa.exe [command] --help" for more information about a command.

看介绍能知道每个命令是干啥的,用到时具体看 help 就行

这里边挑常用的命令讲下:

run

opa runOPA实现的交互式运行REPL)命令,本地学习或者调试会很方便

1
mkdir quick-start

data.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"roles": [
{
"operation": "read",
"resource": "widgets",
"name": "widget-reader"
},
{
"operation": "write",
"resource": "widgets",
"name": "widget-writer"
}
],
"bindings": [
{
"user": "inspector-alice",
"role": "widget-reader"
},
{
"user": "maker-bob",
"role": "widget-writer"
}
]
}

example.rego

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package example_rbac

default allow = false

# allow will be true when user has role and role has permission
allow {
# opa eval -f pretty -d quick-start -i quick-start/input.json "data.example_rbac.allow" --explain=notes
# trace(role_name)
some role_name
user_has_role[role_name]
role_has_permission[role_name]
}

# check user role binding exist
user_has_role[role_name] {
role_binding = data.bindings[_]
role_binding.role = role_name
role_binding.user == input.subject.user
}

# check role permission exist
role_has_permission[role_name] {
role = data.roles[_]
role.name = role_name
role.operation == input.action.operation
role.resource == input.action.resource
}

example_test.rego

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package example_rbac_test

import data.example_rbac

# tests to coverage 100%

test_not_allow {
not example_rbac.allow with input as {}
}

test_allow {
example_rbac.allow with input as {
"action": {
"operation": "read",
"resource": "widgets",
},
"subject": {"user": "inspector-alice"},
}
}

input.json

1
2
3
4
5
6
7
8
9
{
"action":{
"operation":"read",
"resource":"widgets"
},
"subject":{
"user":"inspector-alice"
}
}

运行opa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ opa run quick-start
OPA 0.37.2 (commit , built at )
Run 'help' to see a list of commands and check for updates.

#获取上下文
> data
{
"action": {
"operation": "read",
"resource": "widgets"
},
...
}

你会得到 json 格式的 data 下的所有节点内容

主要有两类,所有quick-start目录下的

  • 配置文件(.json|.yaml|.yml

    对应的父节点是data

  • Rego文件(包括 test 文件)

对应的父节点是data.<package name>

Tips: 这样配置文件都挂在data这个根节点下了。

如果想加载配置文件时增加父节点(如data.example.file)该怎么办?

可以文件的路径映射前缀 opa run example.file:quick-start注意只会改变配置文件的父节点

这一点很有用,以后讲bundle也会提到

当然也可以指定输入文件input。这个比较特殊,命令行保留了包前缀repl.inputinput

也就是说,可以用repl.input:<path to input.json>的方式传递输入,而避免挂载到data根节点下

1
2
3
4
5
6
7
8
9
10
11
$ opa run quick-start  repl.input:quick-start/input.json
> data.example_rbac
{
"allow": true,
"role_has_permission": [
"widget-reader"
],
"user_has_role": [
"widget-reader"
]
}

Tips: -w支持交互式窗口内实时文件变更 reload

-s支持服务常驻式启动,启动后可以 REST-Api 查询;

eval

opa eval是用来查询策略结果

-d 指定上下文,-i 指定输入 -f 指定返回格式,默认 json,还支持values,bindings,pretty,source

1
2
3
4
5
6
7
8
9
10
11
12
opa eval -f values -d quick-start -i quick-start/input.json "data.example_rbac"
[
{
"allow": true,
"role_has_permission": [
"widget-reader"
],
"user_has_role": [
"widget-reader"
]
}
]

Tips: 还支持性能分析 --metrics, --instrument, --profile

deps

opa deps 用来分析查询依赖

1
2
3
4
5
6
7
8
9
10
$ opa deps -d quick-start "data.example_rbac"
+------------------------+---------------------------------------+
| BASE DOCUMENTS | VIRTUAL DOCUMENTS |
+------------------------+---------------------------------------+
| data.bindings | data.example_rbac.allow |
| data.roles | data.example_rbac.role_has_permission |
| input.action.operation | data.example_rbac.user_has_role |
| input.action.resource | |
| input.subject.user | |
+------------------------+---------------------------------------+

test

opa test 用来跑测试,-c支持输出覆盖率,--threshold 可指定通过覆盖率

check & fmt

opa check 是语法检查;opa fmt是格式化

vscode-opa

命令虽少可还是要记啊,有没有不用敲命令这么费劲的

有!官方强大的交互式编辑器扩展vscode-opa,了解下~~

安装后只要在vscode的配置里指定OPA执行文件路径就可以用了

1
"opa.path": "/usr/local/bin/opa"

功能

它基本支持上述大部分命令功能:

  • 在保存时检查语法(Check Syntax on Save)
  • 计算包结果(Evaluate Packages)
  • 计算选择部分结果(Evaluate Selections)
  • 部分评估选择(Partially Evaluate Selections)
  • 对选择部分代码运行追踪(Trace Selections)
  • 对选择部分性能分析(Profile Selections)
  • 在工作区中运行测试(Run Tests in Workspace)
  • 切换工作区的测试覆盖(Toggle Coverage in Workspace)
  • 切换选择部分的测试覆盖(Toggle Coverage of Selections)

其中评估(Evaluate)和测试覆盖显示(Coverage)在日常开发中很实用

快捷键配置

这里可以配置用户快捷键方便直接调起命令:

(官方推荐的前两个Evaluate的,后边Test相关的也可以加上)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
"key": "cmd+e",
"command": "opa.eval.selection",
"when": "editorLangId == rego"
},
{
"key": "shift+cmd+a",
"command": "opa.eval.package",
"when": "editorLangId == rego"
},
{
"key": "shift+cmd+c",
"command": "opa.test.coverage.workspace",
"when": "editorLangId == rego"
},
{
"key": "shift+cmd+t",
"command": "opa.test.workspace",
"when": "editorLangId == rego"
},
]

Tip: 插件默认只取项目根目录input.json作为输入

配置opa.bundleMode默认为 true,则会只获取目录里的所有data.{json|yaml}

如果设置为 false,则会获取目录中所有.{json|yaml}

好了,现在可以优雅的使用opa开发了。

下一篇我们来讲讲OPA的函数和虚拟文档,了解下他们各自适用的场景。