Skip to content

Commit

Permalink
Merge #11150
Browse files Browse the repository at this point in the history
11150: Support future glob patterns r=iwahbe a=iwahbe

Fixes #8956

This changes how the various `--target` globs work. Instead of expanding the glob on the current snapshot, we now push the glob into the engine itself. This allows us to glob on resources not yet created.

In exchange for this feature, we give up the ability to reject globs that expand to nothing ahead of running a pulumi up. We gain a much more intuitive interface: globs work as expected, _without caveats_.

Co-authored-by: Ian Wahbe <ian@wahbe.com>
  • Loading branch information
bors[bot] and iwahbe committed Oct 25, 2022
2 parents 0f24b84 + 856bded commit 17b1a66
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 259 deletions.
@@ -0,0 +1,4 @@
changes:
- type: feat
scope: cli
description: Allow globbing for resources that do not yet exist
25 changes: 8 additions & 17 deletions pkg/cmd/pulumi/destroy.go
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/pulumi/pulumi/pkg/v3/backend"
"github.com/pulumi/pulumi/pkg/v3/backend/display"
"github.com/pulumi/pulumi/pkg/v3/engine"
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
"github.com/pulumi/pulumi/pkg/v3/resource/graph"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
Expand Down Expand Up @@ -176,27 +177,17 @@ func newDestroyCmd() *cobra.Command {
return result.FromError(fmt.Errorf("validating stack config: %w", configError))
}

targetUrns := []resource.URN{}
for _, t := range *targets {
targetUrns = append(targetUrns, snap.GlobUrn(resource.URN(t))...)
}
if len(targetUrns) == 0 && len(*targets) > 0 {
if !jsonDisplay {
fmt.Printf("There were no resources matching the wildcards provided.\n")
}
return nil
}

refreshOption, err := getRefreshOption(proj, refresh)
if err != nil {
return result.FromError(err)
}

if targets != nil && len(*targets) > 0 && excludeProtected {
if len(*targets) > 0 && excludeProtected {
return result.FromError(errors.New("You cannot specify --target and --exclude-protected"))
}

var protectedCount int
var targetUrns []string = *targets
if excludeProtected {
contract.Assert(len(targetUrns) == 0)
targetUrns, protectedCount, err = handleExcludeProtected(ctx, s)
Expand All @@ -218,7 +209,7 @@ func newDestroyCmd() *cobra.Command {
Parallel: parallel,
Debug: debug,
Refresh: refreshOption,
DestroyTargets: targetUrns,
DestroyTargets: deploy.NewUrnTargets(targetUrns),
TargetDependents: targetDependents,
UseLegacyDiff: useLegacyDiff(),
DisableProviderPreview: disableProviderPreview(),
Expand Down Expand Up @@ -375,7 +366,7 @@ func seperateProtected(resources []*resource.State) (
}

// Returns the number of protected resources that remain. Appends all unprotected resources to `targetUrns`.
func handleExcludeProtected(ctx context.Context, s backend.Stack) ([]resource.URN, int, error) {
func handleExcludeProtected(ctx context.Context, s backend.Stack) ([]string, int, error) {
// Get snapshot
snapshot, err := s.Snapshot(ctx)
if err != nil {
Expand All @@ -384,9 +375,9 @@ func handleExcludeProtected(ctx context.Context, s backend.Stack) ([]resource.UR
return nil, 0, errors.New("Failed to find the stack snapshot. Are you in a stack?")
}
unprotected, protected := seperateProtected(snapshot.Resources)
targetUrns := []resource.URN{}
for _, r := range unprotected {
targetUrns = append(targetUrns, r.URN)
targetUrns := make([]string, len(unprotected))
for i, r := range unprotected {
targetUrns[i] = string(r.URN)
}
return targetUrns, len(protected), nil
}
18 changes: 9 additions & 9 deletions pkg/cmd/pulumi/preview.go
Expand Up @@ -24,8 +24,8 @@ import (
"github.com/pulumi/pulumi/pkg/v3/backend"
"github.com/pulumi/pulumi/pkg/v3/backend/display"
"github.com/pulumi/pulumi/pkg/v3/engine"
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
Expand Down Expand Up @@ -168,19 +168,19 @@ func newPreviewCmd() *cobra.Command {
return result.FromError(fmt.Errorf("getting stack configuration: %w", err))
}

targetURNs := []resource.URN{}
targetURNs := []string{}
for _, t := range targets {
targetURNs = append(targetURNs, resource.URN(t))
targetURNs = append(targetURNs, t)
}

replaceURNs := []resource.URN{}
replaceURNs := []string{}
for _, r := range replaces {
replaceURNs = append(replaceURNs, resource.URN(r))
replaceURNs = append(replaceURNs, r)
}

for _, tr := range targetReplaces {
targetURNs = append(targetURNs, resource.URN(tr))
replaceURNs = append(replaceURNs, resource.URN(tr))
targetURNs = append(targetURNs, tr)
replaceURNs = append(replaceURNs, tr)
}

refreshOption, err := getRefreshOption(proj, refresh)
Expand All @@ -194,12 +194,12 @@ func newPreviewCmd() *cobra.Command {
Parallel: parallel,
Debug: debug,
Refresh: refreshOption,
ReplaceTargets: replaceURNs,
ReplaceTargets: deploy.NewUrnTargets(replaceURNs),
UseLegacyDiff: useLegacyDiff(),
DisableProviderPreview: disableProviderPreview(),
DisableResourceReferences: disableResourceReferences(),
DisableOutputValues: disableOutputValues(),
UpdateTargets: targetURNs,
UpdateTargets: deploy.NewUrnTargets(targetURNs),
TargetDependents: targetDependents,
// If we're trying to save a plan then we _need_ to generate it. We also turn this on in
// experimental mode to just get more testing of it.
Expand Down
6 changes: 3 additions & 3 deletions pkg/cmd/pulumi/refresh.go
Expand Up @@ -212,9 +212,9 @@ func newRefreshCmd() *cobra.Command {
}
}

targetUrns := []resource.URN{}
targetUrns := []string{}
for _, t := range *targets {
targetUrns = append(targetUrns, resource.URN(t))
targetUrns = append(targetUrns, t)
}

opts.Engine = engine.UpdateOptions{
Expand All @@ -224,7 +224,7 @@ func newRefreshCmd() *cobra.Command {
DisableProviderPreview: disableProviderPreview(),
DisableResourceReferences: disableResourceReferences(),
DisableOutputValues: disableOutputValues(),
RefreshTargets: targetUrns,
RefreshTargets: deploy.NewUrnTargets(targetUrns),
}

changes, res := s.Refresh(ctx, backend.UpdateOperation{
Expand Down
48 changes: 13 additions & 35 deletions pkg/cmd/pulumi/up.go
Expand Up @@ -29,7 +29,6 @@ import (
"github.com/pulumi/pulumi/pkg/v3/engine"
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
"github.com/pulumi/pulumi/pkg/v3/resource/stack"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
Expand Down Expand Up @@ -121,40 +120,19 @@ func newUpCmd() *cobra.Command {
return result.FromError(fmt.Errorf("validating stack config: %w", configErr))
}

targetURNs, replaceURNs := []resource.URN{}, []resource.URN{}
targetURNs, replaceURNs := []string{}, []string{}

if len(targets)+len(replaces)+len(targetReplaces) > 0 {
// The s.Snapshot call below adds needless latency as s.Update further below will call
// (*cloudBackend).getSnapshot again, presumably re-retrieving the same result over the network.
// Although s.Snapshot has a cache, it does not get hit by s.Update as of this writing. For now:
// only call s.Snapshot here if targets, replaces, or targetReplaces require it.
snap, err := s.Snapshot(ctx)
if err != nil {
return result.FromError(err)
}
for _, t := range targets {
targetURNs = append(targetURNs, snap.GlobUrn(resource.URN(t))...)
}

for _, r := range replaces {
replaceURNs = append(replaceURNs, snap.GlobUrn(resource.URN(r))...)
}
for _, t := range targets {
targetURNs = append(targetURNs, t)
}

for _, tr := range targetReplaces {
targetURNs = append(targetURNs, snap.GlobUrn(resource.URN(tr))...)
replaceURNs = append(replaceURNs, snap.GlobUrn(resource.URN(tr))...)
}
for _, r := range replaces {
replaceURNs = append(replaceURNs, r)
}

if len(targetURNs) == 0 && len(targets)+len(targetReplaces) > 0 {
// Wildcards were used, but they all evaluated to empty. We don't
// want a targeted update to turn into a general update, so we
// should abort.
if !jsonDisplay {
fmt.Printf("There were no resources matching the wildcards provided.\n")
fmt.Printf("Wildcards can only be used to target resources that already exist.\n")
}
return nil
for _, tr := range targetReplaces {
targetURNs = append(targetURNs, tr)
replaceURNs = append(replaceURNs, tr)
}

refreshOption, err := getRefreshOption(proj, refresh)
Expand All @@ -166,13 +144,13 @@ func newUpCmd() *cobra.Command {
Parallel: parallel,
Debug: debug,
Refresh: refreshOption,
RefreshTargets: targetURNs,
ReplaceTargets: replaceURNs,
RefreshTargets: deploy.NewUrnTargets(targetURNs),
ReplaceTargets: deploy.NewUrnTargets(replaceURNs),
UseLegacyDiff: useLegacyDiff(),
DisableProviderPreview: disableProviderPreview(),
DisableResourceReferences: disableResourceReferences(),
DisableOutputValues: disableOutputValues(),
UpdateTargets: targetURNs,
UpdateTargets: deploy.NewUrnTargets(targetURNs),
TargetDependents: targetDependents,
// If we're in experimental mode then we trigger a plan to be generated during the preview phase
// which will be constrained to during the update phase.
Expand Down Expand Up @@ -527,7 +505,7 @@ func newUpCmd() *cobra.Command {
" Wildcards (*, **) are also supported")
cmd.PersistentFlags().StringArrayVar(
&replaces, "replace", []string{},
"Specify resources to replace. Multiple resources can be specified using --replace urn1 --replace urn2."+
"Specify a single resource URN to replace. Multiple resources can be specified using --replace urn1 --replace urn2."+
" Wildcards (*, **) are also supported")
cmd.PersistentFlags().StringArrayVar(
&targetReplaces, "target-replace", []string{},
Expand Down
6 changes: 3 additions & 3 deletions pkg/engine/lifecycletest/refresh_test.go
Expand Up @@ -336,7 +336,7 @@ func validateRefreshDeleteCombination(t *testing.T, names []string, targets []st
refreshTargets = append(refreshTargets, pickURN(t, urns, names, target))
}

p.Options.RefreshTargets = refreshTargets
p.Options.RefreshTargets = deploy.NewUrnTargetsFromUrns(refreshTargets)

newResource := func(urn resource.URN, id resource.ID, delete bool, dependencies ...resource.URN) *resource.State {
return &resource.State{
Expand Down Expand Up @@ -496,10 +496,10 @@ func validateRefreshBasicsCombination(t *testing.T, names []string, targets []st
refreshTargets := []resource.URN{}

for _, target := range targets {
refreshTargets = append(p.Options.RefreshTargets, pickURN(t, urns, names, target))
refreshTargets = append(p.Options.RefreshTargets.Literals(), pickURN(t, urns, names, target))
}

p.Options.RefreshTargets = refreshTargets
p.Options.RefreshTargets = deploy.NewUrnTargetsFromUrns(refreshTargets)

newResource := func(urn resource.URN, id resource.ID, delete bool, dependencies ...resource.URN) *resource.State {
return &resource.State{
Expand Down

0 comments on commit 17b1a66

Please sign in to comment.