Skip to content
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

provider rotation #432

Merged
merged 30 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8c4a065
initial rotate verb
nyobe Jan 15, 2025
4010ab8
example
nyobe Jan 15, 2025
a1f4ec9
fn::rotate
nyobe Jan 15, 2025
4fa0993
rotate scoped to specific paths
nyobe Jan 15, 2025
4e22ea2
move patch handling to eval pkg
nyobe Jan 15, 2025
65b0277
separate rotator inputs and state
nyobe Jan 16, 2025
54d9977
need to add RotatorLoaders to Check and Open too
nyobe Jan 16, 2025
889c6f0
license heading
nyobe Jan 16, 2025
e183ea7
fix rotator.Open not getting called when not rotating
nyobe Jan 16, 2025
d17fcda
fix rotator loaders need to be present for opening imports
nyobe Jan 17, 2025
4769a69
initial snapshot test
nyobe Jan 17, 2025
14d609a
test valueToSecretJSON
nyobe Jan 18, 2025
6042f21
lint
nyobe Jan 18, 2025
5910dc7
changelog
nyobe Jan 21, 2025
1922995
re-align rotator and provider interfaces?
nyobe Jan 21, 2025
9648bb6
add rotating flag instead of overloading rotatePaths
nyobe Jan 21, 2025
e3f06d7
fix evaluating state input against correct schema
nyobe Jan 21, 2025
a208a03
drop support for rotating individual paths for now
nyobe Jan 21, 2025
740e999
merge provider and rotator interfaces, pass state param to open
nyobe Jan 22, 2025
5048c2f
Revert "merge provider and rotator interfaces, pass state param to open"
nyobe Jan 22, 2025
dc86285
split provider and rotator interfaces, extend ProviderLoader to load …
nyobe Jan 22, 2025
1a386a8
pass inputs to Rotator.Open
nyobe Jan 22, 2025
53ccc3c
rename fn::rotate provider -> rotator
nyobe Jan 22, 2025
befdf5f
rename ApplyPatches -> ApplyValuePatches
nyobe Jan 22, 2025
53dc66c
do not intermix state and inputs for shortform fn::rotate
nyobe Jan 22, 2025
58732fa
Update eval/eval.go
nyobe Jan 22, 2025
1b0e0dd
Rotator.Rotate should receive execution context
nyobe Jan 22, 2025
38343da
Update eval/eval.go
nyobe Jan 23, 2025
b3fd85d
Revert "rename fn::rotate provider -> rotator"
nyobe Jan 23, 2025
d5f036c
clarify docstring
nyobe Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

- Fix diagnostic messages when updating environment with invalid definition
[#422](https://github.com/pulumi/esc/pull/422)
- Introduce support for rotating static credentials via `fn::rotate` providers [432](https://github.com/pulumi/esc/pull/432)

### Bug Fixes

### Breaking changes

4 changes: 4 additions & 0 deletions analysis/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ func (testProviders) LoadProvider(ctx context.Context, name string) (esc.Provide
return nil, fmt.Errorf("unknown provider %q", name)
}

func (testProviders) LoadRotator(ctx context.Context, name string) (esc.Rotator, error) {
return nil, fmt.Errorf("unknown rotator %q", name)
}

type testEnvironments struct{}

func (testEnvironments) LoadEnvironment(ctx context.Context, name string) ([]byte, eval.Decrypter, error) {
Expand Down
121 changes: 121 additions & 0 deletions ast/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,41 @@ func Open(provider string, inputs *ObjectExpr) *OpenExpr {
}
}

// RotateExpr is a type of OpenExpr that supports a rotate operation.
type RotateExpr struct {
builtinNode

Provider *StringExpr
Inputs Expr
State *ObjectExpr
}

func RotateSyntax(node *syntax.ObjectNode, name *StringExpr, args Expr, provider *StringExpr, inputs Expr, state *ObjectExpr) *RotateExpr {
return &RotateExpr{
builtinNode: builtin(node, name, args),
Provider: provider,
Inputs: inputs,
State: state,
}
}

func Rotate(provider string, inputs, state *ObjectExpr) *RotateExpr {
name, providerX := String("fn::rotate"), String(provider)

entries := []ObjectProperty{
{Key: String("provider"), Value: providerX},
nyobe marked this conversation as resolved.
Show resolved Hide resolved
{Key: String("inputs"), Value: inputs},
{Key: String("state"), Value: state},
}

return &RotateExpr{
builtinNode: builtin(nil, name, Object(entries...)),
Provider: providerX,
Inputs: inputs,
State: state,
}
}

// ToJSON returns the underlying structure as a json string.
type ToJSONExpr struct {
builtinNode
Expand Down Expand Up @@ -607,6 +642,8 @@ func tryParseFunction(node *syntax.ObjectNode) (Expr, syntax.Diagnostics, bool)
parse = parseJoin
case "fn::open":
parse = parseOpen
case "fn::rotate":
parse = parseRotate
case "fn::secret":
parse = parseSecret
case "fn::toBase64":
Expand All @@ -620,6 +657,10 @@ func tryParseFunction(node *syntax.ObjectNode) (Expr, syntax.Diagnostics, bool)
parse = parseShortOpen
break
}
if strings.HasPrefix(kvp.Key.Value(), "fn::rotate::") {
parse = parseShortRotate
break
}

if strings.HasPrefix(strings.ToLower(kvp.Key.Value()), "fn::") {
diags = append(diags, syntax.Error(kvp.Key.Syntax().Range(),
Expand Down Expand Up @@ -696,6 +737,86 @@ func parseShortOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr,
return OpenSyntax(node, name, args, provider, args), nil
}

func parseRotate(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) {
obj, ok := args.(*ObjectExpr)
if !ok {
diags := syntax.Diagnostics{ExprError(args, "the argument to fn::rotate must be an object containing 'provider', 'inputs' and 'state'")}
return RotateSyntax(node, name, args, nil, nil, nil), diags
}

var providerExpr, inputs, stateExpr Expr
var diags syntax.Diagnostics

for i := 0; i < len(obj.Entries); i++ {
kvp := obj.Entries[i]
key := kvp.Key
switch key.GetValue() {
case "provider":
providerExpr = kvp.Value
case "inputs":
inputs = kvp.Value
case "state":
stateExpr = kvp.Value
}
}

provider, ok := providerExpr.(*StringExpr)
if !ok {
if providerExpr == nil {
diags.Extend(ExprError(obj, "missing provider name ('provider')"))
} else {
diags.Extend(ExprError(providerExpr, "provider name must be a string literal"))
}
}

if inputs == nil {
diags.Extend(ExprError(obj, "missing provider inputs ('inputs')"))
}

state, ok := stateExpr.(*ObjectExpr)
if !ok && state != nil {
nyobe marked this conversation as resolved.
Show resolved Hide resolved
diags.Extend(ExprError(stateExpr, "rotation state must be an object literal"))
}

return RotateSyntax(node, name, obj, provider, inputs, state), diags
}

func parseShortRotate(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) {
kvp := node.Index(0)
provider := StringSyntaxValue(name.Syntax().(*syntax.StringNode), strings.TrimPrefix(kvp.Key.Value(), "fn::rotate::"))
nyobe marked this conversation as resolved.
Show resolved Hide resolved

obj, ok := args.(*ObjectExpr)
if !ok {
diags := syntax.Diagnostics{ExprError(args, "the argument to fn::rotate must be an object containing 'inputs' and 'state'")}
return RotateSyntax(node, name, args, nil, nil, nil), diags
}

var inputs, stateExpr Expr
var diags syntax.Diagnostics

for i := 0; i < len(obj.Entries); i++ {
kvp := obj.Entries[i]
key := kvp.Key
switch key.GetValue() {
case "inputs":
inputs = kvp.Value
case "state":
stateExpr = kvp.Value
}
}

if inputs == nil {
diags.Extend(ExprError(obj, "missing provider inputs ('inputs')"))
}

state, ok := stateExpr.(*ObjectExpr)
if !ok && state != nil {
diags.Extend(ExprError(stateExpr, "rotation state must be an object literal"))
}

return RotateSyntax(node, name, args, provider, inputs, state), nil
}

func parseJoin(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) {
list, ok := args.(*ArrayExpr)
if !ok || len(list.Elements) != 2 {
Expand Down
4 changes: 4 additions & 0 deletions cmd/esc/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ func (testProviders) LoadProvider(ctx context.Context, name string) (esc.Provide
return nil, fmt.Errorf("unknown provider %q", name)
}

func (testProviders) LoadRotator(ctx context.Context, name string) (esc.Rotator, error) {
return nil, fmt.Errorf("unknown rotator %q", name)
}

type rot128 struct{}

func (rot128) Encrypt(_ context.Context, plaintext []byte) ([]byte, error) {
Expand Down
Loading
Loading