Go 언어로 Rego runtime 가져오기

19248 단어 GoRegoOPAtech
이 글은 OPA/Rego 추가 캘린더 16일째다.
이번에는 Go 언어에서 Rego의runtime를 호출하여 전략에 대한 평가 결과의 절차를 설명한다.

설정


먼저 Go의 개발 환경을 설정하십시오.이 글은 다음과 같은 환경에서 검증되었다.
  • go: 1.17.2 darwin/arm64
  • opa: v0.34.2
  • Go 개발 환경에 대한 설정은 다음 페이지 등을 참조하십시오.
  • https://go.dev/doc/install
  • 준비 후 아래와 같은 준비 모듈(이하 해설에서 regotest을 작업 디렉터리로 설정하고 다른 명칭에도 문제가 없음)
    $ mkdir regotest
    $ cd regotest
    $ go mod init regotest
    $ go get github.com/open-policy-agent/opa/rego
    

    Rego 실행 시간 활용


    우선 간단한 구체적인 예부터 살펴보자.다음 코드는 main.go./regotest 이하에 저장됩니다.
    main.go
    package main
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/open-policy-agent/opa/rego"
    )
    
    func main() {
    	input := struct {
    		User string `json:"user"`
    	}{
    		User: "mizutani",
    	}
    
    	module := `package blue
    
    	allow {
    		input.user == "mizutani"
    	}
    	`
    
    	q := rego.New(
    		rego.Query(`x := data.blue.allow`),
    		rego.Module("module.rego", module),
    		rego.Input(input),
    	)
    
    	rs, err := q.Eval(context.Background())
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println("allow =>", rs[0].Bindings["x"])
    }
    
    나는 goo 명령으로 집행하면 다음과 같은 결과를 얻을 수 있다고 생각한다.
    $ go run .
    allow => true
    
    Go의 기초 부분에 대한 자세한 내용은 생략하고 레고에 대한 부분은 해설했다.

    쿼리, 모듈 및 입력 설정


    	q := rego.New(
    		rego.Query(`x := data.blue.allow`),
    		rego.Module("module.rego", module),
    		rego.Input(input),
    	)
    
    방법을 통해 운행 시간을 생성한다.Functional Optional Pattern이므로 원하는 수량과 종류의 옵션을 제공하지만 반드시 필요합니다rego.New.이 생성 방법이라면 조회와 모듈의 문법 오류를 실행하기 전까지는 알 수 없기 때문에 먼저 검출하려면 사용할 수 있다Compiler.
    조회와 모듈의 관계는 Rego의 기본(포장편)에서 설명한 바와 같다.지정한 모듈에 기술된 규칙 그룹을 조회하여 호출한 결과를 되돌려줍니다.상기 예에서 rego.Query의 조회x := data.blue.allowx 모듈의 blue를 분배한 결과allow만 해도 data.blue.allow식의 평가 결과만 추출할 수 있다.(자세한 내용은 후술)
    가져오기를 JSON으로 변환한 후 Rego로 처리합니다.따라서 구조적으로 전달되면 json 라벨이 지정한 필드 이름으로 처리됩니다.또한 텍스트 형식으로 수신된 JSON 데이터는 준비interface{}의 변수로 unmarrshal을 한 번 진행해야 한다.

    평가 결과의 취득과 처리


    호출
    	rs, err := q.Eval(context.Background())
    	if err != nil {
    		panic(err)
    	}
    
    Eval()을 통해 전략의 평가 결과(및 오류)를 얻을 수 있습니다.평가 결과는 rego.ResultSetrego.Result의 배열로 돌아왔다.질의data.allowed_users[x]에서 이렇게 지정하면 조건과 일치하는 모든 결과가 반환되므로 여러 평가 결과를 처리할 수 있습니다.이 점에 대해 모듈 측에서 임의의 전략을 기술하더라도 조회 측이 되돌아오는 결과의 수량을 어느 정도 제어할 수 있다.단, 조회가 지정한 평가 결과에 가짜가 포함된 경우 아무런 값도 반환하지 않는다ResultSet는 0이다.
    	input := struct {
    		User string `json:"user"`
    	}{User: "x"}
    
    	module := `package blue
    	allow {
    		input.user == "mizutani"
    	}`
    
    	q := rego.New(
    		rego.Query(`x := data.blue.allow`),
    		rego.Module("module.rego", module),
    		rego.Input(input),
    	)
    
    	rs, err := q.Eval(context.Background())
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println("rs =>", len(rs)) // rs => 0
    
    평가 결과는 rego.Result 내의 ExpressionsBindings 두 필드에 저장된다.
  • Expression: 질의에서 계산된 공식의 결과를 유지합니다.예를 들어 조회가 data.blueblue만 있으면 가방에서 나온 결과{"allow":true}가 저장됩니다.x := data.blue처럼 식의 평가 결과 자체도 남아 있지만'대입'처리의 결과로 값이 들어가면 기록true만 된다.
  • Bindings: 조회에서 새로운 분배 값의 변수
  • 예를 들어, 질의에서만 표현식을 설명하는 경우
    	q := rego.New(
    		rego.Query(`data.blue`),
    		rego.Module("module.rego", module),
    		rego.Input(input),
    	)
    
    	rs, err := q.Eval(context.Background())
    	if err != nil {
    		panic(err)
    	}
    
    	pp.Println(rs)
    
    위에서 말한 바와 같이 결과는 rs[0].Expressions[0].Value["allow"]에 저장된다.
    rego.ResultSet{
      rego.Result{
        Expressions: []*rego.ExpressionValue{
          &rego.ExpressionValue{
            Value: map[string]interface {}{
              "allow": true,
            },
            Text:     "data.blue",
            Location: &rego.Location{
              Row: 1,
              Col: 1,
            },
          },
        },
        Bindings: rego.Vars{},
      },
    }
    
    한 측이 조회를 x := data.blue로 설정하면 새로 분배된x 관련 결과를 rs[0].Bindings["x"]에 저장한다.공식은 위에서 말한 바와 같이 보존만 한다true.
    rego.ResultSet{
      rego.Result{
        Expressions: []*rego.ExpressionValue{
          &rego.ExpressionValue{
            Value:    true,
            Text:     "x := data.blue",
            Location: &rego.Location{
              Row: 1,
              Col: 1,
            },
          },
        },
        Bindings: rego.Vars{
          "x": map[string]interface {}{
            "allow": true,
          },
        },
      },
    }
    

    참고 문헌

  • Integrating with the Go API https://www.openpolicyagent.org/docs/latest/integration/#integrating-with-the-go-api
  • rego package: https://pkg.go.dev/github.com/open-policy-agent/opa/rego#pkg-examples
  • 좋은 웹페이지 즐겨찾기