build(deps): bump github.com/open-policy-agent/opa from 0.59.0 to 0.60.0

Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.59.0 to 0.60.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.59.0...v0.60.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2023-12-21 06:07:34 +00:00
committed by Ralf Haferkamp
parent b62ba69bba
commit f989854f0a
20 changed files with 5320 additions and 90 deletions

4
go.mod
View File

@@ -67,7 +67,7 @@ require (
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.13.2
github.com/onsi/gomega v1.30.0
github.com/open-policy-agent/opa v0.59.0
github.com/open-policy-agent/opa v0.60.0
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20231201125350-a08244876423
github.com/pkg/errors v0.9.1
@@ -297,7 +297,7 @@ require (
github.com/russellhaering/goxmldsig v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/sethvargo/go-password v0.2.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect

8
go.sum
View File

@@ -1784,8 +1784,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/open-policy-agent/opa v0.59.0 h1:1WFU/KUhJAr3qatm0Lf8Ea5jp10ZmlE2M07oaLiHypg=
github.com/open-policy-agent/opa v0.59.0/go.mod h1:rdJSkEc4oQ+0074/3Fsgno5bkPsYxTjU5aLNmMujIvI=
github.com/open-policy-agent/opa v0.60.0 h1:ZPoPt4yeNs5UXCpd/P/btpSyR8CR0wfhVoh9BOwgJNs=
github.com/open-policy-agent/opa v0.60.0/go.mod h1:aD5IK6AiLNYBjNXn7E02++yC8l4Z+bRDvgM6Ss0bBzA=
github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -1933,8 +1933,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sethgrid/pester v1.2.0/go.mod h1:hEUINb4RqvDxtoCaU0BNT/HV4ig5kfgOasrf1xcvr0A=
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=

View File

@@ -24,14 +24,15 @@ type exprChecker func(*TypeEnv, *Expr) *Error
// accumulated on the typeChecker so that a single run can report multiple
// issues.
type typeChecker struct {
builtins map[string]*Builtin
required *Capabilities
errs Errors
exprCheckers map[string]exprChecker
varRewriter varRewriter
ss *SchemaSet
allowNet []string
input types.Type
builtins map[string]*Builtin
required *Capabilities
errs Errors
exprCheckers map[string]exprChecker
varRewriter varRewriter
ss *SchemaSet
allowNet []string
input types.Type
allowUndefinedFuncs bool
}
// newTypeChecker returns a new typeChecker object that has no errors.
@@ -92,6 +93,11 @@ func (tc *typeChecker) WithInputType(tpe types.Type) *typeChecker {
return tc
}
func (tc *typeChecker) WithAllowUndefinedFunctionCalls(allow bool) *typeChecker {
tc.allowUndefinedFuncs = allow
return tc
}
// Env returns a type environment for the specified built-ins with any other
// global types configured on the checker. In practice, this is the default
// environment that other statements will be checked against.
@@ -347,6 +353,9 @@ func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error {
tpe := env.Get(name)
if tpe == nil {
if tc.allowUndefinedFuncs {
return nil
}
return NewError(TypeErr, expr.Location, "undefined function %v", name)
}

View File

@@ -146,6 +146,7 @@ type Compiler struct {
keepModules bool // whether to keep the unprocessed, parse modules (below)
parsedModules map[string]*Module // parsed, but otherwise unprocessed modules, kept track of when keepModules is true
useTypeCheckAnnotations bool // whether to provide annotated information (schemas) to the type checker
allowUndefinedFuncCalls bool // don't error on calls to unknown functions.
evalMode CompilerEvalMode
}
@@ -457,6 +458,11 @@ func (c *Compiler) WithUseTypeCheckAnnotations(enabled bool) *Compiler {
return c
}
func (c *Compiler) WithAllowUndefinedFunctionCalls(allow bool) *Compiler {
c.allowUndefinedFuncCalls = allow
return c
}
// WithEvalMode allows setting the CompilerEvalMode of the compiler
func (c *Compiler) WithEvalMode(e CompilerEvalMode) *Compiler {
c.evalMode = e
@@ -1513,7 +1519,8 @@ func (c *Compiler) checkTypes() {
WithInputType(c.inputType).
WithBuiltins(c.builtins).
WithRequiredCapabilities(c.Required).
WithVarRewriter(rewriteVarsInRef(c.RewrittenVars))
WithVarRewriter(rewriteVarsInRef(c.RewrittenVars)).
WithAllowUndefinedFunctionCalls(c.allowUndefinedFuncCalls)
var as *AnnotationSet
if c.useTypeCheckAnnotations {
as = c.annotationSet
@@ -1537,7 +1544,7 @@ func (c *Compiler) checkUnsafeBuiltins() {
func (c *Compiler) checkDeprecatedBuiltins() {
for _, name := range c.sorted {
mod := c.Modules[name]
if c.strict || mod.regoV1Compatible {
if c.strict || mod.regoV1Compatible() {
errs := checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, mod)
for _, err := range errs {
c.err(err)
@@ -1577,6 +1584,11 @@ func (c *Compiler) compile() {
continue // skip these stages
}
}
if c.allowUndefinedFuncCalls && s.name == "CheckUndefinedFuncs" {
continue
}
c.runStage(s.metricName, s.f)
if c.Failed() {
return
@@ -1683,7 +1695,7 @@ func (c *Compiler) checkDuplicateImports() {
for _, name := range c.sorted {
mod := c.Modules[name]
if c.strict || mod.regoV1Compatible {
if c.strict || mod.regoV1Compatible() {
modules = append(modules, mod)
}
}
@@ -1697,7 +1709,7 @@ func (c *Compiler) checkDuplicateImports() {
func (c *Compiler) checkKeywordOverrides() {
for _, name := range c.sorted {
mod := c.Modules[name]
if c.strict || mod.regoV1Compatible {
if c.strict || mod.regoV1Compatible() {
errs := checkRootDocumentOverrides(mod)
for _, err := range errs {
c.err(err)

View File

@@ -27,6 +27,22 @@ import (
var RegoV1CompatibleRef = Ref{VarTerm("rego"), StringTerm("v1")}
// RegoVersion defines the Rego syntax requirements for a module.
type RegoVersion int
const (
// RegoV0 is the default, original Rego syntax.
RegoV0 RegoVersion = iota
// RegoV0CompatV1 requires modules to comply with both the RegoV0 and RegoV1 syntax (as when 'rego.v1' is imported in a module).
// Shortly, RegoV1 compatibility is required, but 'rego.v1' or 'future.keywords' must also be imported.
RegoV0CompatV1
// RegoV1 is the Rego syntax enforced by OPA 1.0; e.g.:
// future.keywords part of default keyword set, and don't require imports;
// 'if' and 'contains' required in rule heads;
// (some) strict checks on by default.
RegoV1
)
// Note: This state is kept isolated from the parser so that we
// can do efficient shallow copies of these values when doing a
// save() and restore().
@@ -99,14 +115,27 @@ func (e *parsedTermCacheItem) String() string {
// ParserOptions defines the options for parsing Rego statements.
type ParserOptions struct {
Capabilities *Capabilities
ProcessAnnotation bool
AllFutureKeywords bool
FutureKeywords []string
SkipRules bool
JSONOptions *astJSON.Options
unreleasedKeywords bool // TODO(sr): cleanup
Capabilities *Capabilities
ProcessAnnotation bool
AllFutureKeywords bool
FutureKeywords []string
SkipRules bool
JSONOptions *astJSON.Options
// RegoVersion is the version of Rego to parse for.
// RegoV1Compatible additionally affects the Rego version. Use EffectiveRegoVersion to get the effective Rego version.
RegoVersion RegoVersion
// RegoV1Compatible is equivalent to setting RegoVersion to RegoV0CompatV1.
// RegoV1Compatible takes precedence, and if set to true, RegoVersion is ignored.
// Deprecated: use RegoVersion instead. Will be removed in a future version of OPA.
RegoV1Compatible bool
unreleasedKeywords bool // TODO(sr): cleanup
}
func (po *ParserOptions) EffectiveRegoVersion() RegoVersion {
if po.RegoV1Compatible {
return RegoV0CompatV1
}
return po.RegoVersion
}
// NewParser creates and initializes a Parser.
@@ -189,6 +218,11 @@ func (p *Parser) WithJSONOptions(jsonOptions *astJSON.Options) *Parser {
return p
}
func (p *Parser) WithRegoVersion(version RegoVersion) *Parser {
p.po.RegoVersion = version
return p
}
func (p *Parser) parsedTermCacheLookup() (*Term, *state) {
l := p.s.loc.Offset
// stop comparing once the cached offsets are lower than l
@@ -257,16 +291,23 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
allowedFutureKeywords := map[string]tokens.Token{}
for _, kw := range p.po.Capabilities.FutureKeywords {
var ok bool
allowedFutureKeywords[kw], ok = futureKeywords[kw]
if !ok {
return nil, nil, Errors{
&Error{
Code: ParseErr,
Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw),
Location: nil,
},
if p.po.EffectiveRegoVersion() == RegoV1 {
// RegoV1 includes all future keywords in the default language definition
for k, v := range futureKeywords {
allowedFutureKeywords[k] = v
}
} else {
for _, kw := range p.po.Capabilities.FutureKeywords {
var ok bool
allowedFutureKeywords[kw], ok = futureKeywords[kw]
if !ok {
return nil, nil, Errors{
&Error{
Code: ParseErr,
Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw),
Location: nil,
},
}
}
}
}
@@ -284,7 +325,7 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
}
selected := map[string]tokens.Token{}
if p.po.AllFutureKeywords {
if p.po.AllFutureKeywords || p.po.EffectiveRegoVersion() == RegoV1 {
for kw, tok := range allowedFutureKeywords {
selected[kw] = tok
}
@@ -305,6 +346,12 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
}
p.s.s = p.s.s.WithKeywords(selected)
if p.po.EffectiveRegoVersion() == RegoV1 {
for kw, tok := range allowedFutureKeywords {
p.s.s.AddKeyword(kw, tok)
}
}
// read the first token to initialize the parser
p.scan()
@@ -2567,6 +2614,11 @@ func (p *Parser) regoV1Import(imp *Import) {
return
}
if p.po.EffectiveRegoVersion() == RegoV1 {
// We're parsing for Rego v1, where the 'rego.v1' import is a no-op.
return
}
path := imp.Path.Value.(Ref)
if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 {

View File

@@ -477,7 +477,7 @@ func ParseModuleWithOpts(filename, input string, popts ParserOptions) (*Module,
if err != nil {
return nil, err
}
return parseModule(filename, stmts, comments, popts.RegoV1Compatible)
return parseModule(filename, stmts, comments, popts.EffectiveRegoVersion())
}
// ParseBody returns exactly one body.
@@ -626,6 +626,7 @@ func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Sta
WithCapabilities(popts.Capabilities).
WithSkipRules(popts.SkipRules).
WithJSONOptions(popts.JSONOptions).
WithRegoVersion(popts.EffectiveRegoVersion()).
withUnreleasedKeywords(popts.unreleasedKeywords)
stmts, comments, errs := parser.Parse()
@@ -637,7 +638,7 @@ func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Sta
return stmts, comments, nil
}
func parseModule(filename string, stmts []Statement, comments []*Comment, regoV1Compatible bool) (*Module, error) {
func parseModule(filename string, stmts []Statement, comments []*Comment, regoCompatibilityMode RegoVersion) (*Module, error) {
if len(stmts) == 0 {
return nil, NewError(ParseErr, &Location{File: filename}, "empty module")
@@ -658,14 +659,14 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoV1
// The comments slice only holds comments that were not their own statements.
mod.Comments = append(mod.Comments, comments...)
mod.regoV1Compatible = regoV1Compatible
mod.regoVersion = regoCompatibilityMode
for i, stmt := range stmts[1:] {
switch stmt := stmt.(type) {
case *Import:
mod.Imports = append(mod.Imports, stmt)
if Compare(stmt.Path.Value, RegoV1CompatibleRef) == 0 {
mod.regoV1Compatible = true
if mod.regoVersion == RegoV0 && Compare(stmt.Path.Value, RegoV1CompatibleRef) == 0 {
mod.regoVersion = RegoV0CompatV1
}
case *Rule:
setRuleModule(stmt, mod)
@@ -694,7 +695,7 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoV1
}
}
if mod.regoV1Compatible {
if mod.regoVersion == RegoV0CompatV1 || mod.regoVersion == RegoV1 {
for _, rule := range mod.Rules {
for r := rule; r != nil; r = r.Else {
var t string

View File

@@ -145,13 +145,13 @@ type (
// within a namespace (defined by the package) and optional
// dependencies on external documents (defined by imports).
Module struct {
Package *Package `json:"package"`
Imports []*Import `json:"imports,omitempty"`
Annotations []*Annotations `json:"annotations,omitempty"`
Rules []*Rule `json:"rules,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
stmts []Statement
regoV1Compatible bool
Package *Package `json:"package"`
Imports []*Import `json:"imports,omitempty"`
Annotations []*Annotations `json:"annotations,omitempty"`
Rules []*Rule `json:"rules,omitempty"`
Comments []*Comment `json:"comments,omitempty"`
stmts []Statement
regoVersion RegoVersion
}
// Comment contains the raw text from the comment in the definition.
@@ -399,6 +399,14 @@ func (mod *Module) UnmarshalJSON(bs []byte) error {
return nil
}
func (mod *Module) regoV1Compatible() bool {
return mod.regoVersion == RegoV1 || mod.regoVersion == RegoV0CompatV1
}
func (mod *Module) RegoVersion() RegoVersion {
return mod.regoVersion
}
// NewComment returns a new Comment object.
func NewComment(text []byte) *Comment {
return &Comment{

View File

@@ -400,6 +400,7 @@ type Reader struct {
lazyLoadingMode bool
name string
persist bool
regoVersion ast.RegoVersion
}
// NewReader is deprecated. Use NewCustomReader instead.
@@ -503,11 +504,17 @@ func (r *Reader) WithBundlePersistence(persist bool) *Reader {
return r
}
func (r *Reader) WithRegoVersion(version ast.RegoVersion) *Reader {
r.regoVersion = version
return r
}
func (r *Reader) ParserOptions() ast.ParserOptions {
return ast.ParserOptions{
ProcessAnnotation: r.processAnnotations,
Capabilities: r.capabilities,
JSONOptions: r.jsonOptions,
RegoVersion: r.regoVersion,
}
}
@@ -1005,12 +1012,18 @@ func hashBundleFiles(hash SignatureHasher, b *Bundle) ([]FileInfo, error) {
}
// FormatModules formats Rego modules
// Modules will be formatted to comply with rego-v1, but Rego compatibility of individual parsed modules will be respected (e.g. if 'rego.v1' is imported).
func (b *Bundle) FormatModules(useModulePath bool) error {
return b.FormatModulesForRegoVersion(ast.RegoV0, true, useModulePath)
}
// FormatModulesForRegoVersion formats Rego modules to comply with a given Rego version
func (b *Bundle) FormatModulesForRegoVersion(version ast.RegoVersion, preserveModuleRegoVersion bool, useModulePath bool) error {
var err error
for i, module := range b.Modules {
if module.Raw == nil {
module.Raw, err = format.Ast(module.Parsed)
module.Raw, err = format.AstWithOpts(module.Parsed, format.Opts{RegoVersion: version})
if err != nil {
return err
}
@@ -1020,7 +1033,14 @@ func (b *Bundle) FormatModules(useModulePath bool) error {
path = module.Path
}
module.Raw, err = format.Source(path, module.Raw)
opts := format.Opts{}
if preserveModuleRegoVersion {
opts.RegoVersion = module.Parsed.RegoVersion()
} else {
opts.RegoVersion = version
}
module.Raw, err = format.SourceWithOpts(path, module.Raw, opts)
if err != nil {
return err
}

View File

@@ -301,6 +301,7 @@ type ActivateOpts struct {
Bundles map[string]*Bundle // Optional
ExtraModules map[string]*ast.Module // Optional
AuthorizationDecisionRef ast.Ref
ParserOptions ast.ParserOptions
legacy bool
}
@@ -314,10 +315,11 @@ func Activate(opts *ActivateOpts) error {
// DeactivateOpts defines options for the Deactivate API call
type DeactivateOpts struct {
Ctx context.Context
Store storage.Store
Txn storage.Transaction
BundleNames map[string]struct{}
Ctx context.Context
Store storage.Store
Txn storage.Transaction
BundleNames map[string]struct{}
ParserOptions ast.ParserOptions
}
// Deactivate the bundle(s). This will erase associated data, policies, and the manifest entry from the store.
@@ -332,7 +334,7 @@ func Deactivate(opts *DeactivateOpts) error {
erase[root] = struct{}{}
}
}
_, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.BundleNames, erase)
_, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.ParserOptions, opts.BundleNames, erase)
return err
}
@@ -382,7 +384,7 @@ func activateBundles(opts *ActivateOpts) error {
// Erase data and policies at new + old roots, and remove the old
// manifests before activating a new snapshot bundle.
remaining, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, names, erase)
remaining, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.ParserOptions, names, erase)
if err != nil {
return err
}
@@ -585,13 +587,13 @@ func activateDeltaBundles(opts *ActivateOpts, bundles map[string]*Bundle) error
// erase bundles by name and roots. This will clear all policies and data at its roots and remove its
// manifest from storage.
func eraseBundles(ctx context.Context, store storage.Store, txn storage.Transaction, names map[string]struct{}, roots map[string]struct{}) (map[string]*ast.Module, error) {
func eraseBundles(ctx context.Context, store storage.Store, txn storage.Transaction, parserOpts ast.ParserOptions, names map[string]struct{}, roots map[string]struct{}) (map[string]*ast.Module, error) {
if err := eraseData(ctx, store, txn, roots); err != nil {
return nil, err
}
remaining, err := erasePolicies(ctx, store, txn, roots)
remaining, err := erasePolicies(ctx, store, txn, parserOpts, roots)
if err != nil {
return nil, err
}
@@ -633,7 +635,7 @@ func eraseData(ctx context.Context, store storage.Store, txn storage.Transaction
return nil
}
func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transaction, roots map[string]struct{}) (map[string]*ast.Module, error) {
func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transaction, parserOpts ast.ParserOptions, roots map[string]struct{}) (map[string]*ast.Module, error) {
ids, err := store.ListPolicies(ctx, txn)
if err != nil {
@@ -647,7 +649,7 @@ func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transac
if err != nil {
return nil, err
}
module, err := ast.ParseModule(id, string(bs))
module, err := ast.ParseModuleWithOpts(id, string(bs), parserOpts)
if err != nil {
return nil, err
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,7 +26,18 @@ type Opts struct {
// carry along their original source locations.
IgnoreLocations bool
RegoV1 bool
// RegoV1 is equivalent to setting RegoVersion to ast.RegoV0Compat1.
// RegoV1 takes precedence over RegoVersion.
// Deprecated: use RegoVersion instead.
RegoV1 bool
RegoVersion ast.RegoVersion
}
func (o *Opts) effectiveRegoVersion() ast.RegoVersion {
if o.RegoV1 {
return ast.RegoV0CompatV1
}
return o.RegoVersion
}
// defaultLocationFile is the file name used in `Ast()` for terms
@@ -42,12 +53,19 @@ func Source(filename string, src []byte) ([]byte, error) {
}
func SourceWithOpts(filename string, src []byte, opts Opts) ([]byte, error) {
module, err := ast.ParseModule(filename, string(src))
parserOpts := ast.ParserOptions{}
if opts.effectiveRegoVersion() == ast.RegoV1 {
// If the rego version is V1, wee need to parse it as such, to allow for future keywords not being imported.
// Otherwise, we'll default to RegoV0
parserOpts.RegoVersion = ast.RegoV1
}
module, err := ast.ParseModuleWithOpts(filename, string(src), parserOpts)
if err != nil {
return nil, err
}
if opts.RegoV1 {
if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 || opts.effectiveRegoVersion() == ast.RegoV1 {
errors := ast.CheckRegoV1(module)
if len(errors) > 0 {
return nil, errors
@@ -115,7 +133,7 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
o := fmtOpts{}
if opts.RegoV1 {
if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 || opts.effectiveRegoVersion() == ast.RegoV1 {
o.regoV1 = true
o.ifs = true
o.contains = true
@@ -176,10 +194,13 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
switch x := x.(type) {
case *ast.Module:
if o.regoV1 {
if opts.effectiveRegoVersion() == ast.RegoV1 {
x.Imports = filterRegoV1Import(x.Imports)
} else if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 {
x.Imports = ensureRegoV1Import(x.Imports)
}
if o.regoV1 || moduleIsRegoV1Compatible(x) {
if opts.effectiveRegoVersion() == ast.RegoV0CompatV1 || opts.effectiveRegoVersion() == ast.RegoV1 || moduleIsRegoV1Compatible(x) {
x.Imports = future.FilterFutureImports(x.Imports)
} else {
for kw := range extraFutureKeywordImports {
@@ -535,7 +556,11 @@ func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, o fm
// * a.b.c.d -> a.b.c.d := true
isRegoV1RefConst := o.regoV1 && isExpandedConst && head.Key == nil && len(head.Args) == 0
if head.Location == head.Value.Location && head.Name != "else" && !isRegoV1RefConst {
if len(head.Args) > 0 &&
head.Location == head.Value.Location &&
head.Name != "else" &&
ast.Compare(head.Value, ast.BooleanTerm(true)) == 0 &&
!isRegoV1RefConst {
// If the value location is the same as the location of the head,
// we know that the value is generated, i.e. f(1)
// Don't print the value (` = true`) as it is implied.
@@ -1444,6 +1469,17 @@ func ensureRegoV1Import(imps []*ast.Import) []*ast.Import {
return ensureImport(imps, ast.RegoV1CompatibleRef)
}
func filterRegoV1Import(imps []*ast.Import) []*ast.Import {
var ret []*ast.Import
for _, imp := range imps {
path := imp.Path.Value.(ast.Ref)
if !ast.RegoV1CompatibleRef.Equal(path) {
ret = append(ret, imp)
}
}
return ret
}
func ensureImport(imps []*ast.Import, path ast.Ref) []*ast.Import {
for _, imp := range imps {
p := imp.Path.Value.(ast.Ref)

View File

@@ -0,0 +1,184 @@
// Copyright 2020 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
// Package report provides functions to report OPA's version information to an external service and process the response.
package report
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/open-policy-agent/opa/keys"
"github.com/open-policy-agent/opa/logging"
"github.com/open-policy-agent/opa/plugins/rest"
"github.com/open-policy-agent/opa/util"
"github.com/open-policy-agent/opa/version"
)
// ExternalServiceURL is the base HTTP URL for a telemetry service.
// If not otherwise specified it will use the hard coded default.
//
// Override at build time via:
//
// -ldflags "-X github.com/open-policy-agent/opa/internal/report.ExternalServiceURL=<url>"
//
// This will be overridden if the OPA_TELEMETRY_SERVICE_URL environment variable
// is provided.
var ExternalServiceURL = "https://telemetry.openpolicyagent.org"
// Reporter reports information such as the version, heap usage about the running OPA instance to an external service
type Reporter struct {
body map[string]any
client rest.Client
gatherers map[string]Gatherer
gatherersMtx sync.Mutex
}
// Gatherer represents a mechanism to inject additional data in the telemetry report
type Gatherer func(ctx context.Context) (any, error)
// DataResponse represents the data returned by the external service
type DataResponse struct {
Latest ReleaseDetails `json:"latest,omitempty"`
}
// ReleaseDetails holds information about the latest OPA release
type ReleaseDetails struct {
Download string `json:"download,omitempty"` // link to download the OPA release
ReleaseNotes string `json:"release_notes,omitempty"` // link to the OPA release notes
LatestRelease string `json:"latest_release,omitempty"` // latest OPA released version
OPAUpToDate bool `json:"opa_up_to_date,omitempty"` // is running OPA version greater than or equal to the latest released
}
// Options supplies parameters to the reporter.
type Options struct {
Logger logging.Logger
}
// New returns an instance of the Reporter
func New(id string, opts Options) (*Reporter, error) {
r := Reporter{
gatherers: map[string]Gatherer{},
}
r.body = map[string]any{
"id": id,
"version": version.Version,
}
url := os.Getenv("OPA_TELEMETRY_SERVICE_URL")
if url == "" {
url = ExternalServiceURL
}
restConfig := []byte(fmt.Sprintf(`{
"url": %q,
}`, url))
client, err := rest.New(restConfig, map[string]*keys.Config{}, rest.Logger(opts.Logger))
if err != nil {
return nil, err
}
r.client = client
// heap_usage_bytes is always present, so register it unconditionally
r.RegisterGatherer("heap_usage_bytes", readRuntimeMemStats)
return &r, nil
}
// SendReport sends the telemetry report which includes information such as the OPA version, current memory usage to
// the external service
func (r *Reporter) SendReport(ctx context.Context) (*DataResponse, error) {
rCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
r.gatherersMtx.Lock()
defer r.gatherersMtx.Unlock()
for key, g := range r.gatherers {
var err error
r.body[key], err = g(rCtx)
if err != nil {
return nil, fmt.Errorf("gather telemetry error for key %s: %w", key, err)
}
}
resp, err := r.client.WithJSON(r.body).Do(rCtx, "POST", "/v1/version")
if err != nil {
return nil, err
}
defer util.Close(resp)
switch resp.StatusCode {
case http.StatusOK:
if resp.Body != nil {
var result DataResponse
err := json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return nil, err
}
return &result, nil
}
return nil, nil
default:
return nil, fmt.Errorf("server replied with HTTP %v", resp.StatusCode)
}
}
func (r *Reporter) RegisterGatherer(key string, f Gatherer) {
r.gatherersMtx.Lock()
r.gatherers[key] = f
r.gatherersMtx.Unlock()
}
// IsSet returns true if dr is populated.
func (dr *DataResponse) IsSet() bool {
return dr != nil && dr.Latest.LatestRelease != "" && dr.Latest.Download != "" && dr.Latest.ReleaseNotes != ""
}
// Slice returns the dr as a slice of key-value string pairs. If dr is nil, this function returns an empty slice.
func (dr *DataResponse) Slice() [][2]string {
if !dr.IsSet() {
return nil
}
return [][2]string{
{"Latest Upstream Version", strings.TrimPrefix(dr.Latest.LatestRelease, "v")},
{"Download", dr.Latest.Download},
{"Release Notes", dr.Latest.ReleaseNotes},
}
}
// Pretty returns OPA release information in a human-readable format.
func (dr *DataResponse) Pretty() string {
if !dr.IsSet() {
return ""
}
pairs := dr.Slice()
lines := make([]string, 0, len(pairs))
for _, pair := range pairs {
lines = append(lines, fmt.Sprintf("%v: %v", pair[0], pair[1]))
}
return strings.Join(lines, "\n")
}
func readRuntimeMemStats(_ context.Context) (any, error) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return strconv.FormatUint(m.Alloc, 10), nil
}

View File

@@ -122,6 +122,18 @@ func LoadPaths(paths []string,
processAnnotations bool,
caps *ast.Capabilities,
fsys fs.FS) (*LoadPathsResult, error) {
return LoadPathsForRegoVersion(ast.RegoV0, paths, filter, asBundle, bvc, skipVerify, processAnnotations, caps, fsys)
}
func LoadPathsForRegoVersion(regoVersion ast.RegoVersion,
paths []string,
filter loader.Filter,
asBundle bool,
bvc *bundle.VerificationConfig,
skipVerify bool,
processAnnotations bool,
caps *ast.Capabilities,
fsys fs.FS) (*LoadPathsResult, error) {
if caps == nil {
caps = ast.CapabilitiesForThisVersion()
@@ -146,6 +158,7 @@ func LoadPaths(paths []string,
WithFilter(filter).
WithProcessAnnotation(processAnnotations).
WithCapabilities(caps).
WithRegoVersion(regoVersion).
AsBundle(path)
if err != nil {
return nil, err
@@ -161,6 +174,7 @@ func LoadPaths(paths []string,
WithFS(fsys).
WithProcessAnnotation(processAnnotations).
WithCapabilities(caps).
WithRegoVersion(regoVersion).
Filtered(nonBundlePaths, filter)
if err != nil {

View File

@@ -103,7 +103,11 @@ type FileLoader interface {
WithCapabilities(*ast.Capabilities) FileLoader
WithJSONOptions(*astJSON.Options) FileLoader
// WithRegoV1Compatible
// Deprecated: use WithRegoVersion instead
WithRegoV1Compatible(bool) FileLoader
WithRegoVersion(ast.RegoVersion) FileLoader
}
// NewFileLoader returns a new FileLoader instance.
@@ -183,11 +187,20 @@ func (fl *fileLoader) WithJSONOptions(opts *astJSON.Options) FileLoader {
return fl
}
// WithRegoV1Compatible enforces Rego v0 with Rego v1 compatibility.
// See ParserOptions.RegoV1Compatible for more details.
// Deprecated: use WithRegoVersion instead
func (fl *fileLoader) WithRegoV1Compatible(compatible bool) FileLoader {
fl.opts.RegoV1Compatible = compatible
return fl
}
// WithRegoVersion sets the ast.RegoVersion to use when parsing and compiling modules.
func (fl *fileLoader) WithRegoVersion(version ast.RegoVersion) FileLoader {
fl.opts.RegoVersion = version
return fl
}
// All returns a Result object loaded (recursively) from the specified paths.
func (fl fileLoader) All(paths []string) (*Result, error) {
return fl.Filtered(paths, nil)
@@ -256,7 +269,8 @@ func (fl fileLoader) AsBundle(path string) (*bundle.Bundle, error) {
WithSkipBundleVerification(fl.skipVerify).
WithProcessAnnotations(fl.opts.ProcessAnnotation).
WithCapabilities(fl.opts.Capabilities).
WithJSONOptions(fl.opts.JSONOptions)
WithJSONOptions(fl.opts.JSONOptions).
WithRegoVersion(fl.opts.EffectiveRegoVersion())
// For bundle directories add the full path in front of module file names
// to simplify debugging.
@@ -706,7 +720,7 @@ func loadKnownTypes(path string, bs []byte, m metrics.Metrics, opts ast.ParserOp
return loadYAML(path, bs, m)
default:
if strings.HasSuffix(path, ".tar.gz") {
r, err := loadBundleFile(path, bs, m)
r, err := loadBundleFile(path, bs, m, opts)
if err != nil {
err = fmt.Errorf("bundle %s: %w", path, err)
}
@@ -732,9 +746,15 @@ func loadFileForAnyType(path string, bs []byte, m metrics.Metrics, opts ast.Pars
return nil, unrecognizedFile(path)
}
func loadBundleFile(path string, bs []byte, m metrics.Metrics) (bundle.Bundle, error) {
func loadBundleFile(path string, bs []byte, m metrics.Metrics, opts ast.ParserOptions) (bundle.Bundle, error) {
tl := bundle.NewTarballLoaderWithBaseURL(bytes.NewBuffer(bs), path)
br := bundle.NewCustomReader(tl).WithMetrics(m).WithSkipBundleVerification(true).IncludeManifestInData(true)
br := bundle.NewCustomReader(tl).
WithRegoVersion(opts.RegoVersion).
WithJSONOptions(opts.JSONOptions).
WithProcessAnnotations(opts.ProcessAnnotation).
WithMetrics(m).
WithSkipBundleVerification(true).
IncludeManifestInData(true)
return br.Read()
}

View File

@@ -8,9 +8,11 @@ package plugins
import (
"context"
"fmt"
mr "math/rand"
"sync"
"time"
"github.com/open-policy-agent/opa/internal/report"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/sdk/trace"
@@ -148,6 +150,9 @@ const (
DefaultTriggerMode TriggerMode = "periodic"
)
// default interval between OPA report uploads
var defaultUploadIntervalSec = int64(3600)
// Status has a Plugin's current status plus an optional Message.
type Status struct {
State State `json:"state"`
@@ -198,8 +203,13 @@ type Manager struct {
tracerProvider *trace.TracerProvider
distributedTacingOpts tracing.Options
registeredNDCacheTriggers []func(bool)
registeredTelemetryGatherers map[string]report.Gatherer
bootstrapConfigLabels map[string]string
hooks hooks.Hooks
enableTelemetry bool
reporter *report.Reporter
opaReportNotifyCh chan struct{}
stop chan chan struct{}
}
type managerContextKey string
@@ -385,6 +395,21 @@ func WithHooks(hs hooks.Hooks) func(*Manager) {
}
}
// WithEnableTelemetry controls whether OPA will send telemetry reports to an external service.
func WithEnableTelemetry(enableTelemetry bool) func(*Manager) {
return func(m *Manager) {
m.enableTelemetry = enableTelemetry
}
}
// WithTelemetryGatherers allows registration of telemetry gatherers which enable injection of additional data in the
// telemetry report
func WithTelemetryGatherers(gs map[string]report.Gatherer) func(*Manager) {
return func(m *Manager) {
m.registeredTelemetryGatherers = gs
}
}
// New creates a new Manager using config.
func New(raw []byte, id string, store storage.Store, opts ...func(*Manager)) (*Manager, error) {
@@ -453,6 +478,27 @@ func New(raw []byte, id string, store storage.Store, opts ...func(*Manager)) (*M
return nil, err
}
if m.enableTelemetry {
reporter, err := report.New(id, report.Options{Logger: m.logger})
if err != nil {
return nil, err
}
m.reporter = reporter
m.reporter.RegisterGatherer("min_compatible_version", func(_ context.Context) (any, error) {
var minimumCompatibleVersion string
if m.compiler != nil && m.compiler.Required != nil {
minimumCompatibleVersion, _ = m.compiler.Required.MinimumCompatibleVersion()
}
return minimumCompatibleVersion, nil
})
// register any additional gatherers
for k, g := range m.registeredTelemetryGatherers {
m.reporter.RegisterGatherer(k, g)
}
}
return m, nil
}
@@ -469,6 +515,12 @@ func (m *Manager) Init(ctx context.Context) error {
Context: storage.NewContext(),
}
if m.enableTelemetry {
m.opaReportNotifyCh = make(chan struct{})
m.stop = make(chan chan struct{})
go m.sendOPAUpdateLoop(ctx)
}
err := storage.Txn(ctx, m.Store, params, func(txn storage.Transaction) error {
result, err := initload.InsertAndCompile(ctx, initload.InsertAndCompileOptions{
@@ -497,6 +549,12 @@ func (m *Manager) Init(ctx context.Context) error {
})
if err != nil {
if m.stop != nil {
done := make(chan struct{})
m.stop <- done
<-done
}
return err
}
@@ -673,6 +731,12 @@ func (m *Manager) Stop(ctx context.Context) {
m.logger.Error("Error closing store: %v", err)
}
}
if m.stop != nil {
done := make(chan struct{})
m.stop <- done
<-done
}
}
// Reconfigure updates the configuration on the manager.
@@ -817,6 +881,11 @@ func (m *Manager) onCommit(ctx context.Context, txn storage.Transaction, event s
if compiler != nil {
m.setCompiler(compiler)
if m.enableTelemetry && event.PolicyChanged() {
m.opaReportNotifyCh <- struct{}{}
}
for _, f := range m.registeredTriggers {
f(txn)
}
@@ -982,3 +1051,37 @@ func (m *Manager) RegisterNDCacheTrigger(trigger func(bool)) {
defer m.mtx.Unlock()
m.registeredNDCacheTriggers = append(m.registeredNDCacheTriggers, trigger)
}
func (m *Manager) sendOPAUpdateLoop(ctx context.Context) {
ticker := time.NewTicker(time.Duration(int64(time.Second) * defaultUploadIntervalSec))
mr.New(mr.NewSource(time.Now().UnixNano()))
ctx, cancel := context.WithCancel(ctx)
var opaReportNotify bool
for {
select {
case <-m.opaReportNotifyCh:
opaReportNotify = true
case <-ticker.C:
ticker.Stop()
if opaReportNotify {
opaReportNotify = false
_, err := m.reporter.SendReport(ctx)
if err != nil {
m.logger.WithFields(map[string]interface{}{"err": err}).Debug("Unable to send OPA telemetry report.")
}
}
newInterval := mr.Int63n(defaultUploadIntervalSec) + defaultUploadIntervalSec
ticker = time.NewTicker(time.Duration(int64(time.Second) * newInterval))
case done := <-m.stop:
cancel()
ticker.Stop()
done <- struct{}{}
return
}
}
}

View File

@@ -594,11 +594,13 @@ type Rego struct {
pluginMgr *plugins.Manager
plugins []TargetPlugin
targetPrepState TargetPluginEval
regoVersion ast.RegoVersion
}
// Function represents a built-in function that is callable in Rego.
type Function struct {
Name string
Description string
Decl *types.Function
Memoize bool
Nondeterministic bool
@@ -629,6 +631,7 @@ type (
func RegisterBuiltin1(decl *Function, impl Builtin1) {
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: decl.Description,
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
@@ -642,6 +645,7 @@ func RegisterBuiltin1(decl *Function, impl Builtin1) {
func RegisterBuiltin2(decl *Function, impl Builtin2) {
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: decl.Description,
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
@@ -655,6 +659,7 @@ func RegisterBuiltin2(decl *Function, impl Builtin2) {
func RegisterBuiltin3(decl *Function, impl Builtin3) {
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: decl.Description,
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
@@ -668,6 +673,7 @@ func RegisterBuiltin3(decl *Function, impl Builtin3) {
func RegisterBuiltin4(decl *Function, impl Builtin4) {
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: decl.Description,
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
@@ -681,6 +687,7 @@ func RegisterBuiltin4(decl *Function, impl Builtin4) {
func RegisterBuiltinDyn(decl *Function, impl BuiltinDyn) {
ast.RegisterBuiltin(&ast.Builtin{
Name: decl.Name,
Description: decl.Description,
Decl: decl.Decl,
Nondeterministic: decl.Nondeterministic,
})
@@ -1187,6 +1194,12 @@ func Strict(yes bool) func(r *Rego) {
}
}
func SetRegoVersion(version ast.RegoVersion) func(r *Rego) {
return func(r *Rego) {
r.regoVersion = version
}
}
// New returns a new Rego object.
func New(options ...func(r *Rego)) *Rego {
@@ -1797,7 +1810,7 @@ func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metr
return err
}
parsed, err := ast.ParseModule(id, string(bs))
parsed, err := ast.ParseModuleWithOpts(id, string(bs), ast.ParserOptions{RegoVersion: r.regoVersion})
if err != nil {
errs = append(errs, err)
}
@@ -1807,7 +1820,7 @@ func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metr
// Parse any passed in as arguments to the Rego object
for _, module := range r.modules {
p, err := module.Parse()
p, err := module.ParseWithOpts(ast.ParserOptions{RegoVersion: r.regoVersion})
if err != nil {
switch errorWithType := err.(type) {
case ast.Errors:
@@ -1839,6 +1852,7 @@ func (r *Rego) loadFiles(ctx context.Context, txn storage.Transaction, m metrics
result, err := loader.NewFileLoader().
WithMetrics(m).
WithProcessAnnotation(true).
WithRegoVersion(r.regoVersion).
Filtered(r.loadPaths.paths, r.loadPaths.filter)
if err != nil {
return err
@@ -1869,6 +1883,7 @@ func (r *Rego) loadBundles(ctx context.Context, txn storage.Transaction, m metri
WithMetrics(m).
WithProcessAnnotation(true).
WithSkipBundleVerification(r.skipBundleVerification).
WithRegoVersion(r.regoVersion).
AsBundle(path)
if err != nil {
return fmt.Errorf("loading error: %s", err)
@@ -1934,13 +1949,14 @@ func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m me
// Use this as the single-point of compiling everything only a
// single time.
opts := &bundle.ActivateOpts{
Ctx: ctx,
Store: r.store,
Txn: txn,
Compiler: r.compilerForTxn(ctx, r.store, txn),
Metrics: m,
Bundles: r.bundles,
ExtraModules: r.parsedModules,
Ctx: ctx,
Store: r.store,
Txn: txn,
Compiler: r.compilerForTxn(ctx, r.store, txn),
Metrics: m,
Bundles: r.bundles,
ExtraModules: r.parsedModules,
ParserOptions: ast.ParserOptions{RegoVersion: r.regoVersion},
}
err := bundle.Activate(opts)
if err != nil {
@@ -2608,6 +2624,10 @@ func (m rawModule) Parse() (*ast.Module, error) {
return ast.ParseModule(m.filename, m.module)
}
func (m rawModule) ParseWithOpts(opts ast.ParserOptions) (*ast.Module, error) {
return ast.ParseModuleWithOpts(m.filename, m.module, opts)
}
type extraStage struct {
after string
stage ast.QueryCompilerStageDefinition

View File

@@ -172,10 +172,21 @@ func builtinAWSSigV4SignReq(ctx BuiltinContext, operands []*ast.Term, iter func(
}
// Sign the request object's headers, and reconstruct the headers map.
authHeader, signedHeadersMap := aws.SignV4(objectToMap(headers), method, theURL, body, service, awsCreds, signingTimestamp)
headersMap := objectToMap(headers)
authHeader, awsHeadersMap := aws.SignV4(headersMap, method, theURL, body, service, awsCreds, signingTimestamp)
signedHeadersObj := ast.NewObject()
// Restore original headers
for k, v := range headersMap {
// objectToMap doesn't support arrays
if len(v) == 1 {
signedHeadersObj.Insert(ast.StringTerm(k), ast.StringTerm(v[0]))
}
}
// Set authorization header
signedHeadersObj.Insert(ast.StringTerm("Authorization"), ast.StringTerm(authHeader))
for k, v := range signedHeadersMap {
// set aws signature headers
for k, v := range awsHeadersMap {
signedHeadersObj.Insert(ast.StringTerm(k), ast.StringTerm(v))
}

View File

@@ -11,7 +11,7 @@ import (
)
// Version is the canonical version of OPA.
var Version = "0.59.0"
var Version = "0.60.0"
// GoVersion is the version of Go this was built with
var GoVersion = runtime.Version()

View File

@@ -1313,17 +1313,17 @@ func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, stri
// '\x00' is a valid character, but various debuggers don't like it. So we'll insert a junk entry to avoid generating a null character.
lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'
lineHash := make(map[string]int)
//Each string has the index of lineArray which it points to
strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray)
strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray)
strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray, lineHash)
strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray, lineHash)
return intArrayToString(strIndexArray1), intArrayToString(strIndexArray2), lineArray
}
// diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string.
func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string) []uint32 {
func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string, lineHash map[string]int) []uint32 {
// Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect.
lineHash := map[string]int{} // e.g. lineHash['Hello\n'] == 4
lineStart := 0
lineEnd := -1
strs := []uint32{}

5
vendor/modules.txt vendored
View File

@@ -1482,7 +1482,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge
github.com/onsi/gomega/matchers/support/goraph/node
github.com/onsi/gomega/matchers/support/goraph/util
github.com/onsi/gomega/types
# github.com/open-policy-agent/opa v0.59.0
# github.com/open-policy-agent/opa v0.60.0
## explicit; go 1.19
github.com/open-policy-agent/opa/ast
github.com/open-policy-agent/opa/ast/internal/scanner
@@ -1531,6 +1531,7 @@ github.com/open-policy-agent/opa/internal/providers/aws/crypto
github.com/open-policy-agent/opa/internal/providers/aws/v4
github.com/open-policy-agent/opa/internal/ref
github.com/open-policy-agent/opa/internal/rego/opa
github.com/open-policy-agent/opa/internal/report
github.com/open-policy-agent/opa/internal/runtime/init
github.com/open-policy-agent/opa/internal/semver
github.com/open-policy-agent/opa/internal/strings
@@ -1675,7 +1676,7 @@ github.com/russross/blackfriday/v2
# github.com/segmentio/ksuid v1.0.4
## explicit; go 1.12
github.com/segmentio/ksuid
# github.com/sergi/go-diff v1.2.0
# github.com/sergi/go-diff v1.3.1
## explicit; go 1.12
github.com/sergi/go-diff/diffmatchpatch
# github.com/sethvargo/go-password v0.2.0