[cli] Add the ability to control auto-refresh of stacks by Pulumi.yaml (#8071)
Co-authored-by: Komal Ali <komal@pulumi.com>
This commit is contained in:
parent
4dc9c4bf39
commit
09a8cc7079
|
@ -3,6 +3,10 @@
|
|||
- [cli] - Differentiate in-progress actions by bolding output.
|
||||
[#7918](https://github.com/pulumi/pulumi/pull/7918)
|
||||
|
||||
- [CLI] Adding the ability to set `refresh: always` in an options object at a Pulumi.yaml level
|
||||
to allow a user to be able to always refresh their derivative stacks by default
|
||||
[#8071](https://github.com/pulumi/pulumi/pull/8071)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [codegen/go] - Fix generation of cyclic struct types.
|
||||
|
|
|
@ -41,7 +41,7 @@ func newDestroyCmd() *cobra.Command {
|
|||
var diffDisplay bool
|
||||
var eventLogPath string
|
||||
var parallel int
|
||||
var refresh bool
|
||||
var refresh string
|
||||
var showConfig bool
|
||||
var showReplacementSteps bool
|
||||
var showSames bool
|
||||
|
@ -143,10 +143,14 @@ func newDestroyCmd() *cobra.Command {
|
|||
targetUrns = append(targetUrns, resource.URN(t))
|
||||
}
|
||||
|
||||
refreshOption, err := getRefreshOption(proj, refresh)
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
Refresh: refresh,
|
||||
Refresh: refreshOption,
|
||||
DestroyTargets: targetUrns,
|
||||
TargetDependents: targetDependents,
|
||||
UseLegacyDiff: useLegacyDiff(),
|
||||
|
@ -204,9 +208,10 @@ func newDestroyCmd() *cobra.Command {
|
|||
cmd.PersistentFlags().IntVarP(
|
||||
¶llel, "parallel", "p", defaultParallel,
|
||||
"Allow P resource operations to run in parallel at once (1 for no parallelism). Defaults to unbounded.")
|
||||
cmd.PersistentFlags().BoolVarP(
|
||||
&refresh, "refresh", "r", false,
|
||||
cmd.PersistentFlags().StringVarP(
|
||||
&refresh, "refresh", "r", "",
|
||||
"Refresh the state of the stack's resources before this update")
|
||||
cmd.PersistentFlags().Lookup("refresh").NoOptDefVal = "true"
|
||||
cmd.PersistentFlags().BoolVar(
|
||||
&showConfig, "show-config", false,
|
||||
"Show configuration keys and variables")
|
||||
|
|
|
@ -44,7 +44,7 @@ func newPreviewCmd() *cobra.Command {
|
|||
var diffDisplay bool
|
||||
var eventLogPath string
|
||||
var parallel int
|
||||
var refresh bool
|
||||
var refresh string
|
||||
var showConfig bool
|
||||
var showReplacementSteps bool
|
||||
var showSames bool
|
||||
|
@ -160,12 +160,17 @@ func newPreviewCmd() *cobra.Command {
|
|||
replaceURNs = append(replaceURNs, resource.URN(tr))
|
||||
}
|
||||
|
||||
refreshOption, err := getRefreshOption(proj, refresh)
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
|
||||
opts := backend.UpdateOptions{
|
||||
Engine: engine.UpdateOptions{
|
||||
LocalPolicyPacks: engine.MakeLocalPolicyPacks(policyPackPaths, policyPackConfigPaths),
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
Refresh: refresh,
|
||||
Refresh: refreshOption,
|
||||
ReplaceTargets: replaceURNs,
|
||||
UseLegacyDiff: useLegacyDiff(),
|
||||
DisableProviderPreview: disableProviderPreview(),
|
||||
|
@ -256,9 +261,10 @@ func newPreviewCmd() *cobra.Command {
|
|||
cmd.PersistentFlags().IntVarP(
|
||||
¶llel, "parallel", "p", defaultParallel,
|
||||
"Allow P resource operations to run in parallel at once (1 for no parallelism). Defaults to unbounded.")
|
||||
cmd.PersistentFlags().BoolVarP(
|
||||
&refresh, "refresh", "r", false,
|
||||
cmd.PersistentFlags().StringVarP(
|
||||
&refresh, "refresh", "r", "",
|
||||
"Refresh the state of the stack's resources before this update")
|
||||
cmd.PersistentFlags().Lookup("refresh").NoOptDefVal = "true"
|
||||
cmd.PersistentFlags().BoolVar(
|
||||
&showConfig, "show-config", false,
|
||||
"Show configuration keys and variables")
|
||||
|
|
|
@ -60,7 +60,7 @@ func newUpCmd() *cobra.Command {
|
|||
var diffDisplay bool
|
||||
var eventLogPath string
|
||||
var parallel int
|
||||
var refresh bool
|
||||
var refresh string
|
||||
var showConfig bool
|
||||
var showReplacementSteps bool
|
||||
var showSames bool
|
||||
|
@ -122,11 +122,15 @@ func newUpCmd() *cobra.Command {
|
|||
replaceURNs = append(replaceURNs, resource.URN(tr))
|
||||
}
|
||||
|
||||
refreshOption, err := getRefreshOption(proj, refresh)
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
LocalPolicyPacks: engine.MakeLocalPolicyPacks(policyPackPaths, policyPackConfigPaths),
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
Refresh: refresh,
|
||||
Refresh: refreshOption,
|
||||
RefreshTargets: targetURNs,
|
||||
ReplaceTargets: replaceURNs,
|
||||
UseLegacyDiff: useLegacyDiff(),
|
||||
|
@ -287,11 +291,16 @@ func newUpCmd() *cobra.Command {
|
|||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
refreshOption, err := getRefreshOption(proj, refresh)
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
LocalPolicyPacks: engine.MakeLocalPolicyPacks(policyPackPaths, policyPackConfigPaths),
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
Refresh: refresh,
|
||||
Refresh: refreshOption,
|
||||
}
|
||||
|
||||
// TODO for the URL case:
|
||||
|
@ -458,9 +467,10 @@ func newUpCmd() *cobra.Command {
|
|||
cmd.PersistentFlags().IntVarP(
|
||||
¶llel, "parallel", "p", defaultParallel,
|
||||
"Allow P resource operations to run in parallel at once (1 for no parallelism). Defaults to unbounded.")
|
||||
cmd.PersistentFlags().BoolVarP(
|
||||
&refresh, "refresh", "r", false,
|
||||
cmd.PersistentFlags().StringVarP(
|
||||
&refresh, "refresh", "r", "",
|
||||
"Refresh the state of the stack's resources before this update")
|
||||
cmd.PersistentFlags().Lookup("refresh").NoOptDefVal = "true"
|
||||
cmd.PersistentFlags().BoolVar(
|
||||
&showConfig, "show-config", false,
|
||||
"Show configuration keys and variables")
|
||||
|
|
|
@ -839,3 +839,26 @@ func checkDeploymentVersionError(err error, stackName string) error {
|
|||
}
|
||||
return errors.Wrap(err, "could not deserialize deployment")
|
||||
}
|
||||
|
||||
func getRefreshOption(proj *workspace.Project, refresh string) (bool, error) {
|
||||
// we want to check for an explicit --refresh or a --refresh=true or --refresh=false
|
||||
// refresh is assigned the empty string by default to distinguish the difference between
|
||||
// when the user actually interacted with the cli argument (`NoOptDefVal`)
|
||||
// and the default functionality today
|
||||
if refresh != "" {
|
||||
refreshDetails, boolErr := strconv.ParseBool(refresh)
|
||||
if boolErr != nil {
|
||||
// the user has passed a --refresh but with a random value that we don't support
|
||||
return false, errors.New("unable to determine value for --refresh")
|
||||
}
|
||||
return refreshDetails, nil
|
||||
}
|
||||
|
||||
// the user has not specifically passed an argument on the cli to refresh but has set a Project option to refresh
|
||||
if proj.Options != nil && proj.Options.Refresh == "always" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// the default functionality right now is to always skip a refresh
|
||||
return false, nil
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v3/backend"
|
||||
pul_testing "github.com/pulumi/pulumi/sdk/v3/go/common/testing"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/gitutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
||||
)
|
||||
|
||||
// assertEnvValue assert the update metadata's Environment map contains the given value.
|
||||
|
@ -264,3 +266,66 @@ func Test_makeJSONString(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRefreshOption(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
refresh string
|
||||
project workspace.Project
|
||||
expectedRefreshState bool
|
||||
}{
|
||||
{
|
||||
"No options specified means no refresh",
|
||||
"",
|
||||
workspace.Project{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Passing --refresh=true causes a refresh",
|
||||
"true",
|
||||
workspace.Project{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Passing --refresh=false causes no refresh",
|
||||
"false",
|
||||
workspace.Project{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Setting Refresh at a project level via Pulumi.yaml and no CLI args",
|
||||
"",
|
||||
workspace.Project{
|
||||
Name: "auto-refresh",
|
||||
Runtime: workspace.ProjectRuntimeInfo{},
|
||||
Options: &workspace.ProjectOptions{
|
||||
Refresh: "always",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Setting Refresh at a project level via Pulumi.yaml and --refresh=false",
|
||||
"false",
|
||||
workspace.Project{
|
||||
Name: "auto-refresh",
|
||||
Runtime: workspace.ProjectRuntimeInfo{},
|
||||
Options: &workspace.ProjectOptions{
|
||||
Refresh: "always",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
shouldRefresh, err := getRefreshOption(&tt.project, tt.refresh)
|
||||
if err != nil {
|
||||
t.Errorf("getRefreshOption() error = %v", err)
|
||||
}
|
||||
if shouldRefresh != tt.expectedRefreshState {
|
||||
t.Errorf("getRefreshOption got = %t, expected %t", shouldRefresh, tt.expectedRefreshState)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,11 @@ type ProjectBackend struct {
|
|||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
}
|
||||
|
||||
type ProjectOptions struct {
|
||||
// Refresh is the ability to always run a refresh as part of a pulumi update / preview / destroy
|
||||
Refresh string `json:"refresh,omitempty" yaml:"refresh,omitempty"`
|
||||
}
|
||||
|
||||
// Project is a Pulumi project manifest.
|
||||
//
|
||||
// We explicitly add yaml tags (instead of using the default behavior from https://github.com/ghodss/yaml which works
|
||||
|
@ -90,6 +95,9 @@ type Project struct {
|
|||
|
||||
// Backend is an optional backend configuration
|
||||
Backend *ProjectBackend `json:"backend,omitempty" yaml:"backend,omitempty"`
|
||||
|
||||
// Options is an optional set of project options
|
||||
Options *ProjectOptions `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
func (proj *Project) Validate() error {
|
||||
|
|
Loading…
Reference in a new issue