-
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
v1 SDK #7161
base: main
Are you sure you want to change the base?
v1 SDK #7161
Conversation
✅ Deploy Preview for openpolicyagent ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
Hm, looks like I based the branch off of an old working branch. Will rebase on |
a679c17
to
cf4dbe3
Compare
@@ -97,6 +97,7 @@ func CapabilitiesForThisVersion() *Capabilities { | |||
return f.Builtins[i].Name < f.Builtins[j].Name | |||
}) | |||
|
|||
// FIXME: In v1, this list should be empty |
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.
For ref: #6290
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
c52b291
to
79594df
Compare
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
@@ -689,6 +691,10 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoCo | |||
|
|||
// The comments slice only holds comments that were not their own statements. | |||
mod.Comments = append(mod.Comments, comments...) | |||
if regoCompatibilityMode == RegoUndefined { | |||
// We default to v0 behaviour for backwards compatibility reasons. |
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.
We default to v0
here as we have a v1
variant of the external parser.
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.
Correct
@@ -50,7 +50,8 @@ type Value interface { | |||
Find(path Ref) (Value, error) // Find returns value referred to by path or an error if path is not found. | |||
Hash() int // Returns hash code of the value. | |||
IsGround() bool // IsGround returns true if this value is not a variable or contains no variables. | |||
String() string // String returns a human readable string representation of the value. | |||
// FIXME: Some String() impls depend on DefaultRegoVersion. Do we need a v1/ast/ValueToString()? | |||
String() string // String returns a human readable string representation of the value. |
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.
Seems like it. At least it would be consistent.
@@ -17,9 +17,9 @@ import ( | |||
"github.com/open-policy-agent/opa/ast" |
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.
Shouldn't we have a v1/ast
? Otherwise wouldn't ast.DefaultRegoVersion
would be v0
?
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'm not sure a v1/ast
lends itself to a good developer experience. Most likely, if you're using v1/ast.DefaultRegoVersion
, you're probably also using other ast
types that don't exist in v1
. Then you'd need to do aliased imports to not have conflicts:
import (
"github.com/open-policy-agent/opa/ast"
v1ast "github.com/open-policy-agent/opa/v1/ast"
)
If you already have github.com/open-policy-agent/opa/ast
imported, auto-complete in your IDE would most likely bring you the wrong type.
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.
We could maybe have a const DefaultRegoVersion = ast.RegoV1
directly in the v1
package, that ties down all rego-version usage for v1
internally 🤔.
E.g.:
opa/v1/rego_version.go
:
package v1
import "github.com/open-policy-agent/opa/ast"
const DefaultRegoVersion = ast.RegoV1
opa/v1/parser/parser.go:
...
func NewParser(opts ...Option) Parser {
p := ast.NewParser()
options := Options{
p: p,
}
for _, opt := range opts {
opt(&options)
}
if p.ParserOptions().RegoVersion == ast.RegoUndefined {
p = p.WithRegoVersion(v1.DefaultRegoVersion)
}
return p
}
This wouldn't cause any ast
import conflicts for users should they choose to use it, and the v1.DefaultRegoVersion
(or v1.RegoVersion
even) makes it pretty clear what it is.
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.
ast.DefaultRegoVersion
is something of a misnomer, though as this doesn't apply to the SDK components, only to cmd/services that we don't see as part of the SDK ..
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.
Doesn't look like Intellij/Goland can handle the opa/v1
package reference from a child package like opa/v1/rego
😞.
Command-line compilation doesn't seem to have any issues, though.
So to support those IDE:s for OPA development, we'd need to put this in some child pkg if we decide this is a good route to go down. But then we'll lose the nice free v1.
prefix.
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 raised this since we're using ast.DefaultRegoVersion
. How is this correctly set?
cmd/build.go
Outdated
@@ -309,14 +320,17 @@ func dobuild(params buildParams, args []string) error { | |||
WithPartialNamespace(params.ns). | |||
WithFollowSymlinks(params.followSymlinks) | |||
|
|||
regoVersion := ast.DefaultRegoVersion | |||
regoVersion := ast.RegoUndefined | |||
if params.v0Compatible { | |||
// v0 takes precedence over v1 |
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.
Is this still the case for v1?
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.
Yes, technically still true; but we'd probably drop the params.v1Compatible
check in 1.0.
Come to think of it, we could probably completely drop this check, as WithRegoVersion(params.regoVersion())
already does this for us 🤔.
profiler/profiler_bench_test.go
Outdated
@@ -33,6 +33,7 @@ func BenchmarkProfilerBigLocalVar(b *testing.B) { | |||
ctx := context.Background() | |||
|
|||
pq, err := rego.New( | |||
rego.SetRegoVersion(ast.RegoV1), |
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.
Why not use ast.DefaultRegoVersion
here as we do in other places?
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.
This is a mistype. Should be ast.RegoV0
, since the generated module is v0 Rego. I'll drop this and update the code generation instead.
regoVersion ast.RegoVersion | ||
} | ||
// | ||
// Deprecated: Use [v1.Rego] instead. |
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.
Hmm do we need deprecation labels for all the aliases?
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'm not sure. Could be unnecessary. But by deprecating the type itself, users' IDE:s will tell them they're using an outdated type. wherever that type is declared, and not just at instantiation (if we only deprecate the factory function).
|
||
// RegisterBuiltin1 adds a built-in function globally inside the OPA runtime. | ||
// | ||
// Deprecated: Use the Compiler option in the [github.com/open-policy-agent/opa/v1] package instead. |
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.
Not sure I understand this comment here.
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.
Hm, this should have been [github.com/open-policy-agent/opa/v1/rego]
.
I wanted to be general here, to not need to be specific for each function (e.g. [github.com/open-policy-agent/opa/v1/rego.RegisterBuiltin1]
Maybe something like this is more easy to understand(?):
Deprecated: Use the equivalent Compiler option in the [github.com/open-policy-agent/opa/v1/rego] package instead.
Or we just bite the bullet end give a specific link for each option:
Deprecated: Use [github.com/open-policy-agent/opa/v1/rego.RegisterBuiltin1] instead.
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.
👇 seems more clear.
Deprecated: Use [github.com/open-policy-agent/opa/v1/rego.RegisterBuiltin1] instead.
If you search for the |
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
3155b49
to
ee146a2
Compare
I think that's OK. Going forward, the DX of v1 should be the priority. Do you two think that we should put some notes somewhere to calm people? I'm wondering about the notion of "Deprecation" and potential reactions along the lines of "OMG the function will be deleted, I need to work on this or we'll have to drop OPA now because it's so much work!" What I mean is, what does "deprecation of v0" mean for our users? Do they have a way to find out the answer themselves? |
I'm a little hesitant about the top-level import "github.com/open-policy-agent/opa/v2/sdk" then I believe the There's a variant of this where you can have v1 and v2 side-by-side, as top-level folders... I'm not sure about all this; it's not someting I've tried before. But I suppose to be sure, it would make sense to have a small test program that'll import and use the new packages. |
options := Options{ | ||
p: p, | ||
} | ||
|
||
for _, opt := range opts { | ||
opt(&options) | ||
} |
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 we create options
, apply options to them, and then discard it, because what we wanted to happen happened to the p
inside options
.
This looks funny. I mean, I see what's going on, and it's a clever way to provide "shadow" options of sorts...
var RegoVersion = ast.RegoVersion
This would probably also allow us to do
p := ast.NewParser()
for _, opt := range opts {
opt(p)
}
? But I think it's not a huge advantage over what we have here, is it?
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.
The idea was to not have any of the option functions leak "internal" types, and thereby make the v1 parser API look contained.
I'm not sure I understand your suggestion. You're saying Option
should be type Option func(*ast.Parser)
?
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.
You're saying Option should be
type Option func(*ast.Parser)
?
Uhm yeah I guess that's what it would amount to. But it would leave us less room for changes in the future, so it's probably a bad suggestion.
🚧 This is a draft PR for discussion 🚧
This PR employs two primary methods for extracting a
v1
SDK while retaining old v0 behavior for existing users (no changes required to existing code):v1.rego.Rego
v1.sdk.OPA
v1.compile.BundleCompiler
v1.parser.Parser
: Put outside theast
package to avoid users needing to do import aliasing when importing both../v1/ast
and../ast
.Note: type aliasing breaks godoc generation, and requires users to click through on the aliased type to get documentation, which isn't a good developer experience. Because of this, I don't think we can have any type aliases in the v1 SDK, as that makes green field exploration clunkier; but it's something I think we can accept on the "old" v0 locations.
ast.Compiler
is an exception to the above approaches: it only has a simple factory function in thev1
SDK. Similar toast.Parser
, it depends on types and fields internal toast
. The question is if we believe its used by enough users to warrant its own proxy type, or if it can be seen as "internal" enough to do without.