-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change required scope of entrypoint
from rule
to document
#6963
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -417,7 +417,7 @@ func (a *Annotations) Copy(node Node) *Annotations { | |
return &cpy | ||
} | ||
|
||
// toObject constructs an AST Object from a. | ||
// toObject constructs an AST Object from the annotation. | ||
func (a *Annotations) toObject() (*Object, *Error) { | ||
obj := NewObject() | ||
|
||
|
@@ -556,7 +556,11 @@ func attachAnnotationsNodes(mod *Module) Errors { | |
if a.Scope == "" { | ||
switch a.node.(type) { | ||
case *Rule: | ||
a.Scope = annotationScopeRule | ||
if a.Entrypoint { | ||
a.Scope = annotationScopeDocument | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we left this out, then this breaking change would be more overt, and not hidden. The drawback would be a more verbose annotation block whenever the Personally, I prefer when breaking changes are more obvious. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that's missing the larger point, which is that without this behavior, this change will be breaking every current policy where an entrypoint annotation is currently used today. Here's a list of public repos making use of that: https://github.com/search?q=language%3A%22Open+Policy+Agent%22+%22%23+entrypoint%3A%22&type=code And users of those projects would effectively have to use the latest version of OPA as soon as "their project" bumps the OPA version, as setting Only one of the projects in that list would AFAICS "break" with this change as currently desigend, and that's a single place in Regal. I can deal with that :) So if we want to minimize the impact area, this is the way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that, which is why I'm not making a big fuss ;). Generally though, I think it's better to break user projects by compile-time errors, than to change the semantic behavior and rely on users either reading the changelog (and realizing the impact) or not being adversely affected by the change without noticing. |
||
} else { | ||
a.Scope = annotationScopeRule | ||
} | ||
case *Package: | ||
a.Scope = annotationScopePackage | ||
case *Import: | ||
|
@@ -596,8 +600,9 @@ func validateAnnotationScopeAttachment(a *Annotations) *Error { | |
} | ||
|
||
func validateAnnotationEntrypointAttachment(a *Annotations) *Error { | ||
if a.Entrypoint && !(a.Scope == annotationScopeRule || a.Scope == annotationScopePackage) { | ||
return NewError(ParseErr, a.Loc(), "annotation entrypoint applied to non-rule or package scope '%v'", a.Scope) | ||
if a.Entrypoint && !(a.Scope == annotationScopeDocument || a.Scope == annotationScopePackage) { | ||
return NewError( | ||
ParseErr, a.Loc(), "annotation entrypoint applied to non-document or package scope '%v'", a.Scope) | ||
} | ||
return nil | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,88 @@ import ( | |
"testing" | ||
) | ||
|
||
func TestEntrypointAnnotationScopeRequirements(t *testing.T) { | ||
tests := []struct { | ||
note string | ||
module string | ||
expectError bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we also assert that the scope is set as expected? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I'll amend the tests to do that 👍 |
||
expectScope string | ||
}{ | ||
{ | ||
note: "package scope explicit", | ||
module: `# METADATA | ||
# entrypoint: true | ||
# scope: package | ||
package foo`, | ||
expectError: false, | ||
expectScope: "package", | ||
}, | ||
{ | ||
note: "package scope implied", | ||
module: `# METADATA | ||
# entrypoint: true | ||
package foo`, | ||
expectError: false, | ||
expectScope: "package", | ||
}, | ||
{ | ||
note: "subpackages scope explicit", | ||
module: `# METADATA | ||
# entrypoint: true | ||
# scope: subpackages | ||
package foo`, | ||
expectError: true, | ||
}, | ||
{ | ||
note: "document scope explicit", | ||
module: `package foo | ||
# METADATA | ||
# entrypoint: true | ||
# scope: document | ||
foo := true`, | ||
expectError: false, | ||
expectScope: "document", | ||
}, | ||
{ | ||
note: "document scope implied", | ||
module: `package foo | ||
# METADATA | ||
# entrypoint: true | ||
foo := true`, | ||
expectError: false, | ||
expectScope: "document", | ||
}, | ||
{ | ||
note: "rule scope explicit", | ||
module: `package foo | ||
# METADATA | ||
# entrypoint: true | ||
# scope: rule | ||
foo := true`, | ||
expectError: true, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.note, func(t *testing.T) { | ||
module, err := ParseModuleWithOpts("test.rego", tc.module, ParserOptions{ProcessAnnotation: true}) | ||
if err != nil { | ||
if !tc.expectError { | ||
t.Errorf("unexpected error: %v", err) | ||
} | ||
return | ||
} | ||
if tc.expectError { | ||
t.Fatalf("expected error") | ||
} | ||
if tc.expectScope != module.Annotations[0].Scope { | ||
t.Fatalf("expected scope %q, got %q", tc.expectScope, module.Annotations[0].Scope) | ||
} | ||
}) | ||
} | ||
|
||
} | ||
|
||
// Test of example code in docs/content/annotations.md | ||
func ExampleAnnotationSet_Flatten() { | ||
modules := [][]string{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2687,8 +2687,12 @@ Since the `document` scope annotation applies to all rules with the same name in | |
and the `package` and `subpackages` scope annotations apply to all packages with a matching path, metadata blocks with | ||
these scopes are applied over all files with applicable package- and rule paths. | ||
As there is no ordering across files in the same package, the `document`, `package`, and `subpackages` scope annotations | ||
can only be specified **once** per path. | ||
The `document` scope annotation can be applied to any rule in the set (i.e., ordering does not matter.) | ||
can only be specified **once** per path. The `document` scope annotation can be applied to any rule in the set (i.e., | ||
ordering does not matter.) | ||
|
||
An `entrypoint` annotation implies a `scope` of either `package` or `document`. When `entrypoint` is set to `true` on a | ||
rule, the `scope` is automatically set to `document` if not explicitly provided. Setting the `scope` to `rule` will | ||
result in an error, as an entrypoint always applies to the whole document. | ||
|
||
#### Example | ||
|
||
|
@@ -2708,6 +2712,13 @@ allow if { | |
allow if { | ||
x == 2 | ||
} | ||
# METADATA | ||
# entrypoint: true | ||
# description: | | ||
# `scope` annotation automatically set to `document` | ||
# as that is required for entrypoints | ||
message := "welcome!" if allow | ||
``` | ||
|
||
### Title | ||
|
@@ -2890,7 +2901,8 @@ allow if { | |
### Entrypoint | ||
|
||
The `entrypoint` annotation is a boolean used to mark rules and packages that should be used as entrypoints for a policy. | ||
This value is false by default, and can only be used at `rule` or `package` scope. | ||
This value is false by default, and can only be used at `document` or `package` scope. When used on a rule with no | ||
explicit `scope` set, the presence of an `entrypoint` annotation will automatically set the scope to `document`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed this section doesn't have an example. Should we add one since we're here already? Would be an opportunity to make the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! I'll add an example |
||
|
||
The `build` and `eval` CLI commands will automatically pick up annotated entrypoints; you do not have to specify them with | ||
[`--entrypoint`](../cli/#options-1). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the
document
scope is only implied ifentrypoint
is explicitly set totrue
?Only going by the docs, someone authoring the above would probably expect the metadata to be scoped to
document
. We should either make this explicit in the docs, or enforce thedocument
scope regardless of its value (make it a pointer to a bool, so we can check fornil
? 🤔).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add to the docs that it applies when
entrypoint
is set totrue
.Changing to a pointer is more of a breaking change than anything in here so far, IMHO :)