-
Notifications
You must be signed in to change notification settings - Fork 9
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
Add rotate command #433
Merged
Merged
Add rotate command #433
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,7 +141,7 @@ type Client interface { | |
// DeleteEnvironment deletes the environment envName in org orgName. | ||
DeleteEnvironment(ctx context.Context, orgName, projectName, envName string) error | ||
|
||
// OpenEnvironment evaluates the environment envName in org orgName and returns the ID of the opened | ||
// OpenEnvironment evaluates the environment projectName/envName in org orgName and returns the ID of the opened | ||
// environment. The opened environment will be available for the indicated duration, after which it | ||
// will expire. | ||
// | ||
|
@@ -155,6 +155,20 @@ type Client interface { | |
duration time.Duration, | ||
) (string, []EnvironmentDiagnostic, error) | ||
|
||
// RotateEnvironment will rotate credentials in an environment. | ||
// It also evaluates the environment projectName/envName in org orgName and returns the ID of the opened | ||
// environment. The opened environment will be available for the indicated duration, after which it | ||
// will expire. | ||
// | ||
// If the environment contains errors, the open will fail with diagnostics. | ||
RotateEnvironment( | ||
ctx context.Context, | ||
orgName string, | ||
projectName string, | ||
envName string, | ||
duration time.Duration, | ||
) (string, []EnvironmentDiagnostic, error) | ||
|
||
// CheckYAMLEnvironment checks the given environment YAML for errors within the context of org orgName. | ||
// | ||
// This call returns the checked environment's AST, values, schema, and any diagnostics issued by the | ||
|
@@ -616,6 +630,37 @@ func (pc *client) OpenEnvironment( | |
return resp.ID, nil, nil | ||
} | ||
|
||
func (pc *client) RotateEnvironment( | ||
ctx context.Context, | ||
orgName string, | ||
projectName string, | ||
envName string, | ||
duration time.Duration, | ||
) (string, []EnvironmentDiagnostic, error) { | ||
path := fmt.Sprintf("/api/esc/environments/%v/%v/%v/rotate", orgName, projectName, envName) | ||
|
||
queryObj := struct { | ||
Duration string `url:"duration"` | ||
}{ | ||
Duration: duration.String(), | ||
} | ||
var resp struct { | ||
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. Nit: could maybe move these queryObj and resp structs out to reuse between functions, not sure how many times these are used |
||
ID string `json:"id"` | ||
} | ||
var errResp EnvironmentErrorResponse | ||
err := pc.restCallWithOptions(ctx, http.MethodPost, path, queryObj, nil, &resp, httpCallOptions{ | ||
ErrorResponse: &errResp, | ||
}) | ||
if err != nil { | ||
var diags *EnvironmentErrorResponse | ||
if errors.As(err, &diags) && diags.Code == http.StatusBadRequest && len(diags.Diagnostics) != 0 { | ||
return "", diags.Diagnostics, nil | ||
} | ||
return "", nil, err | ||
} | ||
return resp.ID, nil, nil | ||
} | ||
|
||
func (pc *client) CheckYAMLEnvironment( | ||
ctx context.Context, | ||
orgName string, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2025, Pulumi Corporation. | ||
|
||
package cli | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/pulumi/esc" | ||
"github.com/pulumi/esc/cmd/esc/cli/client" | ||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func newEnvRotateCmd(envcmd *envCommand) *cobra.Command { | ||
var duration time.Duration | ||
var format string | ||
|
||
cmd := &cobra.Command{ | ||
Use: "rotate [<org-name>/][<project-name>/]<environment-name>", | ||
Short: "Rotate secrets and open the environment", | ||
Long: "Rotate secrets and open the environment\n" + | ||
"\n" + | ||
"This command opens the environment with the given name. The result is written to\n" + | ||
"stdout as JSON.\n", | ||
SilenceUsage: true, | ||
Hidden: true, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctx := context.Background() | ||
|
||
if err := envcmd.esc.getCachedClient(ctx); err != nil { | ||
return err | ||
} | ||
|
||
ref, _, err := envcmd.getExistingEnvRef(ctx, args) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if ref.version != "" { | ||
return fmt.Errorf("the rotate command does not accept environments at specific versions") | ||
} | ||
|
||
switch format { | ||
case "detailed", "json", "yaml", "string", "dotenv", "shell": | ||
// OK | ||
default: | ||
return fmt.Errorf("unknown output format %q", format) | ||
} | ||
|
||
env, diags, err := envcmd.rotateEnvironment(ctx, ref, duration) | ||
if err != nil { | ||
return err | ||
} | ||
if len(diags) != 0 { | ||
return envcmd.writePropertyEnvironmentDiagnostics(envcmd.esc.stderr, diags) | ||
} | ||
|
||
return envcmd.renderValue(envcmd.esc.stdout, env, resource.PropertyPath{}, format, false, true) | ||
}, | ||
} | ||
|
||
cmd.Flags().DurationVarP( | ||
&duration, "lifetime", "l", 2*time.Hour, | ||
"the lifetime of the opened environment in the form HhMm (e.g. 2h, 1h30m, 15m)") | ||
cmd.Flags().StringVarP( | ||
&format, "format", "f", "json", | ||
"the output format to use. May be 'dotenv', 'json', 'yaml', 'detailed', or 'shell'") | ||
|
||
return cmd | ||
} | ||
|
||
func (env *envCommand) rotateEnvironment( | ||
ctx context.Context, | ||
ref environmentRef, | ||
duration time.Duration, | ||
) (*esc.Environment, []client.EnvironmentDiagnostic, error) { | ||
envID, diags, err := env.esc.client.RotateEnvironment(ctx, ref.orgName, ref.projectName, ref.envName, duration) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
if len(diags) != 0 { | ||
return nil, diags, err | ||
} | ||
open, err := env.esc.client.GetOpenEnvironmentWithProject(ctx, ref.orgName, ref.projectName, ref.envName, envID) | ||
return open, nil, err | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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, not sure I understand why RotateEnvironment calls open? Is this a typo?
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 for the test client for tests. I don't know exactly how it should do a fake "rotate" so I'm assuming a no-op and calling open (since rotating calls open)