Compare commits
80 commits
master
...
fraser/pla
Author | SHA1 | Date | |
---|---|---|---|
|
36bf0a43dd | ||
|
19dc1bf865 | ||
|
3bad3c4abf | ||
|
f0ad8cffc7 | ||
|
69d81638f9 | ||
|
605bc2ecdf | ||
|
884d460c39 | ||
|
429c93def9 | ||
|
a88200c70d | ||
|
146680abda | ||
|
6c337ad7f9 | ||
|
9242b835f4 | ||
|
f9bda75d38 | ||
|
1cf5e7b763 | ||
|
50232e26ab | ||
|
dfef30f5a6 | ||
|
bd7d9b797b | ||
|
8f959a03b0 | ||
|
94bf0840df | ||
|
37b22b8d3c | ||
|
6022b98134 | ||
|
202c0f0ae4 | ||
|
76f3e938aa | ||
|
b692140160 | ||
|
5f57c2ab72 | ||
|
ad6047cd08 | ||
|
b7a5310e07 | ||
|
e182cf0ff3 | ||
|
1ddd452faa | ||
|
7143b728b7 | ||
|
6d02c4adc8 | ||
|
b3d8cd9870 | ||
|
9032552d69 | ||
|
c1047fb3d9 | ||
|
9739ff0eed | ||
|
4fa395b03d | ||
|
598f2c7213 | ||
|
96af4684db | ||
|
844e8f0c1e | ||
|
20eaa27b3d | ||
|
de3e95b7ab | ||
|
23e9ec2c54 | ||
|
3b9cf5b648 | ||
|
40400b664b | ||
|
878c2bb28a | ||
|
83e655e19f | ||
|
b1a64a65ca | ||
|
5b6a03f548 | ||
|
e2c5f12d65 | ||
|
feaccb1fe1 | ||
|
9865fa58e3 | ||
|
c41981a84f | ||
|
1db1ab5137 | ||
|
82222cc19e | ||
|
775ace8f13 | ||
|
12c0188329 | ||
|
3e73c036fb | ||
|
f4c28053f8 | ||
|
d995901fa7 | ||
|
505e4c5d06 | ||
|
5352638e51 | ||
|
fdea5bb588 | ||
|
b5eb955301 | ||
|
b1e23a5287 | ||
|
83e2cd7e4a | ||
|
8fae881a81 | ||
|
1b59952a20 | ||
|
155c035e34 | ||
|
0d9043f6a2 | ||
|
90b75141cb | ||
|
365bfd2ef8 | ||
|
954bdc025b | ||
|
24a0eb0274 | ||
|
908c3aeb70 | ||
|
6de090c540 | ||
|
2818c9888d | ||
|
bbfd8256de | ||
|
13f8d342ce | ||
|
e101f80839 | ||
|
33e8980cee |
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"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/apitype"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
||||
|
@ -44,7 +45,7 @@ type ApplierOptions struct {
|
|||
|
||||
// Applier applies the changes specified by this update operation against the target stack.
|
||||
type Applier func(ctx context.Context, kind apitype.UpdateKind, stack Stack, op UpdateOperation,
|
||||
opts ApplierOptions, events chan<- engine.Event) (engine.ResourceChanges, result.Result)
|
||||
opts ApplierOptions, events chan<- engine.Event) (*deploy.Plan, engine.ResourceChanges, result.Result)
|
||||
|
||||
func ActionLabel(kind apitype.UpdateKind, dryRun bool) string {
|
||||
v := updateTextMap[kind]
|
||||
|
@ -79,7 +80,7 @@ const (
|
|||
)
|
||||
|
||||
func PreviewThenPrompt(ctx context.Context, kind apitype.UpdateKind, stack Stack,
|
||||
op UpdateOperation, apply Applier) (engine.ResourceChanges, result.Result) {
|
||||
op UpdateOperation, apply Applier) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
// create a channel to hear about the update events from the engine. this will be used so that
|
||||
// we can build up the diff display in case the user asks to see the details of the diff
|
||||
|
||||
|
@ -111,22 +112,22 @@ func PreviewThenPrompt(ctx context.Context, kind apitype.UpdateKind, stack Stack
|
|||
ShowLink: true,
|
||||
}
|
||||
|
||||
changes, res := apply(ctx, kind, stack, op, opts, eventsChannel)
|
||||
plan, changes, res := apply(ctx, kind, stack, op, opts, eventsChannel)
|
||||
if res != nil {
|
||||
close(eventsChannel)
|
||||
return changes, res
|
||||
return plan, changes, res
|
||||
}
|
||||
|
||||
// If there are no changes, or we're auto-approving or just previewing, we can skip the confirmation prompt.
|
||||
if op.Opts.AutoApprove || kind == apitype.PreviewUpdate {
|
||||
close(eventsChannel)
|
||||
return changes, nil
|
||||
return plan, changes, nil
|
||||
}
|
||||
|
||||
// Otherwise, ensure the user wants to proceed.
|
||||
res = confirmBeforeUpdating(kind, stack, events, op.Opts)
|
||||
close(eventsChannel)
|
||||
return changes, res
|
||||
return plan, changes, res
|
||||
}
|
||||
|
||||
// confirmBeforeUpdating asks the user whether to proceed. A nil error means yes.
|
||||
|
@ -196,10 +197,13 @@ func PreviewThenPromptThenExecute(ctx context.Context, kind apitype.UpdateKind,
|
|||
// Preview the operation to the user and ask them if they want to proceed.
|
||||
|
||||
if !op.Opts.SkipPreview {
|
||||
changes, res := PreviewThenPrompt(ctx, kind, stack, op, apply)
|
||||
plan, changes, res := PreviewThenPrompt(ctx, kind, stack, op, apply)
|
||||
if res != nil || kind == apitype.PreviewUpdate {
|
||||
return changes, res
|
||||
}
|
||||
|
||||
// TODO(pdg-plan): should this check for an existing plan?
|
||||
op.Opts.Engine.Plan = plan
|
||||
}
|
||||
|
||||
// Perform the change (!DryRun) and show the cloud link to the result.
|
||||
|
@ -208,7 +212,8 @@ func PreviewThenPromptThenExecute(ctx context.Context, kind apitype.UpdateKind,
|
|||
DryRun: false,
|
||||
ShowLink: true,
|
||||
}
|
||||
return apply(ctx, kind, stack, op, opts, nil /*events*/)
|
||||
_, changes, res := apply(ctx, kind, stack, op, opts, nil /*events*/)
|
||||
return changes, res
|
||||
}
|
||||
|
||||
func createDiff(updateKind apitype.UpdateKind, events []engine.Event, displayOpts display.Options) string {
|
||||
|
|
|
@ -161,7 +161,7 @@ type Backend interface {
|
|||
RenameStack(ctx context.Context, stack Stack, newName tokens.QName) (StackReference, error)
|
||||
|
||||
// Preview shows what would be updated given the current workspace's contents.
|
||||
Preview(ctx context.Context, stack Stack, op UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
Preview(ctx context.Context, stack Stack, op UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result)
|
||||
// Update updates the target stack with the current workspace's contents (config and code).
|
||||
Update(ctx context.Context, stack Stack, op UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
// Import imports resources into a stack.
|
||||
|
|
|
@ -461,12 +461,12 @@ func (b *localBackend) PackPolicies(
|
|||
}
|
||||
|
||||
func (b *localBackend) Preview(ctx context.Context, stack backend.Stack,
|
||||
op backend.UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
op backend.UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
if cmdutil.IsTruthy(os.Getenv(PulumiFilestateLockingEnvVar)) {
|
||||
err := b.Lock(ctx, stack.Ref())
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer b.Unlock(ctx, stack.Ref())
|
||||
}
|
||||
|
@ -548,7 +548,7 @@ func (b *localBackend) Watch(ctx context.Context, stack backend.Stack,
|
|||
func (b *localBackend) apply(
|
||||
ctx context.Context, kind apitype.UpdateKind, stack backend.Stack,
|
||||
op backend.UpdateOperation, opts backend.ApplierOptions,
|
||||
events chan<- engine.Event) (engine.ResourceChanges, result.Result) {
|
||||
events chan<- engine.Event) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
stackRef := stack.Ref()
|
||||
stackName := stackRef.Name()
|
||||
|
@ -563,7 +563,7 @@ func (b *localBackend) apply(
|
|||
// Start the update.
|
||||
update, err := b.newUpdate(stackName, op)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
|
||||
// Spawn a display loop to show events on the CLI.
|
||||
|
@ -604,19 +604,20 @@ func (b *localBackend) apply(
|
|||
|
||||
// Perform the update
|
||||
start := time.Now().Unix()
|
||||
var plan *deploy.Plan
|
||||
var changes engine.ResourceChanges
|
||||
var updateRes result.Result
|
||||
switch kind {
|
||||
case apitype.PreviewUpdate:
|
||||
changes, updateRes = engine.Update(update, engineCtx, op.Opts.Engine, true)
|
||||
plan, changes, updateRes = engine.Update(update, engineCtx, op.Opts.Engine, true)
|
||||
case apitype.UpdateUpdate:
|
||||
changes, updateRes = engine.Update(update, engineCtx, op.Opts.Engine, opts.DryRun)
|
||||
_, changes, updateRes = engine.Update(update, engineCtx, op.Opts.Engine, opts.DryRun)
|
||||
case apitype.ResourceImportUpdate:
|
||||
changes, updateRes = engine.Import(update, engineCtx, op.Opts.Engine, op.Imports, opts.DryRun)
|
||||
_, changes, updateRes = engine.Import(update, engineCtx, op.Opts.Engine, op.Imports, opts.DryRun)
|
||||
case apitype.RefreshUpdate:
|
||||
changes, updateRes = engine.Refresh(update, engineCtx, op.Opts.Engine, opts.DryRun)
|
||||
_, changes, updateRes = engine.Refresh(update, engineCtx, op.Opts.Engine, opts.DryRun)
|
||||
case apitype.DestroyUpdate:
|
||||
changes, updateRes = engine.Destroy(update, engineCtx, op.Opts.Engine, opts.DryRun)
|
||||
_, changes, updateRes = engine.Destroy(update, engineCtx, op.Opts.Engine, opts.DryRun)
|
||||
default:
|
||||
contract.Failf("Unrecognized update kind: %s", kind)
|
||||
}
|
||||
|
@ -660,16 +661,16 @@ func (b *localBackend) apply(
|
|||
|
||||
if updateRes != nil {
|
||||
// We swallow saveErr and backupErr as they are less important than the updateErr.
|
||||
return changes, updateRes
|
||||
return plan, changes, updateRes
|
||||
}
|
||||
|
||||
if saveErr != nil {
|
||||
// We swallow backupErr as it is less important than the saveErr.
|
||||
return changes, result.FromError(fmt.Errorf("saving update info: %w", saveErr))
|
||||
return plan, changes, result.FromError(fmt.Errorf("saving update info: %w", saveErr))
|
||||
}
|
||||
|
||||
if backupErr != nil {
|
||||
return changes, result.FromError(fmt.Errorf("saving backup: %w", backupErr))
|
||||
return plan, changes, result.FromError(fmt.Errorf("saving backup: %w", backupErr))
|
||||
}
|
||||
|
||||
// Make sure to print a link to the stack's checkpoint before exiting.
|
||||
|
@ -705,7 +706,7 @@ func (b *localBackend) apply(
|
|||
}
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
return plan, changes, nil
|
||||
}
|
||||
|
||||
// query executes a query program against the resource outputs of a locally hosted stack.
|
||||
|
|
|
@ -64,7 +64,10 @@ func (s *localStack) Rename(ctx context.Context, newName tokens.QName) (backend.
|
|||
return backend.RenameStack(ctx, s, newName)
|
||||
}
|
||||
|
||||
func (s *localStack) Preview(ctx context.Context, op backend.UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
func (s *localStack) Preview(
|
||||
ctx context.Context,
|
||||
op backend.UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
return backend.PreviewStack(ctx, s, op)
|
||||
}
|
||||
|
||||
|
|
|
@ -828,7 +828,7 @@ func (b *cloudBackend) RenameStack(ctx context.Context, stack backend.Stack,
|
|||
}
|
||||
|
||||
func (b *cloudBackend) Preview(ctx context.Context, stack backend.Stack,
|
||||
op backend.UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
op backend.UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
// We can skip PreviewtThenPromptThenExecute, and just go straight to Execute.
|
||||
opts := backend.ApplierOptions{
|
||||
DryRun: true,
|
||||
|
@ -931,7 +931,7 @@ func (b *cloudBackend) createAndStartUpdate(
|
|||
func (b *cloudBackend) apply(
|
||||
ctx context.Context, kind apitype.UpdateKind, stack backend.Stack,
|
||||
op backend.UpdateOperation, opts backend.ApplierOptions,
|
||||
events chan<- engine.Event) (engine.ResourceChanges, result.Result) {
|
||||
events chan<- engine.Event) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
actionLabel := backend.ActionLabel(kind, opts.DryRun)
|
||||
|
||||
|
@ -945,7 +945,7 @@ func (b *cloudBackend) apply(
|
|||
update, version, token, err :=
|
||||
b.createAndStartUpdate(ctx, kind, stack, &op, opts.DryRun)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
|
||||
if !op.Opts.Display.SuppressPermalink && opts.ShowLink && !op.Opts.Display.JSONDisplay {
|
||||
|
@ -985,12 +985,12 @@ func (b *cloudBackend) query(ctx context.Context, op backend.QueryOperation,
|
|||
func (b *cloudBackend) runEngineAction(
|
||||
ctx context.Context, kind apitype.UpdateKind, stackRef backend.StackReference,
|
||||
op backend.UpdateOperation, update client.UpdateIdentifier, token string,
|
||||
callerEventsOpt chan<- engine.Event, dryRun bool) (engine.ResourceChanges, result.Result) {
|
||||
callerEventsOpt chan<- engine.Event, dryRun bool) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
contract.Assertf(token != "", "persisted actions require a token")
|
||||
u, err := b.newUpdate(ctx, stackRef, op, update, token)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
|
||||
// displayEvents renders the event to the console and Pulumi service. The processor for the
|
||||
|
@ -1039,19 +1039,20 @@ func (b *cloudBackend) runEngineAction(
|
|||
engineCtx.ParentSpan = parentSpan.Context()
|
||||
}
|
||||
|
||||
var plan *deploy.Plan
|
||||
var changes engine.ResourceChanges
|
||||
var res result.Result
|
||||
switch kind {
|
||||
case apitype.PreviewUpdate:
|
||||
changes, res = engine.Update(u, engineCtx, op.Opts.Engine, true)
|
||||
plan, changes, res = engine.Update(u, engineCtx, op.Opts.Engine, true)
|
||||
case apitype.UpdateUpdate:
|
||||
changes, res = engine.Update(u, engineCtx, op.Opts.Engine, dryRun)
|
||||
_, changes, res = engine.Update(u, engineCtx, op.Opts.Engine, dryRun)
|
||||
case apitype.ResourceImportUpdate:
|
||||
changes, res = engine.Import(u, engineCtx, op.Opts.Engine, op.Imports, dryRun)
|
||||
_, changes, res = engine.Import(u, engineCtx, op.Opts.Engine, op.Imports, dryRun)
|
||||
case apitype.RefreshUpdate:
|
||||
changes, res = engine.Refresh(u, engineCtx, op.Opts.Engine, dryRun)
|
||||
_, changes, res = engine.Refresh(u, engineCtx, op.Opts.Engine, dryRun)
|
||||
case apitype.DestroyUpdate:
|
||||
changes, res = engine.Destroy(u, engineCtx, op.Opts.Engine, dryRun)
|
||||
_, changes, res = engine.Destroy(u, engineCtx, op.Opts.Engine, dryRun)
|
||||
default:
|
||||
contract.Failf("Unrecognized update kind: %s", kind)
|
||||
}
|
||||
|
@ -1077,7 +1078,7 @@ func (b *cloudBackend) runEngineAction(
|
|||
res = result.Merge(res, result.FromError(fmt.Errorf("failed to complete update: %w", completeErr)))
|
||||
}
|
||||
|
||||
return changes, res
|
||||
return plan, changes, res
|
||||
}
|
||||
|
||||
func (b *cloudBackend) CancelCurrentUpdate(ctx context.Context, stackRef backend.StackReference) error {
|
||||
|
|
|
@ -140,7 +140,10 @@ func (s *cloudStack) Rename(ctx context.Context, newName tokens.QName) (backend.
|
|||
return backend.RenameStack(ctx, s, newName)
|
||||
}
|
||||
|
||||
func (s *cloudStack) Preview(ctx context.Context, op backend.UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
func (s *cloudStack) Preview(
|
||||
ctx context.Context,
|
||||
op backend.UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
return backend.PreviewStack(ctx, s, op)
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ type MockBackend struct {
|
|||
LogoutAllF func() error
|
||||
CurrentUserF func() (string, error)
|
||||
PreviewF func(context.Context, Stack,
|
||||
UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result)
|
||||
UpdateF func(context.Context, Stack,
|
||||
UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
ImportF func(context.Context, Stack,
|
||||
|
@ -180,7 +180,7 @@ func (be *MockBackend) GetStackCrypter(stackRef StackReference) (config.Crypter,
|
|||
}
|
||||
|
||||
func (be *MockBackend) Preview(ctx context.Context, stack Stack,
|
||||
op UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
op UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
if be.PreviewF != nil {
|
||||
return be.PreviewF(ctx, stack, op)
|
||||
|
@ -335,7 +335,7 @@ type MockStack struct {
|
|||
ConfigF func() config.Map
|
||||
SnapshotF func(ctx context.Context) (*deploy.Snapshot, error)
|
||||
BackendF func() Backend
|
||||
PreviewF func(ctx context.Context, op UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
PreviewF func(ctx context.Context, op UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result)
|
||||
UpdateF func(ctx context.Context, op UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
ImportF func(ctx context.Context, op UpdateOperation,
|
||||
imports []deploy.Import) (engine.ResourceChanges, result.Result)
|
||||
|
@ -381,7 +381,10 @@ func (ms *MockStack) Backend() Backend {
|
|||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (ms *MockStack) Preview(ctx context.Context, op UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
func (ms *MockStack) Preview(
|
||||
ctx context.Context,
|
||||
op UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
if ms.PreviewF != nil {
|
||||
return ms.PreviewF(ctx, op)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ type Stack interface {
|
|||
Backend() Backend // the backend this stack belongs to.
|
||||
|
||||
// Preview changes to this stack.
|
||||
Preview(ctx context.Context, op UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
Preview(ctx context.Context, op UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result)
|
||||
// Update this stack.
|
||||
Update(ctx context.Context, op UpdateOperation) (engine.ResourceChanges, result.Result)
|
||||
// Import resources into this stack.
|
||||
|
@ -73,7 +73,11 @@ func RenameStack(ctx context.Context, s Stack, newName tokens.QName) (StackRefer
|
|||
}
|
||||
|
||||
// PreviewStack previews changes to this stack.
|
||||
func PreviewStack(ctx context.Context, s Stack, op UpdateOperation) (engine.ResourceChanges, result.Result) {
|
||||
func PreviewStack(
|
||||
ctx context.Context,
|
||||
s Stack,
|
||||
op UpdateOperation) (*deploy.Plan, engine.ResourceChanges, result.Result) {
|
||||
|
||||
return s.Backend().Preview(ctx, s, op)
|
||||
}
|
||||
|
||||
|
@ -117,7 +121,10 @@ func GetStackLogs(ctx context.Context, s Stack, cfg StackConfiguration,
|
|||
}
|
||||
|
||||
// ExportStackDeployment exports the given stack's deployment as an opaque JSON message.
|
||||
func ExportStackDeployment(ctx context.Context, s Stack) (*apitype.UntypedDeployment, error) {
|
||||
func ExportStackDeployment(
|
||||
ctx context.Context,
|
||||
s Stack) (*apitype.UntypedDeployment, error) {
|
||||
|
||||
return s.Backend().ExportDeployment(ctx, s)
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ func Watch(ctx context.Context, b Backend, stack Stack, op UpdateOperation,
|
|||
op.Opts.Display.Color.Colorize(colors.SpecImportant+"Updating..."+colors.Reset+"\n"))
|
||||
|
||||
// Perform the update operation
|
||||
_, res := apply(ctx, apitype.UpdateUpdate, stack, op, opts, nil)
|
||||
_, _, res := apply(ctx, apitype.UpdateUpdate, stack, op, opts, nil)
|
||||
if res != nil {
|
||||
logging.V(5).Infof("watch update failed: %v", res.Error())
|
||||
if res.Error() == context.Canceled {
|
||||
|
|
|
@ -38,6 +38,8 @@ func newPreviewCmd() *cobra.Command {
|
|||
var configArray []string
|
||||
var configPath bool
|
||||
var client string
|
||||
var planFilePath string
|
||||
var showSecrets bool
|
||||
|
||||
// Flags for engine.UpdateOptions.
|
||||
var jsonDisplay bool
|
||||
|
@ -184,7 +186,7 @@ func newPreviewCmd() *cobra.Command {
|
|||
Display: displayOpts,
|
||||
}
|
||||
|
||||
changes, res := s.Preview(commandContext(), backend.UpdateOperation{
|
||||
plan, changes, res := s.Preview(commandContext(), backend.UpdateOperation{
|
||||
Proj: proj,
|
||||
Root: root,
|
||||
M: m,
|
||||
|
@ -200,6 +202,16 @@ func newPreviewCmd() *cobra.Command {
|
|||
case expectNop && changes != nil && changes.HasChanges():
|
||||
return result.FromError(errors.New("error: no changes were expected but changes were proposed"))
|
||||
default:
|
||||
if planFilePath != "" {
|
||||
encrypter, err := sm.Encrypter()
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
if err = writePlan(planFilePath, plan, encrypter, showSecrets); err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
// TODO(pdg-plan): emit a message about how to apply the plan
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}),
|
||||
|
@ -223,6 +235,13 @@ func newPreviewCmd() *cobra.Command {
|
|||
cmd.PersistentFlags().BoolVar(
|
||||
&configPath, "config-path", false,
|
||||
"Config keys contain a path to a property in a map or list to set")
|
||||
if hasExperimentalCommands() {
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&planFilePath, "save-plan", "",
|
||||
"Save the operations proposed by the preview to a plan file at the given path")
|
||||
}
|
||||
cmd.Flags().BoolVarP(
|
||||
&showSecrets, "show-secrets", "", false, "Emit secrets in plaintext in the plan file. Defaults to `false`")
|
||||
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&client, "client", "", "The address of an existing language runtime host to connect to")
|
||||
|
|
|
@ -76,6 +76,7 @@ func newUpCmd() *cobra.Command {
|
|||
var replaces []string
|
||||
var targetReplaces []string
|
||||
var targetDependents bool
|
||||
var planFilePath string
|
||||
|
||||
// up implementation used when the source of the Pulumi program is in the current working directory.
|
||||
upWorkingDirectory := func(opts backend.UpdateOptions) result.Result {
|
||||
|
@ -143,6 +144,22 @@ func newUpCmd() *cobra.Command {
|
|||
TargetDependents: targetDependents,
|
||||
}
|
||||
|
||||
if planFilePath != "" {
|
||||
dec, err := sm.Decrypter()
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
enc, err := sm.Encrypter()
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
plan, err := readPlan(planFilePath, dec, enc)
|
||||
if err != nil {
|
||||
return result.FromError(err)
|
||||
}
|
||||
opts.Engine.Plan = plan
|
||||
}
|
||||
|
||||
changes, res := s.Update(commandContext(), backend.UpdateOperation{
|
||||
Proj: proj,
|
||||
Root: root,
|
||||
|
@ -505,6 +522,14 @@ func newUpCmd() *cobra.Command {
|
|||
&yes, "yes", "y", false,
|
||||
"Automatically approve and perform the update after previewing it")
|
||||
|
||||
if hasExperimentalCommands() {
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&planFilePath, "plan", "",
|
||||
"Path to a plan file to use for the update. The update will not "+
|
||||
"perform operations that exceed its plan (e.g. replacements instead of updates, or updates instead"+
|
||||
"of sames).")
|
||||
}
|
||||
|
||||
if hasDebugCommands() {
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&eventLogPath, "event-log", "",
|
||||
|
|
|
@ -42,12 +42,15 @@ import (
|
|||
"github.com/pulumi/pulumi/pkg/v3/backend/httpstate"
|
||||
"github.com/pulumi/pulumi/pkg/v3/backend/state"
|
||||
"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/pkg/v3/secrets/passphrase"
|
||||
"github.com/pulumi/pulumi/pkg/v3/util/cancel"
|
||||
"github.com/pulumi/pulumi/pkg/v3/util/tracing"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/constant"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/ciutil"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
||||
|
@ -866,6 +869,34 @@ func getRefreshOption(proj *workspace.Project, refresh string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func writePlan(path string, plan *deploy.Plan, enc config.Encrypter, showSecrets bool) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer contract.IgnoreClose(f)
|
||||
|
||||
deploymentPlan, err := stack.SerializePlan(plan, enc, showSecrets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(f).Encode(deploymentPlan)
|
||||
}
|
||||
|
||||
func readPlan(path string, dec config.Decrypter, enc config.Encrypter) (*deploy.Plan, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer contract.IgnoreClose(f)
|
||||
|
||||
var deploymentPlan apitype.DeploymentPlanV1
|
||||
if err := json.NewDecoder(f).Decode(&deploymentPlan); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stack.DeserializePlan(deploymentPlan, dec, enc)
|
||||
}
|
||||
|
||||
func buildStackName(stackName string) (string, error) {
|
||||
if strings.Count(stackName, "/") == 2 {
|
||||
return stackName, nil
|
||||
|
|
|
@ -3110,10 +3110,8 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag
|
|||
pkg.functions = append(pkg.functions, f)
|
||||
|
||||
name := tokenToName(f.Token)
|
||||
|
||||
if pkg.names.Has(name) ||
|
||||
pkg.names.Has(name+"Args") ||
|
||||
pkg.names.Has(name+"Result") {
|
||||
originalName := name
|
||||
if pkg.names.Has(name) {
|
||||
switch {
|
||||
case strings.HasPrefix(name, "New"):
|
||||
name = "Create" + name[3:]
|
||||
|
@ -3126,9 +3124,15 @@ func generatePackageContextMap(tool string, pkg *schema.Package, goInfo GoPackag
|
|||
|
||||
if f.Inputs != nil {
|
||||
pkg.names.Add(name + "Args")
|
||||
if originalName != name {
|
||||
pkg.renamed[originalName+"Args"] = name + "Args"
|
||||
}
|
||||
}
|
||||
if f.Outputs != nil {
|
||||
pkg.names.Add(name + "Result")
|
||||
if originalName != name {
|
||||
pkg.renamed[originalName+"Result"] = name + "Result"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -203,11 +203,6 @@ var sdkTests = []sdkTest{
|
|||
Skip: codegen.NewStringSet("python/test", "nodejs/test"),
|
||||
SkipCompileCheck: codegen.NewStringSet(dotnet),
|
||||
},
|
||||
{
|
||||
Directory: "regress-8403",
|
||||
Description: "Regress pulumi/pulumi#8403",
|
||||
SkipCompileCheck: codegen.NewStringSet(dotnet, python, nodejs),
|
||||
},
|
||||
}
|
||||
|
||||
var genSDKOnly bool
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
title: "MongoDB Atlas"
|
||||
title_tag: "mongodbatlas.MongoDB Atlas"
|
||||
meta_desc: ""
|
||||
layout: api
|
||||
no_edit_this_page: true
|
||||
---
|
||||
|
||||
<!-- WARNING: this file was generated by test. -->
|
||||
<!-- Do not edit by hand unless you're certain you know what you are doing! -->
|
||||
|
||||
|
||||
|
||||
<h2 id="resources">Resources</h2>
|
||||
<ul class="api">
|
||||
<li><a href="provider" title="Provider"><span class="api-symbol api-symbol--resource"></span>Provider</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="functions">Functions</h2>
|
||||
<ul class="api">
|
||||
<li><a href="getcustomdbroles" title="GetCustomDbRoles"><span class="api-symbol api-symbol--function"></span>GetCustomDbRoles</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="package-details">Package Details</h2>
|
||||
<dl class="package-details">
|
||||
<dt>Repository</dt>
|
||||
<dd><a href=""></a></dd>
|
||||
<dt>License</dt>
|
||||
<dd></dd>
|
||||
<dt>Version</dt>
|
||||
<dd>0.0.1</dd>
|
||||
</dl>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"emittedFiles": [
|
||||
"_index.md",
|
||||
"getcustomdbroles/_index.md",
|
||||
"provider/_index.md"
|
||||
]
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
|
||||
---
|
||||
title: "getCustomDbRoles"
|
||||
title_tag: "mongodbatlas.getCustomDbRoles"
|
||||
meta_desc: "Documentation for the mongodbatlas.getCustomDbRoles function with examples, input properties, output properties, and supporting types."
|
||||
layout: api
|
||||
no_edit_this_page: true
|
||||
---
|
||||
|
||||
|
||||
|
||||
<!-- WARNING: this file was generated by test. -->
|
||||
<!-- Do not edit by hand unless you're certain you know what you are doing! -->
|
||||
|
||||
|
||||
|
||||
|
||||
## Using getCustomDbRoles {#using}
|
||||
|
||||
{{< chooser language "typescript,python,go,csharp" / >}}
|
||||
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
<div class="highlight"
|
||||
><pre class="chroma"><code class="language-typescript" data-lang="typescript"
|
||||
><span class="k">function </span>getCustomDbRoles<span class="p">(</span><span class="nx">args</span><span class="p">:</span> <span class="nx">GetCustomDbRolesArgs</span><span class="p">,</span> <span class="nx">opts</span><span class="p">?:</span> <span class="nx"><a href="/docs/reference/pkg/nodejs/pulumi/pulumi/#InvokeOptions">InvokeOptions</a></span><span class="p">): Promise<<span class="nx"><a href="#result">GetCustomDbRolesResult</a></span>></span
|
||||
></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
{{% choosable language python %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"
|
||||
><span class="k">def </span>get_custom_db_roles<span class="p">(</span><span class="nx">opts</span><span class="p">:</span> <span class="nx"><a href="/docs/reference/pkg/python/pulumi/#pulumi.InvokeOptions">Optional[InvokeOptions]</a></span> = None<span class="p">) -></span> <span>GetCustomDbRolesResult</span
|
||||
></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
{{% choosable language go %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"
|
||||
><span class="k">func </span>LookupCustomDbRoles<span class="p">(</span><span class="nx">ctx</span><span class="p"> *</span><span class="nx"><a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#Context">Context</a></span><span class="p">,</span> <span class="nx">args</span><span class="p"> *</span><span class="nx">LookupCustomDbRolesArgs</span><span class="p">,</span> <span class="nx">opts</span><span class="p"> ...</span><span class="nx"><a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#InvokeOption">InvokeOption</a></span><span class="p">) (*<span class="nx"><a href="#result">LookupCustomDbRolesResult</a></span>, error)</span
|
||||
></code></pre></div>
|
||||
|
||||
> Note: This function is named `LookupCustomDbRoles` in the Go SDK.
|
||||
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-csharp" data-lang="csharp"><span class="k">public static class </span><span class="nx">GetCustomDbRoles </span><span class="p">
|
||||
{</span><span class="k">
|
||||
public static </span>Task<<span class="nx"><a href="#result">GetCustomDbRolesResult</a></span>> <span class="p">InvokeAsync(</span><span class="nx">GetCustomDbRolesArgs</span><span class="p"> </span><span class="nx">args<span class="p">,</span> <span class="nx"><a href="/docs/reference/pkg/dotnet/Pulumi/Pulumi.InvokeOptions.html">InvokeOptions</a></span><span class="p">? </span><span class="nx">opts = null<span class="p">)</span><span class="p">
|
||||
}</span></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language go %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language python %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
|
||||
|
||||
## getCustomDbRoles Result {#result}
|
||||
|
||||
The following output properties are available:
|
||||
|
||||
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="result_csharp">
|
||||
<a href="#result_csharp" style="color: inherit; text-decoration: inherit;">Result</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#getcustomdbrolesresult">Get<wbr>Custom<wbr>Db<wbr>Roles<wbr>Result</a></span>
|
||||
</dt>
|
||||
<dd>{{% md %}}{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language go %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="result_go">
|
||||
<a href="#result_go" style="color: inherit; text-decoration: inherit;">Result</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#getcustomdbrolesresult">Get<wbr>Custom<wbr>Db<wbr>Roles<wbr>Result</a></span>
|
||||
</dt>
|
||||
<dd>{{% md %}}{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="result_nodejs">
|
||||
<a href="#result_nodejs" style="color: inherit; text-decoration: inherit;">result</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#getcustomdbrolesresult">Get<wbr>Custom<wbr>Db<wbr>Roles<wbr>Result</a></span>
|
||||
</dt>
|
||||
<dd>{{% md %}}{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language python %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="result_python">
|
||||
<a href="#result_python" style="color: inherit; text-decoration: inherit;">result</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#getcustomdbrolesresult">Get<wbr>Custom<wbr>Db<wbr>Roles<wbr>Result</a></span>
|
||||
</dt>
|
||||
<dd>{{% md %}}{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="package-details">Package Details</h2>
|
||||
<dl class="package-details">
|
||||
<dt>Repository</dt>
|
||||
<dd><a href=""></a></dd>
|
||||
<dt>License</dt>
|
||||
<dd></dd>
|
||||
</dl>
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
|
||||
---
|
||||
title: "Provider"
|
||||
title_tag: "mongodbatlas.Provider"
|
||||
meta_desc: "Documentation for the mongodbatlas.Provider resource with examples, input properties, output properties, lookup functions, and supporting types."
|
||||
layout: api
|
||||
no_edit_this_page: true
|
||||
---
|
||||
|
||||
|
||||
|
||||
<!-- WARNING: this file was generated by test. -->
|
||||
<!-- Do not edit by hand unless you're certain you know what you are doing! -->
|
||||
|
||||
|
||||
|
||||
|
||||
## Create a Provider Resource {#create}
|
||||
{{< chooser language "typescript,python,go,csharp" / >}}
|
||||
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-typescript" data-lang="typescript"><span class="k">new </span><span class="nx">Provider</span><span class="p">(</span><span class="nx">name</span><span class="p">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">args</span><span class="p">?:</span> <span class="nx"><a href="#inputs">ProviderArgs</a></span><span class="p">,</span> <span class="nx">opts</span><span class="p">?:</span> <span class="nx"><a href="/docs/reference/pkg/nodejs/pulumi/pulumi/#CustomResourceOptions">CustomResourceOptions</a></span><span class="p">);</span></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language python %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class=nd>@overload</span>
|
||||
<span class="k">def </span><span class="nx">Provider</span><span class="p">(</span><span class="nx">resource_name</span><span class="p">:</span> <span class="nx">str</span><span class="p">,</span>
|
||||
<span class="nx">opts</span><span class="p">:</span> <span class="nx"><a href="/docs/reference/pkg/python/pulumi/#pulumi.ResourceOptions">Optional[ResourceOptions]</a></span> = None<span class="p">)</span>
|
||||
<span class=nd>@overload</span>
|
||||
<span class="k">def </span><span class="nx">Provider</span><span class="p">(</span><span class="nx">resource_name</span><span class="p">:</span> <span class="nx">str</span><span class="p">,</span>
|
||||
<span class="nx">args</span><span class="p">:</span> <span class="nx"><a href="#inputs">Optional[ProviderArgs]</a></span> = None<span class="p">,</span>
|
||||
<span class="nx">opts</span><span class="p">:</span> <span class="nx"><a href="/docs/reference/pkg/python/pulumi/#pulumi.ResourceOptions">Optional[ResourceOptions]</a></span> = None<span class="p">)</span></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language go %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="k">func </span><span class="nx">NewProvider</span><span class="p">(</span><span class="nx">ctx</span><span class="p"> *</span><span class="nx"><a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#Context">Context</a></span><span class="p">,</span> <span class="nx">name</span><span class="p"> </span><span class="nx">string</span><span class="p">,</span> <span class="nx">args</span><span class="p"> *</span><span class="nx"><a href="#inputs">ProviderArgs</a></span><span class="p">,</span> <span class="nx">opts</span><span class="p"> ...</span><span class="nx"><a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#ResourceOption">ResourceOption</a></span><span class="p">) (*<span class="nx">Provider</span>, error)</span></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
<div class="highlight"><pre class="chroma"><code class="language-csharp" data-lang="csharp"><span class="k">public </span><span class="nx">Provider</span><span class="p">(</span><span class="nx">string</span><span class="p"> </span><span class="nx">name<span class="p">,</span> <span class="nx"><a href="#inputs">ProviderArgs</a></span><span class="p">? </span><span class="nx">args = null<span class="p">,</span> <span class="nx"><a href="/docs/reference/pkg/dotnet/Pulumi/Pulumi.CustomResourceOptions.html">CustomResourceOptions</a></span><span class="p">? </span><span class="nx">opts = null<span class="p">)</span></code></pre></div>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
|
||||
<dl class="resources-properties"><dt
|
||||
class="property-required" title="Required">
|
||||
<span>name</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>The unique name of the resource.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>args</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#inputs">ProviderArgs</a></span>
|
||||
</dt>
|
||||
<dd>The arguments to resource properties.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>opts</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="/docs/reference/pkg/nodejs/pulumi/pulumi/#CustomResourceOptions">CustomResourceOptions</a></span>
|
||||
</dt>
|
||||
<dd>Bag of options to control resource's behavior.</dd></dl>
|
||||
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language python %}}
|
||||
|
||||
<dl class="resources-properties"><dt
|
||||
class="property-required" title="Required">
|
||||
<span>resource_name</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">str</span>
|
||||
</dt>
|
||||
<dd>The unique name of the resource.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>args</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#inputs">ProviderArgs</a></span>
|
||||
</dt>
|
||||
<dd>The arguments to resource properties.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>opts</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="/docs/reference/pkg/python/pulumi/#pulumi.ResourceOptions">ResourceOptions</a></span>
|
||||
</dt>
|
||||
<dd>Bag of options to control resource's behavior.</dd></dl>
|
||||
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language go %}}
|
||||
|
||||
<dl class="resources-properties"><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>ctx</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#Context">Context</a></span>
|
||||
</dt>
|
||||
<dd>Context object for the current deployment.</dd><dt
|
||||
class="property-required" title="Required">
|
||||
<span>name</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>The unique name of the resource.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>args</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#inputs">ProviderArgs</a></span>
|
||||
</dt>
|
||||
<dd>The arguments to resource properties.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>opts</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#ResourceOption">ResourceOption</a></span>
|
||||
</dt>
|
||||
<dd>Bag of options to control resource's behavior.</dd></dl>
|
||||
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
|
||||
<dl class="resources-properties"><dt
|
||||
class="property-required" title="Required">
|
||||
<span>name</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>The unique name of the resource.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>args</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="#inputs">ProviderArgs</a></span>
|
||||
</dt>
|
||||
<dd>The arguments to resource properties.</dd><dt
|
||||
class="property-optional" title="Optional">
|
||||
<span>opts</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type"><a href="/docs/reference/pkg/dotnet/Pulumi/Pulumi.CustomResourceOptions.html">CustomResourceOptions</a></span>
|
||||
</dt>
|
||||
<dd>Bag of options to control resource's behavior.</dd></dl>
|
||||
|
||||
{{% /choosable %}}
|
||||
|
||||
## Provider Resource Properties {#properties}
|
||||
|
||||
To learn more about resource properties and how to use them, see [Inputs and Outputs]({{< relref "/docs/intro/concepts/inputs-outputs" >}}) in the Architecture and Concepts docs.
|
||||
|
||||
### Inputs
|
||||
|
||||
The Provider resource accepts the following [input]({{< relref "/docs/intro/concepts/inputs-outputs" >}}) properties:
|
||||
|
||||
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language go %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language python %}}
|
||||
<dl class="resources-properties"></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
### Outputs
|
||||
|
||||
All [input](#inputs) properties are implicitly available as output properties. Additionally, the Provider resource produces the following output properties:
|
||||
|
||||
|
||||
|
||||
{{% choosable language csharp %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="id_csharp">
|
||||
<a href="#id_csharp" style="color: inherit; text-decoration: inherit;">Id</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>{{% md %}}The provider-assigned unique ID for this managed resource.{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language go %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="id_go">
|
||||
<a href="#id_go" style="color: inherit; text-decoration: inherit;">Id</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>{{% md %}}The provider-assigned unique ID for this managed resource.{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language nodejs %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="id_nodejs">
|
||||
<a href="#id_nodejs" style="color: inherit; text-decoration: inherit;">id</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">string</span>
|
||||
</dt>
|
||||
<dd>{{% md %}}The provider-assigned unique ID for this managed resource.{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
{{% choosable language python %}}
|
||||
<dl class="resources-properties"><dt class="property-"
|
||||
title="">
|
||||
<span id="id_python">
|
||||
<a href="#id_python" style="color: inherit; text-decoration: inherit;">id</a>
|
||||
</span>
|
||||
<span class="property-indicator"></span>
|
||||
<span class="property-type">str</span>
|
||||
</dt>
|
||||
<dd>{{% md %}}The provider-assigned unique ID for this managed resource.{{% /md %}}</dd></dl>
|
||||
{{% /choosable %}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="package-details">Package Details</h2>
|
||||
<dl class="package-details">
|
||||
<dt>Repository</dt>
|
||||
<dd><a href=""></a></dd>
|
||||
<dt>License</dt>
|
||||
<dd></dd>
|
||||
</dl>
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Pulumi.Serialization;
|
||||
|
||||
namespace Pulumi.Mongodbatlas
|
||||
{
|
||||
public static class GetCustomDbRoles
|
||||
{
|
||||
public static Task<GetCustomDbRolesResult> InvokeAsync(GetCustomDbRolesArgs? args = null, InvokeOptions? options = null)
|
||||
=> Pulumi.Deployment.Instance.InvokeAsync<GetCustomDbRolesResult>("mongodbatlas::getCustomDbRoles", args ?? new GetCustomDbRolesArgs(), options.WithVersion());
|
||||
}
|
||||
|
||||
|
||||
public sealed class GetCustomDbRolesArgs : Pulumi.InvokeArgs
|
||||
{
|
||||
public GetCustomDbRolesArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[OutputType]
|
||||
public sealed class GetCustomDbRolesResult
|
||||
{
|
||||
public readonly Outputs.GetCustomDbRolesResult? Result;
|
||||
|
||||
[OutputConstructor]
|
||||
private GetCustomDbRolesResult(Outputs.GetCustomDbRolesResult? result)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Pulumi.Serialization;
|
||||
|
||||
namespace Pulumi.Mongodbatlas.Outputs
|
||||
{
|
||||
|
||||
[OutputType]
|
||||
public sealed class GetCustomDbRolesResult
|
||||
{
|
||||
[OutputConstructor]
|
||||
private GetCustomDbRolesResult()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Pulumi.Serialization;
|
||||
|
||||
namespace Pulumi.Mongodbatlas
|
||||
{
|
||||
[MongodbatlasResourceType("pulumi:providers:mongodbatlas")]
|
||||
public partial class Provider : Pulumi.ProviderResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a Provider resource with the given unique name, arguments, and options.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name="name">The unique name of the resource</param>
|
||||
/// <param name="args">The arguments used to populate this resource's properties</param>
|
||||
/// <param name="options">A bag of options that control this resource's behavior</param>
|
||||
public Provider(string name, ProviderArgs? args = null, CustomResourceOptions? options = null)
|
||||
: base("mongodbatlas", name, args ?? new ProviderArgs(), MakeResourceOptions(options, ""))
|
||||
{
|
||||
}
|
||||
|
||||
private static CustomResourceOptions MakeResourceOptions(CustomResourceOptions? options, Input<string>? id)
|
||||
{
|
||||
var defaultOptions = new CustomResourceOptions
|
||||
{
|
||||
Version = Utilities.Version,
|
||||
};
|
||||
var merged = CustomResourceOptions.Merge(defaultOptions, options);
|
||||
// Override the ID if one was specified for consistency with other language SDKs.
|
||||
merged.Id = id ?? merged.Id;
|
||||
return merged;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ProviderArgs : Pulumi.ResourceArgs
|
||||
{
|
||||
public ProviderArgs()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>Pulumi Corp.</Authors>
|
||||
<Company>Pulumi Corp.</Company>
|
||||
<Description></Description>
|
||||
<PackageLicenseExpression></PackageLicenseExpression>
|
||||
<PackageProjectUrl></PackageProjectUrl>
|
||||
<RepositoryUrl></RepositoryUrl>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseSharedCompilation>false</UseSharedCompilation>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="version.txt" />
|
||||
<None Include="version.txt" Pack="True" PackagePath="content" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="logo.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,87 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Pulumi;
|
||||
|
||||
namespace Pulumi.Mongodbatlas
|
||||
{
|
||||
static class Utilities
|
||||
{
|
||||
public static string? GetEnv(params string[] names)
|
||||
{
|
||||
foreach (var n in names)
|
||||
{
|
||||
var value = Environment.GetEnvironmentVariable(n);
|
||||
if (value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static string[] trueValues = { "1", "t", "T", "true", "TRUE", "True" };
|
||||
static string[] falseValues = { "0", "f", "F", "false", "FALSE", "False" };
|
||||
public static bool? GetEnvBoolean(params string[] names)
|
||||
{
|
||||
var s = GetEnv(names);
|
||||
if (s != null)
|
||||
{
|
||||
if (Array.IndexOf(trueValues, s) != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (Array.IndexOf(falseValues, s) != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int? GetEnvInt32(params string[] names) => int.TryParse(GetEnv(names), out int v) ? (int?)v : null;
|
||||
|
||||
public static double? GetEnvDouble(params string[] names) => double.TryParse(GetEnv(names), out double v) ? (double?)v : null;
|
||||
|
||||
public static InvokeOptions WithVersion(this InvokeOptions? options)
|
||||
{
|
||||
if (options?.Version != null)
|
||||
{
|
||||
return options;
|
||||
}
|
||||
return new InvokeOptions
|
||||
{
|
||||
Parent = options?.Parent,
|
||||
Provider = options?.Provider,
|
||||
Version = Version,
|
||||
};
|
||||
}
|
||||
|
||||
private readonly static string version;
|
||||
public static string Version => version;
|
||||
|
||||
static Utilities()
|
||||
{
|
||||
var assembly = typeof(Utilities).GetTypeInfo().Assembly;
|
||||
using var stream = assembly.GetManifestResourceStream("Pulumi.Mongodbatlas.version.txt");
|
||||
using var reader = new StreamReader(stream ?? throw new NotSupportedException("Missing embedded version.txt file"));
|
||||
version = reader.ReadToEnd().Trim();
|
||||
var parts = version.Split("\n");
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
// The first part is the provider name.
|
||||
version = parts[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class MongodbatlasResourceTypeAttribute : Pulumi.ResourceTypeAttribute
|
||||
{
|
||||
public MongodbatlasResourceTypeAttribute(string type) : base(type, Utilities.Version)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"emittedFiles": [
|
||||
"GetCustomDbRoles.cs",
|
||||
"Outputs/GetCustomDbRolesResult.cs",
|
||||
"Provider.cs",
|
||||
"Pulumi.Mongodbatlas.csproj",
|
||||
"README.md",
|
||||
"Utilities.cs",
|
||||
"logo.png"
|
||||
]
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 7.7 KiB |
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"emittedFiles": [
|
||||
"mongodbatlas/doc.go",
|
||||
"mongodbatlas/getCustomDbRoles.go",
|
||||
"mongodbatlas/init.go",
|
||||
"mongodbatlas/provider.go",
|
||||
"mongodbatlas/pulumiTypes.go",
|
||||
"mongodbatlas/pulumiUtilities.go"
|
||||
]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
// Package mongodbatlas exports types, functions, subpackages for provisioning mongodbatlas resources.
|
||||
//
|
||||
package mongodbatlas
|
|
@ -1,24 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
package mongodbatlas
|
||||
|
||||
import (
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
)
|
||||
|
||||
func LookupCustomDbRoles(ctx *pulumi.Context, args *LookupCustomDbRolesArgs, opts ...pulumi.InvokeOption) (*LookupCustomDbRolesResult, error) {
|
||||
var rv LookupCustomDbRolesResult
|
||||
err := ctx.Invoke("mongodbatlas::getCustomDbRoles", args, &rv, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rv, nil
|
||||
}
|
||||
|
||||
type LookupCustomDbRolesArgs struct {
|
||||
}
|
||||
|
||||
type LookupCustomDbRolesResult struct {
|
||||
Result *GetCustomDbRolesResult `pulumi:"result"`
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
package mongodbatlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
)
|
||||
|
||||
type pkg struct {
|
||||
version semver.Version
|
||||
}
|
||||
|
||||
func (p *pkg) Version() semver.Version {
|
||||
return p.version
|
||||
}
|
||||
|
||||
func (p *pkg) ConstructProvider(ctx *pulumi.Context, name, typ, urn string) (pulumi.ProviderResource, error) {
|
||||
if typ != "pulumi:providers:mongodbatlas" {
|
||||
return nil, fmt.Errorf("unknown provider type: %s", typ)
|
||||
}
|
||||
|
||||
r := &Provider{}
|
||||
err := ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn))
|
||||
return r, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
version, err := PkgVersion()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to determine package version. defaulting to v1: %v\n", err)
|
||||
}
|
||||
pulumi.RegisterResourcePackage(
|
||||
"mongodbatlas",
|
||||
&pkg{version},
|
||||
)
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
package mongodbatlas
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
pulumi.ProviderResourceState
|
||||
}
|
||||
|
||||
// NewProvider registers a new resource with the given unique name, arguments, and options.
|
||||
func NewProvider(ctx *pulumi.Context,
|
||||
name string, args *ProviderArgs, opts ...pulumi.ResourceOption) (*Provider, error) {
|
||||
if args == nil {
|
||||
args = &ProviderArgs{}
|
||||
}
|
||||
|
||||
var resource Provider
|
||||
err := ctx.RegisterResource("pulumi:providers:mongodbatlas", name, args, &resource, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resource, nil
|
||||
}
|
||||
|
||||
type providerArgs struct {
|
||||
}
|
||||
|
||||
// The set of arguments for constructing a Provider resource.
|
||||
type ProviderArgs struct {
|
||||
}
|
||||
|
||||
func (ProviderArgs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*providerArgs)(nil)).Elem()
|
||||
}
|
||||
|
||||
type ProviderInput interface {
|
||||
pulumi.Input
|
||||
|
||||
ToProviderOutput() ProviderOutput
|
||||
ToProviderOutputWithContext(ctx context.Context) ProviderOutput
|
||||
}
|
||||
|
||||
func (*Provider) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**Provider)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i *Provider) ToProviderOutput() ProviderOutput {
|
||||
return i.ToProviderOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i *Provider) ToProviderOutputWithContext(ctx context.Context) ProviderOutput {
|
||||
return pulumi.ToOutputWithContext(ctx, i).(ProviderOutput)
|
||||
}
|
||||
|
||||
type ProviderOutput struct{ *pulumi.OutputState }
|
||||
|
||||
func (ProviderOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**Provider)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o ProviderOutput) ToProviderOutput() ProviderOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o ProviderOutput) ToProviderOutputWithContext(ctx context.Context) ProviderOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func init() {
|
||||
pulumi.RegisterInputType(reflect.TypeOf((*ProviderInput)(nil)).Elem(), &Provider{})
|
||||
pulumi.RegisterOutputType(ProviderOutput{})
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
package mongodbatlas
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
)
|
||||
|
||||
type GetCustomDbRolesResult struct {
|
||||
}
|
||||
|
||||
// GetCustomDbRolesResultInput is an input type that accepts GetCustomDbRolesResultArgs and GetCustomDbRolesResultOutput values.
|
||||
// You can construct a concrete instance of `GetCustomDbRolesResultInput` via:
|
||||
//
|
||||
// GetCustomDbRolesResultArgs{...}
|
||||
type GetCustomDbRolesResultInput interface {
|
||||
pulumi.Input
|
||||
|
||||
ToGetCustomDbRolesResultOutput() GetCustomDbRolesResultOutput
|
||||
ToGetCustomDbRolesResultOutputWithContext(context.Context) GetCustomDbRolesResultOutput
|
||||
}
|
||||
|
||||
type GetCustomDbRolesResultArgs struct {
|
||||
}
|
||||
|
||||
func (GetCustomDbRolesResultArgs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*GetCustomDbRolesResult)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i GetCustomDbRolesResultArgs) ToGetCustomDbRolesResultOutput() GetCustomDbRolesResultOutput {
|
||||
return i.ToGetCustomDbRolesResultOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i GetCustomDbRolesResultArgs) ToGetCustomDbRolesResultOutputWithContext(ctx context.Context) GetCustomDbRolesResultOutput {
|
||||
return pulumi.ToOutputWithContext(ctx, i).(GetCustomDbRolesResultOutput)
|
||||
}
|
||||
|
||||
func (i GetCustomDbRolesResultArgs) ToGetCustomDbRolesResultPtrOutput() GetCustomDbRolesResultPtrOutput {
|
||||
return i.ToGetCustomDbRolesResultPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i GetCustomDbRolesResultArgs) ToGetCustomDbRolesResultPtrOutputWithContext(ctx context.Context) GetCustomDbRolesResultPtrOutput {
|
||||
return pulumi.ToOutputWithContext(ctx, i).(GetCustomDbRolesResultOutput).ToGetCustomDbRolesResultPtrOutputWithContext(ctx)
|
||||
}
|
||||
|
||||
// GetCustomDbRolesResultPtrInput is an input type that accepts GetCustomDbRolesResultArgs, GetCustomDbRolesResultPtr and GetCustomDbRolesResultPtrOutput values.
|
||||
// You can construct a concrete instance of `GetCustomDbRolesResultPtrInput` via:
|
||||
//
|
||||
// GetCustomDbRolesResultArgs{...}
|
||||
//
|
||||
// or:
|
||||
//
|
||||
// nil
|
||||
type GetCustomDbRolesResultPtrInput interface {
|
||||
pulumi.Input
|
||||
|
||||
ToGetCustomDbRolesResultPtrOutput() GetCustomDbRolesResultPtrOutput
|
||||
ToGetCustomDbRolesResultPtrOutputWithContext(context.Context) GetCustomDbRolesResultPtrOutput
|
||||
}
|
||||
|
||||
type getCustomDbRolesResultPtrType GetCustomDbRolesResultArgs
|
||||
|
||||
func GetCustomDbRolesResultPtr(v *GetCustomDbRolesResultArgs) GetCustomDbRolesResultPtrInput {
|
||||
return (*getCustomDbRolesResultPtrType)(v)
|
||||
}
|
||||
|
||||
func (*getCustomDbRolesResultPtrType) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**GetCustomDbRolesResult)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i *getCustomDbRolesResultPtrType) ToGetCustomDbRolesResultPtrOutput() GetCustomDbRolesResultPtrOutput {
|
||||
return i.ToGetCustomDbRolesResultPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i *getCustomDbRolesResultPtrType) ToGetCustomDbRolesResultPtrOutputWithContext(ctx context.Context) GetCustomDbRolesResultPtrOutput {
|
||||
return pulumi.ToOutputWithContext(ctx, i).(GetCustomDbRolesResultPtrOutput)
|
||||
}
|
||||
|
||||
type GetCustomDbRolesResultOutput struct{ *pulumi.OutputState }
|
||||
|
||||
func (GetCustomDbRolesResultOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*GetCustomDbRolesResult)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultOutput) ToGetCustomDbRolesResultOutput() GetCustomDbRolesResultOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultOutput) ToGetCustomDbRolesResultOutputWithContext(ctx context.Context) GetCustomDbRolesResultOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultOutput) ToGetCustomDbRolesResultPtrOutput() GetCustomDbRolesResultPtrOutput {
|
||||
return o.ToGetCustomDbRolesResultPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultOutput) ToGetCustomDbRolesResultPtrOutputWithContext(ctx context.Context) GetCustomDbRolesResultPtrOutput {
|
||||
return o.ApplyTWithContext(ctx, func(_ context.Context, v GetCustomDbRolesResult) *GetCustomDbRolesResult {
|
||||
return &v
|
||||
}).(GetCustomDbRolesResultPtrOutput)
|
||||
}
|
||||
|
||||
type GetCustomDbRolesResultPtrOutput struct{ *pulumi.OutputState }
|
||||
|
||||
func (GetCustomDbRolesResultPtrOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**GetCustomDbRolesResult)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultPtrOutput) ToGetCustomDbRolesResultPtrOutput() GetCustomDbRolesResultPtrOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultPtrOutput) ToGetCustomDbRolesResultPtrOutputWithContext(ctx context.Context) GetCustomDbRolesResultPtrOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o GetCustomDbRolesResultPtrOutput) Elem() GetCustomDbRolesResultOutput {
|
||||
return o.ApplyT(func(v *GetCustomDbRolesResult) GetCustomDbRolesResult {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
var ret GetCustomDbRolesResult
|
||||
return ret
|
||||
}).(GetCustomDbRolesResultOutput)
|
||||
}
|
||||
|
||||
func init() {
|
||||
pulumi.RegisterInputType(reflect.TypeOf((*GetCustomDbRolesResultInput)(nil)).Elem(), GetCustomDbRolesResultArgs{})
|
||||
pulumi.RegisterInputType(reflect.TypeOf((*GetCustomDbRolesResultPtrInput)(nil)).Elem(), GetCustomDbRolesResultArgs{})
|
||||
pulumi.RegisterOutputType(GetCustomDbRolesResultOutput{})
|
||||
pulumi.RegisterOutputType(GetCustomDbRolesResultPtrOutput{})
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
package mongodbatlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
)
|
||||
|
||||
type envParser func(v string) interface{}
|
||||
|
||||
func parseEnvBool(v string) interface{} {
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func parseEnvInt(v string) interface{} {
|
||||
i, err := strconv.ParseInt(v, 0, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return int(i)
|
||||
}
|
||||
|
||||
func parseEnvFloat(v string) interface{} {
|
||||
f, err := strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func parseEnvStringArray(v string) interface{} {
|
||||
var result pulumi.StringArray
|
||||
for _, item := range strings.Split(v, ";") {
|
||||
result = append(result, pulumi.String(item))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getEnvOrDefault(def interface{}, parser envParser, vars ...string) interface{} {
|
||||
for _, v := range vars {
|
||||
if value := os.Getenv(v); value != "" {
|
||||
if parser != nil {
|
||||
return parser(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// PkgVersion uses reflection to determine the version of the current package.
|
||||
func PkgVersion() (semver.Version, error) {
|
||||
type sentinal struct{}
|
||||
pkgPath := reflect.TypeOf(sentinal{}).PkgPath()
|
||||
re := regexp.MustCompile("^.*/pulumi-mongodbatlas/sdk(/v\\d+)?")
|
||||
if match := re.FindStringSubmatch(pkgPath); match != nil {
|
||||
vStr := match[1]
|
||||
if len(vStr) == 0 { // If the version capture group was empty, default to v1.
|
||||
return semver.Version{Major: 1}, nil
|
||||
}
|
||||
return semver.MustParse(fmt.Sprintf("%s.0.0", vStr[2:])), nil
|
||||
}
|
||||
return semver.Version{}, fmt.Errorf("failed to determine the package version from %s", pkgPath)
|
||||
}
|
||||
|
||||
// isZero is a null safe check for if a value is it's types zero value.
|
||||
func isZero(v interface{}) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
return reflect.ValueOf(v).IsZero()
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"emittedFiles": [
|
||||
"README.md",
|
||||
"getCustomDbRoles.ts",
|
||||
"index.ts",
|
||||
"package.json",
|
||||
"provider.ts",
|
||||
"tsconfig.json",
|
||||
"types/index.ts",
|
||||
"types/input.ts",
|
||||
"types/output.ts",
|
||||
"utilities.ts"
|
||||
]
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import { input as inputs, output as outputs } from "./types";
|
||||
import * as utilities from "./utilities";
|
||||
|
||||
export function getCustomDbRoles(args?: GetCustomDbRolesArgs, opts?: pulumi.InvokeOptions): Promise<GetCustomDbRolesResult> {
|
||||
args = args || {};
|
||||
if (!opts) {
|
||||
opts = {}
|
||||
}
|
||||
|
||||
if (!opts.version) {
|
||||
opts.version = utilities.getVersion();
|
||||
}
|
||||
return pulumi.runtime.invoke("mongodbatlas::getCustomDbRoles", {
|
||||
}, opts);
|
||||
}
|
||||
|
||||
export interface GetCustomDbRolesArgs {
|
||||
}
|
||||
|
||||
export interface GetCustomDbRolesResult {
|
||||
readonly result?: outputs.GetCustomDbRolesResult;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as utilities from "./utilities";
|
||||
|
||||
// Export members:
|
||||
export * from "./getCustomDbRoles";
|
||||
export * from "./provider";
|
||||
|
||||
// Export sub-modules:
|
||||
import * as types from "./types";
|
||||
|
||||
export {
|
||||
types,
|
||||
};
|
||||
|
||||
import { Provider } from "./provider";
|
||||
|
||||
pulumi.runtime.registerResourcePackage("mongodbatlas", {
|
||||
version: utilities.getVersion(),
|
||||
constructProvider: (name: string, type: string, urn: string): pulumi.ProviderResource => {
|
||||
if (type !== "pulumi:providers:mongodbatlas") {
|
||||
throw new Error(`unknown provider type ${type}`);
|
||||
}
|
||||
return new Provider(name, <any>undefined, { urn });
|
||||
},
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "@pulumi/mongodbatlas",
|
||||
"version": "${VERSION}",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pulumi/pulumi": "latest"
|
||||
},
|
||||
"pulumi": {
|
||||
"resource": true
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as utilities from "./utilities";
|
||||
|
||||
export class Provider extends pulumi.ProviderResource {
|
||||
/** @internal */
|
||||
public static readonly __pulumiType = 'mongodbatlas';
|
||||
|
||||
/**
|
||||
* Returns true if the given object is an instance of Provider. This is designed to work even
|
||||
* when multiple copies of the Pulumi SDK have been loaded into the same process.
|
||||
*/
|
||||
public static isInstance(obj: any): obj is Provider {
|
||||
if (obj === undefined || obj === null) {
|
||||
return false;
|
||||
}
|
||||
return obj['__pulumiType'] === Provider.__pulumiType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a Provider resource with the given unique name, arguments, and options.
|
||||
*
|
||||
* @param name The _unique_ name of the resource.
|
||||
* @param args The arguments to use to populate this resource's properties.
|
||||
* @param opts A bag of options that control this resource's behavior.
|
||||
*/
|
||||
constructor(name: string, args?: ProviderArgs, opts?: pulumi.ResourceOptions) {
|
||||
let resourceInputs: pulumi.Inputs = {};
|
||||
opts = opts || {};
|
||||
{
|
||||
}
|
||||
if (!opts.version) {
|
||||
opts = pulumi.mergeOptions(opts, { version: utilities.getVersion()});
|
||||
}
|
||||
super(Provider.__pulumiType, name, resourceInputs, opts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of arguments for constructing a Provider resource.
|
||||
*/
|
||||
export interface ProviderArgs {
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "bin",
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true
|
||||
},
|
||||
"files": [
|
||||
"getCustomDbRoles.ts",
|
||||
"index.ts",
|
||||
"provider.ts",
|
||||
"types/index.ts",
|
||||
"types/input.ts",
|
||||
"types/output.ts",
|
||||
"utilities.ts"
|
||||
]
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
// Export sub-modules:
|
||||
import * as input from "./input";
|
||||
import * as output from "./output";
|
||||
|
||||
export {
|
||||
input,
|
||||
output,
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import { input as inputs, output as outputs } from "../types";
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import { input as inputs, output as outputs } from "../types";
|
||||
|
||||
export interface GetCustomDbRolesResult {
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// *** WARNING: this file was generated by test. ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
|
||||
export function getEnv(...vars: string[]): string | undefined {
|
||||
for (const v of vars) {
|
||||
const value = process.env[v];
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getEnvBoolean(...vars: string[]): boolean | undefined {
|
||||
const s = getEnv(...vars);
|
||||
if (s !== undefined) {
|
||||
// NOTE: these values are taken from https://golang.org/src/strconv/atob.go?s=351:391#L1, which is what
|
||||
// Terraform uses internally when parsing boolean values.
|
||||
if (["1", "t", "T", "true", "TRUE", "True"].find(v => v === s) !== undefined) {
|
||||
return true;
|
||||
}
|
||||
if (["0", "f", "F", "false", "FALSE", "False"].find(v => v === s) !== undefined) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getEnvNumber(...vars: string[]): number | undefined {
|
||||
const s = getEnv(...vars);
|
||||
if (s !== undefined) {
|
||||
const f = parseFloat(s);
|
||||
if (!isNaN(f)) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getVersion(): string {
|
||||
let version = require('./package.json').version;
|
||||
// Node allows for the version to be prefixed by a "v", while semver doesn't.
|
||||
// If there is a v, strip it off.
|
||||
if (version.indexOf('v') === 0) {
|
||||
version = version.slice(1);
|
||||
}
|
||||
return version;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"emittedFiles": [
|
||||
"pulumi_mongodbatlas/README.md",
|
||||
"pulumi_mongodbatlas/__init__.py",
|
||||
"pulumi_mongodbatlas/_utilities.py",
|
||||
"pulumi_mongodbatlas/get_custom_db_roles.py",
|
||||
"pulumi_mongodbatlas/outputs.py",
|
||||
"pulumi_mongodbatlas/provider.py",
|
||||
"pulumi_mongodbatlas/py.typed",
|
||||
"setup.py"
|
||||
]
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
# coding=utf-8
|
||||
# *** WARNING: this file was generated by test. ***
|
||||
# *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
from . import _utilities
|
||||
import typing
|
||||
# Export this package's modules as members:
|
||||
from .get_custom_db_roles import *
|
||||
from .provider import *
|
||||
from . import outputs
|
||||
_utilities.register(
|
||||
resource_modules="""
|
||||
[]
|
||||
""",
|
||||
resource_packages="""
|
||||
[
|
||||
{
|
||||
"pkg": "mongodbatlas",
|
||||
"token": "pulumi:providers:mongodbatlas",
|
||||
"fqn": "pulumi_mongodbatlas",
|
||||
"class": "Provider"
|
||||
}
|
||||
]
|
||||
"""
|
||||
)
|
|
@ -1,236 +0,0 @@
|
|||
# coding=utf-8
|
||||
# *** WARNING: this file was generated by test. ***
|
||||
# *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
|
||||
import importlib.util
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import pkg_resources
|
||||
import sys
|
||||
import typing
|
||||
|
||||
import pulumi
|
||||
import pulumi.runtime
|
||||
|
||||
from semver import VersionInfo as SemverVersion
|
||||
from parver import Version as PEP440Version
|
||||
|
||||
|
||||
def get_env(*args):
|
||||
for v in args:
|
||||
value = os.getenv(v)
|
||||
if value is not None:
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def get_env_bool(*args):
|
||||
str = get_env(*args)
|
||||
if str is not None:
|
||||
# NOTE: these values are taken from https://golang.org/src/strconv/atob.go?s=351:391#L1, which is what
|
||||
# Terraform uses internally when parsing boolean values.
|
||||
if str in ["1", "t", "T", "true", "TRUE", "True"]:
|
||||
return True
|
||||
if str in ["0", "f", "F", "false", "FALSE", "False"]:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def get_env_int(*args):
|
||||
str = get_env(*args)
|
||||
if str is not None:
|
||||
try:
|
||||
return int(str)
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def get_env_float(*args):
|
||||
str = get_env(*args)
|
||||
if str is not None:
|
||||
try:
|
||||
return float(str)
|
||||
except:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def _get_semver_version():
|
||||
# __name__ is set to the fully-qualified name of the current module, In our case, it will be
|
||||
# <some module>._utilities. <some module> is the module we want to query the version for.
|
||||
root_package, *rest = __name__.split('.')
|
||||
|
||||
# pkg_resources uses setuptools to inspect the set of installed packages. We use it here to ask
|
||||
# for the currently installed version of the root package (i.e. us) and get its version.
|
||||
|
||||
# Unfortunately, PEP440 and semver differ slightly in incompatible ways. The Pulumi engine expects
|
||||
# to receive a valid semver string when receiving requests from the language host, so it's our
|
||||
# responsibility as the library to convert our own PEP440 version into a valid semver string.
|
||||
|
||||
pep440_version_string = pkg_resources.require(root_package)[0].version
|
||||
pep440_version = PEP440Version.parse(pep440_version_string)
|
||||
(major, minor, patch) = pep440_version.release
|
||||
prerelease = None
|
||||
if pep440_version.pre_tag == 'a':
|
||||
prerelease = f"alpha.{pep440_version.pre}"
|
||||
elif pep440_version.pre_tag == 'b':
|
||||
prerelease = f"beta.{pep440_version.pre}"
|
||||
elif pep440_version.pre_tag == 'rc':
|
||||
prerelease = f"rc.{pep440_version.pre}"
|
||||
elif pep440_version.dev is not None:
|
||||
prerelease = f"dev.{pep440_version.dev}"
|
||||
|
||||
# The only significant difference between PEP440 and semver as it pertains to us is that PEP440 has explicit support
|
||||
# for dev builds, while semver encodes them as "prerelease" versions. In order to bridge between the two, we convert
|
||||
# our dev build version into a prerelease tag. This matches what all of our other packages do when constructing
|
||||
# their own semver string.
|
||||
return SemverVersion(major=major, minor=minor, patch=patch, prerelease=prerelease)
|
||||
|
||||
|
||||
# Determine the version once and cache the value, which measurably improves program performance.
|
||||
_version = _get_semver_version()
|
||||
_version_str = str(_version)
|
||||
|
||||
|
||||
def get_version():
|
||||
return _version_str
|
||||
|
||||
|
||||
def get_resource_args_opts(resource_args_type, resource_options_type, *args, **kwargs):
|
||||
"""
|
||||
Return the resource args and options given the *args and **kwargs of a resource's
|
||||
__init__ method.
|
||||
"""
|
||||
|
||||
resource_args, opts = None, None
|
||||
|
||||
# If the first item is the resource args type, save it and remove it from the args list.
|
||||
if args and isinstance(args[0], resource_args_type):
|
||||
resource_args, args = args[0], args[1:]
|
||||
|
||||
# Now look at the first item in the args list again.
|
||||
# If the first item is the resource options class, save it.
|
||||
if args and isinstance(args[0], resource_options_type):
|
||||
opts = args[0]
|
||||
|
||||
# If resource_args is None, see if "args" is in kwargs, and, if so, if it's typed as the
|
||||
# the resource args type.
|
||||
if resource_args is None:
|
||||
a = kwargs.get("args")
|
||||
if isinstance(a, resource_args_type):
|
||||
resource_args = a
|
||||
|
||||
# If opts is None, look it up in kwargs.
|
||||
if opts is None:
|
||||
opts = kwargs.get("opts")
|
||||
|
||||
return resource_args, opts
|
||||
|
||||
|
||||
# Temporary: just use pulumi._utils.lazy_import once everyone upgrades.
|
||||
def lazy_import(fullname):
|
||||
|
||||
import pulumi._utils as u
|
||||
f = getattr(u, 'lazy_import', None)
|
||||
if f is None:
|
||||
f = _lazy_import_temp
|
||||
|
||||
return f(fullname)
|
||||
|
||||
|
||||
# Copied from pulumi._utils.lazy_import, see comments there.
|
||||
def _lazy_import_temp(fullname):
|
||||
m = sys.modules.get(fullname, None)
|
||||
if m is not None:
|
||||
return m
|
||||
|
||||
spec = importlib.util.find_spec(fullname)
|
||||
|
||||
m = sys.modules.get(fullname, None)
|
||||
if m is not None:
|
||||
return m
|
||||
|
||||
loader = importlib.util.LazyLoader(spec.loader)
|
||||
spec.loader = loader
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
|
||||
m = sys.modules.get(fullname, None)
|
||||
if m is not None:
|
||||
return m
|
||||
|
||||
sys.modules[fullname] = module
|
||||
loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class Package(pulumi.runtime.ResourcePackage):
|
||||
def __init__(self, pkg_info):
|
||||
super().__init__()
|
||||
self.pkg_info = pkg_info
|
||||
|
||||
def version(self):
|
||||
return _version
|
||||
|
||||
def construct_provider(self, name: str, typ: str, urn: str) -> pulumi.ProviderResource:
|
||||
if typ != self.pkg_info['token']:
|
||||
raise Exception(f"unknown provider type {typ}")
|
||||
Provider = getattr(lazy_import(self.pkg_info['fqn']), self.pkg_info['class'])
|
||||
return Provider(name, pulumi.ResourceOptions(urn=urn))
|
||||
|
||||
|
||||
class Module(pulumi.runtime.ResourceModule):
|
||||
def __init__(self, mod_info):
|
||||
super().__init__()
|
||||
self.mod_info = mod_info
|
||||
|
||||
def version(self):
|
||||
return _version
|
||||
|
||||
def construct(self, name: str, typ: str, urn: str) -> pulumi.Resource:
|
||||
class_name = self.mod_info['classes'].get(typ, None)
|
||||
|
||||
if class_name is None:
|
||||
raise Exception(f"unknown resource type {typ}")
|
||||
|
||||
TheClass = getattr(lazy_import(self.mod_info['fqn']), class_name)
|
||||
return TheClass(name, pulumi.ResourceOptions(urn=urn))
|
||||
|
||||
|
||||
def register(resource_modules, resource_packages):
|
||||
resource_modules = json.loads(resource_modules)
|
||||
resource_packages = json.loads(resource_packages)
|
||||
|
||||
for pkg_info in resource_packages:
|
||||
pulumi.runtime.register_resource_package(pkg_info['pkg'], Package(pkg_info))
|
||||
|
||||
for mod_info in resource_modules:
|
||||
pulumi.runtime.register_resource_module(
|
||||
mod_info['pkg'],
|
||||
mod_info['mod'],
|
||||
Module(mod_info))
|
||||
|
||||
|
||||
_F = typing.TypeVar('_F', bound=typing.Callable[..., typing.Any])
|
||||
|
||||
|
||||
def lift_output_func(func: typing.Any) -> typing.Callable[[_F], _F]:
|
||||
"""Decorator internally used on {fn}_output lifted function versions
|
||||
to implement them automatically from the un-lifted function."""
|
||||
|
||||
func_sig = inspect.signature(func)
|
||||
|
||||
def lifted_func(*args, opts=None, **kwargs):
|
||||
bound_args = func_sig.bind(*args, **kwargs)
|
||||
# Convert tuple to list, see pulumi/pulumi#8172
|
||||
args_list = list(bound_args.args)
|
||||
return pulumi.Output.from_input({
|
||||
'args': args_list,
|
||||
'kwargs': bound_args.kwargs
|
||||
}).apply(lambda resolved_args: func(*resolved_args['args'],
|
||||
opts=opts,
|
||||
**resolved_args['kwargs']))
|
||||
|
||||
return (lambda _: lifted_func)
|
|
@ -1,52 +0,0 @@
|
|||
# coding=utf-8
|
||||
# *** WARNING: this file was generated by test. ***
|
||||
# *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import warnings
|
||||
import pulumi
|
||||
import pulumi.runtime
|
||||
from typing import Any, Mapping, Optional, Sequence, Union, overload
|
||||
from . import _utilities
|
||||
from . import outputs
|
||||
|
||||
__all__ = [
|
||||
'GetCustomDbRolesResult',
|
||||
'AwaitableGetCustomDbRolesResult',
|
||||
'get_custom_db_roles',
|
||||
]
|
||||
|
||||
@pulumi.output_type
|
||||
class GetCustomDbRolesResult:
|
||||
def __init__(__self__, result=None):
|
||||
if result and not isinstance(result, dict):
|
||||
raise TypeError("Expected argument 'result' to be a dict")
|
||||
pulumi.set(__self__, "result", result)
|
||||
|
||||
@property
|
||||
@pulumi.getter
|
||||
def result(self) -> Optional['outputs.GetCustomDbRolesResult']:
|
||||
return pulumi.get(self, "result")
|
||||
|
||||
|
||||
class AwaitableGetCustomDbRolesResult(GetCustomDbRolesResult):
|
||||
# pylint: disable=using-constant-test
|
||||
def __await__(self):
|
||||
if False:
|
||||
yield self
|
||||
return GetCustomDbRolesResult(
|
||||
result=self.result)
|
||||
|
||||
|
||||
def get_custom_db_roles(opts: Optional[pulumi.InvokeOptions] = None) -> AwaitableGetCustomDbRolesResult:
|
||||
"""
|
||||
Use this data source to access information about an existing resource.
|
||||
"""
|
||||
__args__ = dict()
|
||||
if opts is None:
|
||||
opts = pulumi.InvokeOptions()
|
||||
if opts.version is None:
|
||||
opts.version = _utilities.get_version()
|
||||
__ret__ = pulumi.runtime.invoke('mongodbatlas::getCustomDbRoles', __args__, opts=opts, typ=GetCustomDbRolesResult).value
|
||||
|
||||
return AwaitableGetCustomDbRolesResult(
|
||||
result=__ret__.result)
|
|
@ -1,20 +0,0 @@
|
|||
# coding=utf-8
|
||||
# *** WARNING: this file was generated by test. ***
|
||||
# *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import warnings
|
||||
import pulumi
|
||||
import pulumi.runtime
|
||||
from typing import Any, Mapping, Optional, Sequence, Union, overload
|
||||
from . import _utilities
|
||||
|
||||
__all__ = [
|
||||
'GetCustomDbRolesResult',
|
||||
]
|
||||
|
||||
@pulumi.output_type
|
||||
class GetCustomDbRolesResult(dict):
|
||||
def __init__(__self__):
|
||||
pass
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
# coding=utf-8
|
||||
# *** WARNING: this file was generated by test. ***
|
||||
# *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import warnings
|
||||
import pulumi
|
||||
import pulumi.runtime
|
||||
from typing import Any, Mapping, Optional, Sequence, Union, overload
|
||||
from . import _utilities
|
||||
|
||||
__all__ = ['ProviderArgs', 'Provider']
|
||||
|
||||
@pulumi.input_type
|
||||
class ProviderArgs:
|
||||
def __init__(__self__):
|
||||
"""
|
||||
The set of arguments for constructing a Provider resource.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Provider(pulumi.ProviderResource):
|
||||
@overload
|
||||
def __init__(__self__,
|
||||
resource_name: str,
|
||||
opts: Optional[pulumi.ResourceOptions] = None,
|
||||
__props__=None):
|
||||
"""
|
||||
Create a Mongodbatlas resource with the given unique name, props, and options.
|
||||
:param str resource_name: The name of the resource.
|
||||
:param pulumi.ResourceOptions opts: Options for the resource.
|
||||
"""
|
||||
...
|
||||
@overload
|
||||
def __init__(__self__,
|
||||
resource_name: str,
|
||||
args: Optional[ProviderArgs] = None,
|
||||
opts: Optional[pulumi.ResourceOptions] = None):
|
||||
"""
|
||||
Create a Mongodbatlas resource with the given unique name, props, and options.
|
||||
:param str resource_name: The name of the resource.
|
||||
:param ProviderArgs args: The arguments to use to populate this resource's properties.
|
||||
:param pulumi.ResourceOptions opts: Options for the resource.
|
||||
"""
|
||||
...
|
||||
def __init__(__self__, resource_name: str, *args, **kwargs):
|
||||
resource_args, opts = _utilities.get_resource_args_opts(ProviderArgs, pulumi.ResourceOptions, *args, **kwargs)
|
||||
if resource_args is not None:
|
||||
__self__._internal_init(resource_name, opts, **resource_args.__dict__)
|
||||
else:
|
||||
__self__._internal_init(resource_name, *args, **kwargs)
|
||||
|
||||
def _internal_init(__self__,
|
||||
resource_name: str,
|
||||
opts: Optional[pulumi.ResourceOptions] = None,
|
||||
__props__=None):
|
||||
if opts is None:
|
||||
opts = pulumi.ResourceOptions()
|
||||
if not isinstance(opts, pulumi.ResourceOptions):
|
||||
raise TypeError('Expected resource options to be a ResourceOptions instance')
|
||||
if opts.version is None:
|
||||
opts.version = _utilities.get_version()
|
||||
if opts.id is None:
|
||||
if __props__ is not None:
|
||||
raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource')
|
||||
__props__ = ProviderArgs.__new__(ProviderArgs)
|
||||
|
||||
super(Provider, __self__).__init__(
|
||||
'mongodbatlas',
|
||||
resource_name,
|
||||
__props__,
|
||||
opts)
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# coding=utf-8
|
||||
# *** WARNING: this file was generated by test. ***
|
||||
# *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
import errno
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.install import install
|
||||
from subprocess import check_call
|
||||
|
||||
|
||||
VERSION = "0.0.0"
|
||||
PLUGIN_VERSION = "0.0.0"
|
||||
|
||||
class InstallPluginCommand(install):
|
||||
def run(self):
|
||||
install.run(self)
|
||||
try:
|
||||
check_call(['pulumi', 'plugin', 'install', 'resource', 'mongodbatlas', PLUGIN_VERSION])
|
||||
except OSError as error:
|
||||
if error.errno == errno.ENOENT:
|
||||
print(f"""
|
||||
There was an error installing the mongodbatlas resource provider plugin.
|
||||
It looks like `pulumi` is not installed on your system.
|
||||
Please visit https://pulumi.com/ to install the Pulumi CLI.
|
||||
You may try manually installing the plugin by running
|
||||
`pulumi plugin install resource mongodbatlas {PLUGIN_VERSION}`
|
||||
""")
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def readme():
|
||||
try:
|
||||
with open('README.md', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
return "mongodbatlas Pulumi Package - Development Version"
|
||||
|
||||
|
||||
setup(name='pulumi_mongodbatlas',
|
||||
version=VERSION,
|
||||
long_description=readme(),
|
||||
long_description_content_type='text/markdown',
|
||||
cmdclass={
|
||||
'install': InstallPluginCommand,
|
||||
},
|
||||
packages=find_packages(),
|
||||
package_data={
|
||||
'pulumi_mongodbatlas': [
|
||||
'py.typed',
|
||||
]
|
||||
},
|
||||
install_requires=[
|
||||
'parver>=0.2.1',
|
||||
'pulumi',
|
||||
'semver>=2.8.1'
|
||||
],
|
||||
zip_safe=False)
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"version": "0.0.1",
|
||||
"name": "mongodbatlas",
|
||||
"types": {
|
||||
"mongodbatlas::getCustomDbRolesResult": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"functions": {
|
||||
"mongodbatlas::getCustomDbRoles": {
|
||||
"inputs": {},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/types/mongodbatlas::getCustomDbRolesResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -167,7 +167,7 @@ func newDeployment(ctx *Context, info *deploymentContext, opts deploymentOptions
|
|||
var depl *deploy.Deployment
|
||||
if !opts.isImport {
|
||||
depl, err = deploy.NewDeployment(
|
||||
plugctx, target, target.Snapshot, source, localPolicyPackPaths, dryRun, ctx.BackendClient)
|
||||
plugctx, target, target.Snapshot, opts.Plan, source, localPolicyPackPaths, dryRun, ctx.BackendClient)
|
||||
} else {
|
||||
_, defaultProviderVersions, pluginErr := installPlugins(proj, pwd, main, target, plugctx,
|
||||
false /*returnInstallErrors*/)
|
||||
|
@ -218,12 +218,12 @@ type runActions interface {
|
|||
|
||||
// run executes the deployment. It is primarily responsible for handling cancellation.
|
||||
func (deployment *deployment) run(cancelCtx *Context, actions runActions, policyPacks map[string]string,
|
||||
preview bool) (ResourceChanges, result.Result) {
|
||||
preview bool) (*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
// Change into the plugin context's working directory.
|
||||
chdir, err := fsutil.Chdir(deployment.Plugctx.Pwd)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer chdir()
|
||||
|
||||
|
@ -242,6 +242,7 @@ func (deployment *deployment) run(cancelCtx *Context, actions runActions, policy
|
|||
start := time.Now()
|
||||
|
||||
done := make(chan bool)
|
||||
var newPlan *deploy.Plan
|
||||
var walkResult result.Result
|
||||
go func() {
|
||||
opts := deploy.Options{
|
||||
|
@ -259,7 +260,7 @@ func (deployment *deployment) run(cancelCtx *Context, actions runActions, policy
|
|||
DisableResourceReferences: deployment.Options.DisableResourceReferences,
|
||||
DisableOutputValues: deployment.Options.DisableOutputValues,
|
||||
}
|
||||
walkResult = deployment.Deployment.Execute(ctx, opts, preview)
|
||||
newPlan, walkResult = deployment.Deployment.Execute(ctx, opts, preview)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
|
@ -290,7 +291,7 @@ func (deployment *deployment) run(cancelCtx *Context, actions runActions, policy
|
|||
// Emit a summary event.
|
||||
deployment.Options.Events.summaryEvent(preview, actions.MaybeCorrupt(), duration, changes, policyPacks)
|
||||
|
||||
return changes, res
|
||||
return newPlan, changes, res
|
||||
}
|
||||
|
||||
func (deployment *deployment) Close() error {
|
||||
|
|
|
@ -23,7 +23,12 @@ import (
|
|||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
||||
)
|
||||
|
||||
func Destroy(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (ResourceChanges, result.Result) {
|
||||
func Destroy(
|
||||
u UpdateInfo,
|
||||
ctx *Context,
|
||||
opts UpdateOptions,
|
||||
dryRun bool) (*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
contract.Require(u != nil, "u")
|
||||
contract.Require(ctx != nil, "ctx")
|
||||
|
||||
|
@ -31,13 +36,13 @@ func Destroy(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (Resou
|
|||
|
||||
info, err := newDeploymentContext(u, "destroy", ctx.ParentSpan)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer info.Close()
|
||||
|
||||
emitter, err := makeEventEmitter(ctx.Events, u)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer emitter.Close()
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
)
|
||||
|
||||
func Import(u UpdateInfo, ctx *Context, opts UpdateOptions, imports []deploy.Import,
|
||||
dryRun bool) (ResourceChanges, result.Result) {
|
||||
dryRun bool) (*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
contract.Require(u != nil, "u")
|
||||
contract.Require(ctx != nil, "ctx")
|
||||
|
@ -30,13 +30,13 @@ func Import(u UpdateInfo, ctx *Context, opts UpdateOptions, imports []deploy.Imp
|
|||
|
||||
info, err := newDeploymentContext(u, "import", ctx.ParentSpan)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer info.Close()
|
||||
|
||||
emitter, err := makeEventEmitter(ctx.Events, u)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer emitter.Close()
|
||||
|
||||
|
|
|
@ -84,12 +84,12 @@ func TestImportOption(t *testing.T) {
|
|||
// Run the initial update. The import should fail due to a mismatch in inputs between the program and the
|
||||
// actual resource state.
|
||||
project := p.GetProject()
|
||||
_, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient, nil)
|
||||
_, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
// Run a second update after fixing the inputs. The import should succeed.
|
||||
inputs["foo"] = resource.NewStringProperty("bar")
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -107,7 +107,7 @@ func TestImportOption(t *testing.T) {
|
|||
assert.Len(t, snap.Resources, 2)
|
||||
|
||||
// Now, run another update. The update should succeed and there should be no diffs.
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -123,7 +123,7 @@ func TestImportOption(t *testing.T) {
|
|||
|
||||
// Change a property value and run a third update. The update should succeed.
|
||||
inputs["foo"] = resource.NewStringProperty("rab")
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -141,11 +141,11 @@ func TestImportOption(t *testing.T) {
|
|||
|
||||
// Change the property value s.t. the resource requires replacement. The update should fail.
|
||||
inputs["foo"] = resource.NewStringProperty("replace")
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient, nil)
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
// Finally, destroy the stack. The `Delete` function should be called.
|
||||
_, res = TestOp(Destroy).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
_, res = TestOp(Destroy).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -161,7 +161,7 @@ func TestImportOption(t *testing.T) {
|
|||
|
||||
// Now clear the ID to import and run an initial update to create a resource that we will import-replace.
|
||||
importID, inputs["foo"] = "", resource.NewStringProperty("bar")
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -182,7 +182,7 @@ func TestImportOption(t *testing.T) {
|
|||
importID = r.ID
|
||||
}
|
||||
}
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -199,7 +199,7 @@ func TestImportOption(t *testing.T) {
|
|||
// Then set the import ID and run another update. The update should succeed and should show an import-replace and
|
||||
// a delete-replaced.
|
||||
importID = "id"
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -222,7 +222,7 @@ func TestImportOption(t *testing.T) {
|
|||
|
||||
// Change the program to read a resource rather than creating one.
|
||||
readID = "id"
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -241,7 +241,7 @@ func TestImportOption(t *testing.T) {
|
|||
|
||||
// Now have the program import the resource. We should see an import-replace and a read-discard.
|
||||
readID, importID = "", readID
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
_, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -327,7 +327,7 @@ func TestImportWithDifferingImportIdentifierFormat(t *testing.T) {
|
|||
|
||||
// Run the initial update. The import should succeed.
|
||||
project := p.GetProject()
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient,
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -345,7 +345,7 @@ func TestImportWithDifferingImportIdentifierFormat(t *testing.T) {
|
|||
assert.Len(t, snap.Resources, 2)
|
||||
|
||||
// Now, run another update. The update should succeed and there should be no diffs.
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient,
|
||||
snap, res = TestOp(Update).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient,
|
||||
func(_ workspace.Project, _ deploy.Target, entries JournalEntries, _ []Event, res result.Result) result.Result {
|
||||
for _, entry := range entries {
|
||||
switch urn := entry.Step.URN(); urn {
|
||||
|
@ -483,7 +483,7 @@ func TestImportPlan(t *testing.T) {
|
|||
|
||||
// Run the initial update.
|
||||
project := p.GetProject()
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient, nil)
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Run an import.
|
||||
|
@ -491,7 +491,7 @@ func TestImportPlan(t *testing.T) {
|
|||
Type: "pkgA:m:typA",
|
||||
Name: "resB",
|
||||
ID: "imported-id",
|
||||
}}).Run(project, p.GetTarget(snap), p.Options, false, p.BackendClient, nil)
|
||||
}}).Run(project, p.GetTarget(t, snap), p.Options, false, p.BackendClient, nil)
|
||||
|
||||
assert.Nil(t, res)
|
||||
assert.Len(t, snap.Resources, 4)
|
||||
|
@ -552,7 +552,7 @@ func TestImportIgnoreChanges(t *testing.T) {
|
|||
}
|
||||
|
||||
project := p.GetProject()
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(nil), p.Options, false, p.BackendClient, nil)
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
|
||||
assert.Nil(t, res)
|
||||
|
||||
assert.Len(t, snap.Resources, 2)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -678,7 +678,7 @@ func TestCanceledRefresh(t *testing.T) {
|
|||
Parallel: 1,
|
||||
Host: deploytest.NewPluginHost(nil, nil, nil, loaders...),
|
||||
}
|
||||
project, target := p.GetProject(), p.GetTarget(old)
|
||||
project, target := p.GetProject(), p.GetTarget(t, old)
|
||||
validate := func(project workspace.Project, target deploy.Target, entries JournalEntries,
|
||||
_ []Event, res result.Result) result.Result {
|
||||
|
||||
|
|
|
@ -39,16 +39,25 @@ func (u *updateInfo) GetTarget() *deploy.Target {
|
|||
}
|
||||
|
||||
func ImportOp(imports []deploy.Import) TestOp {
|
||||
return TestOp(func(info UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (ResourceChanges, result.Result) {
|
||||
return TestOp(func(info UpdateInfo, ctx *Context, opts UpdateOptions,
|
||||
dryRun bool) (*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
return Import(info, ctx, opts, imports, dryRun)
|
||||
})
|
||||
}
|
||||
|
||||
type TestOp func(UpdateInfo, *Context, UpdateOptions, bool) (ResourceChanges, result.Result)
|
||||
type TestOp func(UpdateInfo, *Context, UpdateOptions, bool) (*deploy.Plan, ResourceChanges, result.Result)
|
||||
|
||||
type ValidateFunc func(project workspace.Project, target deploy.Target, entries JournalEntries,
|
||||
events []Event, res result.Result) result.Result
|
||||
|
||||
func (op TestOp) Plan(project workspace.Project, target deploy.Target, opts UpdateOptions,
|
||||
backendClient deploy.BackendClient, validate ValidateFunc) (*deploy.Plan, result.Result) {
|
||||
|
||||
plan, _, res := op.runWithContext(context.Background(), project, target, opts, true, backendClient, validate)
|
||||
return plan, res
|
||||
}
|
||||
|
||||
func (op TestOp) Run(project workspace.Project, target deploy.Target, opts UpdateOptions,
|
||||
dryRun bool, backendClient deploy.BackendClient, validate ValidateFunc) (*deploy.Snapshot, result.Result) {
|
||||
|
||||
|
@ -60,6 +69,15 @@ func (op TestOp) RunWithContext(
|
|||
target deploy.Target, opts UpdateOptions, dryRun bool,
|
||||
backendClient deploy.BackendClient, validate ValidateFunc) (*deploy.Snapshot, result.Result) {
|
||||
|
||||
_, snap, res := op.runWithContext(callerCtx, project, target, opts, dryRun, backendClient, validate)
|
||||
return snap, res
|
||||
}
|
||||
|
||||
func (op TestOp) runWithContext(
|
||||
callerCtx context.Context, project workspace.Project,
|
||||
target deploy.Target, opts UpdateOptions, dryRun bool,
|
||||
backendClient deploy.BackendClient, validate ValidateFunc) (*deploy.Plan, *deploy.Snapshot, result.Result) {
|
||||
|
||||
// Create an appropriate update info and context.
|
||||
info := &updateInfo{project: project, target: target}
|
||||
|
||||
|
@ -93,21 +111,21 @@ func (op TestOp) RunWithContext(
|
|||
}()
|
||||
|
||||
// Run the step and its validator.
|
||||
_, res := op(info, ctx, opts, dryRun)
|
||||
plan, _, res := op(info, ctx, opts, dryRun)
|
||||
contract.IgnoreClose(journal)
|
||||
|
||||
if validate != nil {
|
||||
res = validate(project, target, journal.Entries(), firedEvents, res)
|
||||
}
|
||||
if dryRun {
|
||||
return nil, res
|
||||
return plan, nil, res
|
||||
}
|
||||
|
||||
snap := journal.Snap(target.Snapshot)
|
||||
if res == nil && snap != nil {
|
||||
res = result.WrapIfNonNil(snap.VerifyIntegrity())
|
||||
}
|
||||
return snap, res
|
||||
return nil, snap, res
|
||||
}
|
||||
|
||||
type TestStep struct {
|
||||
|
@ -168,7 +186,7 @@ func (p *TestPlan) GetProject() workspace.Project {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *TestPlan) GetTarget(snapshot *deploy.Snapshot) deploy.Target {
|
||||
func (p *TestPlan) GetTarget(t *testing.T, snapshot *deploy.Snapshot) deploy.Target {
|
||||
stack, _, _ := p.getNames()
|
||||
|
||||
cfg := p.Config
|
||||
|
@ -180,7 +198,10 @@ func (p *TestPlan) GetTarget(snapshot *deploy.Snapshot) deploy.Target {
|
|||
Name: stack,
|
||||
Config: cfg,
|
||||
Decrypter: p.Decrypter,
|
||||
Snapshot: snapshot,
|
||||
// note: it's really important that the preview and update operate on different snapshots. the engine can and
|
||||
// does mutate the snapshot in-place, even in previews, and sharing a snapshot between preview and update can
|
||||
// cause state changes from the preview to persist even when doing an update.
|
||||
Snapshot: CloneSnapshot(t, snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,6 +209,18 @@ func assertIsErrorOrBailResult(t *testing.T, res result.Result) {
|
|||
assert.NotNil(t, res)
|
||||
}
|
||||
|
||||
// ClonePlan makes a deep copy of the given plan and returns a pointer to the clone.
|
||||
func ClonePlan(t *testing.T, plan *deploy.Plan) *deploy.Plan {
|
||||
t.Helper()
|
||||
if plan != nil {
|
||||
copiedPlan := copystructure.Must(copystructure.Copy(plan)).(*deploy.Plan)
|
||||
assert.True(t, reflect.DeepEqual(plan, copiedPlan))
|
||||
return copiedPlan
|
||||
}
|
||||
|
||||
return plan
|
||||
}
|
||||
|
||||
// CloneSnapshot makes a deep copy of the given snapshot and returns a pointer to the clone.
|
||||
func CloneSnapshot(t *testing.T, snap *deploy.Snapshot) *deploy.Snapshot {
|
||||
t.Helper()
|
||||
|
@ -204,12 +237,9 @@ func (p *TestPlan) Run(t *testing.T, snapshot *deploy.Snapshot) *deploy.Snapshot
|
|||
project := p.GetProject()
|
||||
snap := snapshot
|
||||
for _, step := range p.Steps {
|
||||
// note: it's really important that the preview and update operate on different snapshots. the engine can and
|
||||
// does mutate the snapshot in-place, even in previews, and sharing a snapshot between preview and update can
|
||||
// cause state changes from the preview to persist even when doing an update.
|
||||
if !step.SkipPreview {
|
||||
previewSnap := CloneSnapshot(t, snap)
|
||||
previewTarget := p.GetTarget(previewSnap)
|
||||
previewTarget := p.GetTarget(t, previewSnap)
|
||||
// Don't run validate on the preview step
|
||||
_, res := step.Op.Run(project, previewTarget, p.Options, true, p.BackendClient, nil)
|
||||
if step.ExpectFailure {
|
||||
|
@ -221,7 +251,7 @@ func (p *TestPlan) Run(t *testing.T, snapshot *deploy.Snapshot) *deploy.Snapshot
|
|||
}
|
||||
|
||||
var res result.Result
|
||||
target := p.GetTarget(snap)
|
||||
target := p.GetTarget(t, snap)
|
||||
snap, res = step.Op.Run(project, target, p.Options, false, p.BackendClient, step.Validate)
|
||||
if step.ExpectFailure {
|
||||
assertIsErrorOrBailResult(t, res)
|
||||
|
|
|
@ -23,7 +23,12 @@ import (
|
|||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
||||
)
|
||||
|
||||
func Refresh(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (ResourceChanges, result.Result) {
|
||||
func Refresh(
|
||||
u UpdateInfo,
|
||||
ctx *Context,
|
||||
opts UpdateOptions,
|
||||
dryRun bool) (*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
contract.Require(u != nil, "u")
|
||||
contract.Require(ctx != nil, "ctx")
|
||||
|
||||
|
@ -31,13 +36,13 @@ func Refresh(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (Resou
|
|||
|
||||
info, err := newDeploymentContext(u, "refresh", ctx.ParentSpan)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer info.Close()
|
||||
|
||||
emitter, err := makeEventEmitter(ctx.Events, u)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer emitter.Close()
|
||||
|
||||
|
|
|
@ -146,6 +146,9 @@ type UpdateOptions struct {
|
|||
|
||||
// the plugin host to use for this update
|
||||
Host plugin.Host
|
||||
|
||||
// The plan to use for the update, if any.
|
||||
Plan *deploy.Plan
|
||||
}
|
||||
|
||||
// ResourceChanges contains the aggregate resource changes by operation type.
|
||||
|
@ -165,7 +168,9 @@ func (changes ResourceChanges) HasChanges() bool {
|
|||
return c > 0
|
||||
}
|
||||
|
||||
func Update(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (ResourceChanges, result.Result) {
|
||||
func Update(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (
|
||||
*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
contract.Require(u != nil, "update")
|
||||
contract.Require(ctx != nil, "ctx")
|
||||
|
||||
|
@ -173,13 +178,13 @@ func Update(u UpdateInfo, ctx *Context, opts UpdateOptions, dryRun bool) (Resour
|
|||
|
||||
info, err := newDeploymentContext(u, "update", ctx.ParentSpan)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer info.Close()
|
||||
|
||||
emitter, err := makeEventEmitter(ctx.Events, u)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer emitter.Close()
|
||||
|
||||
|
@ -420,7 +425,7 @@ func newUpdateSource(
|
|||
}
|
||||
|
||||
func update(ctx *Context, info *deploymentContext, opts deploymentOptions,
|
||||
preview bool) (ResourceChanges, result.Result) {
|
||||
preview bool) (*deploy.Plan, ResourceChanges, result.Result) {
|
||||
|
||||
// Refresh and Import do not execute Policy Packs.
|
||||
policies := map[string]string{}
|
||||
|
@ -445,7 +450,7 @@ func update(ctx *Context, info *deploymentContext, opts deploymentOptions,
|
|||
|
||||
deployment, err := newDeployment(ctx, info, opts, preview)
|
||||
if err != nil {
|
||||
return nil, result.FromError(err)
|
||||
return nil, nil, result.FromError(err)
|
||||
}
|
||||
defer contract.IgnoreClose(deployment)
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/pulumi/pulumi/pkg/v3/resource/graph"
|
||||
"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/resource/config"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
||||
|
@ -145,6 +146,36 @@ func (m *resourceMap) mapRange(callback func(urn resource.URN, state *resource.S
|
|||
})
|
||||
}
|
||||
|
||||
type resourcePlans struct {
|
||||
m sync.RWMutex
|
||||
plans Plan
|
||||
}
|
||||
|
||||
func newResourcePlan(config config.Map) *resourcePlans {
|
||||
return &resourcePlans{
|
||||
plans: NewPlan(config),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *resourcePlans) set(urn resource.URN, plan *ResourcePlan) {
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
|
||||
m.plans.ResourcePlans[urn] = plan
|
||||
}
|
||||
|
||||
func (m *resourcePlans) get(urn resource.URN) (*ResourcePlan, bool) {
|
||||
m.m.RLock()
|
||||
defer m.m.RUnlock()
|
||||
|
||||
p, ok := m.plans.ResourcePlans[urn]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
func (m *resourcePlans) plan() *Plan {
|
||||
return &m.plans
|
||||
}
|
||||
|
||||
// A Deployment manages the iterative computation and execution of a deployment based on a stream of goal states.
|
||||
// A running deployment emits events that indicate its progress. These events must be used to record the new state
|
||||
// of the deployment target.
|
||||
|
@ -153,6 +184,7 @@ type Deployment struct {
|
|||
target *Target // the deployment target.
|
||||
prev *Snapshot // the old resource snapshot for comparison.
|
||||
olds map[resource.URN]*resource.State // a map of all old resources.
|
||||
plan *Plan // a map of all planned resource changes, if any.
|
||||
imports []Import // resources to import, if this is an import deployment.
|
||||
isImport bool // true if this is an import deployment.
|
||||
schemaLoader schema.Loader // the schema cache for this deployment, if any.
|
||||
|
@ -162,7 +194,8 @@ type Deployment struct {
|
|||
depGraph *graph.DependencyGraph // the dependency graph of the old snapshot.
|
||||
providers *providers.Registry // the provider registry for this deployment.
|
||||
goals *goalMap // the set of resource goals generated by the deployment.
|
||||
news *resourceMap // the set of new resources generated by the deployment.
|
||||
news *resourceMap // the set of new resources generated by the deployment
|
||||
newPlans *resourcePlans // the set of new resource plans.
|
||||
}
|
||||
|
||||
// addDefaultProviders adds any necessary default provider definitions and references to the given snapshot. Version
|
||||
|
@ -299,7 +332,7 @@ func buildResourceMap(prev *Snapshot, preview bool) ([]*resource.State, map[reso
|
|||
//
|
||||
// Note that a deployment uses internal concurrency and parallelism in various ways, so it must be closed if for some
|
||||
// reason it isn't carried out to its final conclusion. This will result in cancellation and reclamation of resources.
|
||||
func NewDeployment(ctx *plugin.Context, target *Target, prev *Snapshot, source Source,
|
||||
func NewDeployment(ctx *plugin.Context, target *Target, prev *Snapshot, plan *Plan, source Source,
|
||||
localPolicyPackPaths []string, preview bool, backendClient BackendClient) (*Deployment, error) {
|
||||
|
||||
contract.Assert(ctx != nil)
|
||||
|
@ -343,6 +376,7 @@ func NewDeployment(ctx *plugin.Context, target *Target, prev *Snapshot, source S
|
|||
ctx: ctx,
|
||||
target: target,
|
||||
prev: prev,
|
||||
plan: plan,
|
||||
olds: olds,
|
||||
source: source,
|
||||
localPolicyPackPaths: localPolicyPackPaths,
|
||||
|
@ -351,6 +385,7 @@ func NewDeployment(ctx *plugin.Context, target *Target, prev *Snapshot, source S
|
|||
providers: reg,
|
||||
goals: newGoals,
|
||||
news: newResources,
|
||||
newPlans: newResourcePlan(target.Config),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -405,7 +440,7 @@ func (d *Deployment) generateEventURN(event SourceEvent) resource.URN {
|
|||
}
|
||||
|
||||
// Execute executes a deployment to completion, using the given cancellation context and running a preview or update.
|
||||
func (d *Deployment) Execute(ctx context.Context, opts Options, preview bool) result.Result {
|
||||
func (d *Deployment) Execute(ctx context.Context, opts Options, preview bool) (*Plan, result.Result) {
|
||||
deploymentExec := &deploymentExecutor{deployment: d}
|
||||
return deploymentExec.Execute(ctx, opts, preview)
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ func (ex *deploymentExecutor) reportError(urn resource.URN, err error) {
|
|||
|
||||
// Execute executes a deployment to completion, using the given cancellation context and running a preview
|
||||
// or update.
|
||||
func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, preview bool) result.Result {
|
||||
func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, preview bool) (*Plan, result.Result) {
|
||||
// Set up a goroutine that will signal cancellation to the deployment's plugins if the caller context is cancelled.
|
||||
// We do not hang this off of the context we create below because we do not want the failure of a single step to
|
||||
// cause other steps to fail.
|
||||
|
@ -141,10 +141,10 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
// Before doing anything else, optionally refresh each resource in the base checkpoint.
|
||||
if opts.Refresh {
|
||||
if res := ex.refresh(callerCtx, opts, preview); res != nil {
|
||||
return res
|
||||
return nil, res
|
||||
}
|
||||
if opts.RefreshOnly {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,10 +156,10 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
replaceTargetsOpt := createTargetMap(opts.ReplaceTargets)
|
||||
destroyTargetsOpt := createTargetMap(opts.DestroyTargets)
|
||||
if res := ex.checkTargets(opts.ReplaceTargets, OpReplace); res != nil {
|
||||
return res
|
||||
return nil, res
|
||||
}
|
||||
if res := ex.checkTargets(opts.DestroyTargets, OpDelete); res != nil {
|
||||
return res
|
||||
return nil, res
|
||||
}
|
||||
|
||||
if (updateTargetsOpt != nil || replaceTargetsOpt != nil) && destroyTargetsOpt != nil {
|
||||
|
@ -169,7 +169,7 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
// Begin iterating the source.
|
||||
src, res := ex.deployment.source.Iterate(callerCtx, opts, ex.deployment)
|
||||
if res != nil {
|
||||
return res
|
||||
return nil, res
|
||||
}
|
||||
|
||||
// Set up a step generator for this deployment.
|
||||
|
@ -177,7 +177,7 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
|
||||
// Retire any pending deletes that are currently present in this deployment.
|
||||
if res := ex.retirePendingDeletes(callerCtx, opts, preview); res != nil {
|
||||
return res
|
||||
return nil, res
|
||||
}
|
||||
|
||||
// Derive a cancellable context for this deployment. We will only cancel this context if some piece of the
|
||||
|
@ -236,7 +236,15 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
}
|
||||
|
||||
if event.Event == nil {
|
||||
return false, ex.performDeletes(ctx, updateTargetsOpt, destroyTargetsOpt)
|
||||
res := ex.performDeletes(ctx, updateTargetsOpt, destroyTargetsOpt)
|
||||
if res != nil {
|
||||
if resErr := res.Error(); resErr != nil {
|
||||
logging.V(4).Infof("deploymentExecutor.Execute(...): error performing deletes: %v", resErr)
|
||||
ex.reportError("", resErr)
|
||||
return false, result.Bail()
|
||||
}
|
||||
}
|
||||
return false, res
|
||||
}
|
||||
|
||||
if res := ex.handleSingleEvent(event.Event); res != nil {
|
||||
|
@ -267,8 +275,38 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
res = ex.checkTargets(opts.UpdateTargets, OpUpdate)
|
||||
}
|
||||
|
||||
// Check that we did operations for everything expected in the plan. We mutate ResourcePlan.Ops as we run
|
||||
// so by the time we get here everything in the map should have an empty ops list (except for unneeded deletes)
|
||||
if res == nil && ex.deployment.plan != nil {
|
||||
for urn, resourcePlan := range ex.deployment.plan.ResourcePlans {
|
||||
if len(resourcePlan.Ops) != 0 {
|
||||
if len(resourcePlan.Ops) == 1 && resourcePlan.Ops[0] == OpDelete {
|
||||
// We haven't done a delete for this resource check if it was in the snapshot,
|
||||
// if it's already gone this wasn't done because it wasn't needed
|
||||
found := false
|
||||
for i := range ex.deployment.prev.Resources {
|
||||
if ex.deployment.prev.Resources[i].URN == urn {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find the resource in the old snapshot so this was just an unneeded delete
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
err := fmt.Errorf("expected resource operations for %v but none were seen", urn)
|
||||
logging.V(4).Infof("deploymentExecutor.Execute(...): error handling event: %v", err)
|
||||
ex.reportError(urn, err)
|
||||
res = result.Bail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if res != nil && res.IsBail() {
|
||||
return res
|
||||
return nil, res
|
||||
}
|
||||
|
||||
// If the step generator and step executor were both successful, then we send all the resources
|
||||
|
@ -280,7 +318,7 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
logging.V(4).Infof("deploymentExecutor.Execute(...): error analyzing resources: %v", resErr)
|
||||
ex.reportError("", resErr)
|
||||
}
|
||||
return result.Bail()
|
||||
return nil, result.Bail()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,13 +327,13 @@ func (ex *deploymentExecutor) Execute(callerCtx context.Context, opts Options, p
|
|||
// TODO(cyrusn): We seem to be losing any information about the original 'res's errors. Should
|
||||
// we be doing a merge here?
|
||||
ex.reportExecResult("failed", preview)
|
||||
return result.Bail()
|
||||
return nil, result.Bail()
|
||||
} else if canceled {
|
||||
ex.reportExecResult("canceled", preview)
|
||||
return result.Bail()
|
||||
return nil, result.Bail()
|
||||
}
|
||||
|
||||
return res
|
||||
return ex.deployment.newPlans.plan(), res
|
||||
}
|
||||
|
||||
func (ex *deploymentExecutor) performDeletes(
|
||||
|
@ -375,8 +413,7 @@ func (ex *deploymentExecutor) handleSingleEvent(event SourceEvent) result.Result
|
|||
steps, res = ex.stepGen.GenerateReadSteps(e)
|
||||
case RegisterResourceOutputsEvent:
|
||||
logging.V(4).Infof("deploymentExecutor.handleSingleEvent(...): received register resource outputs")
|
||||
ex.stepExec.ExecuteRegisterResourceOutputs(e)
|
||||
return nil
|
||||
return ex.stepExec.ExecuteRegisterResourceOutputs(e)
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
|
@ -433,9 +470,13 @@ func (ex *deploymentExecutor) retirePendingDeletes(callerCtx context.Context, op
|
|||
}
|
||||
|
||||
// import imports a list of resources into a stack.
|
||||
func (ex *deploymentExecutor) importResources(callerCtx context.Context, opts Options, preview bool) result.Result {
|
||||
func (ex *deploymentExecutor) importResources(
|
||||
callerCtx context.Context,
|
||||
opts Options,
|
||||
preview bool) (*Plan, result.Result) {
|
||||
|
||||
if len(ex.deployment.imports) == 0 {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create an executor for this import.
|
||||
|
@ -461,12 +502,12 @@ func (ex *deploymentExecutor) importResources(callerCtx context.Context, opts Op
|
|||
} else {
|
||||
ex.reportExecResult("failed", preview)
|
||||
}
|
||||
return result.Bail()
|
||||
return nil, result.Bail()
|
||||
} else if canceled {
|
||||
ex.reportExecResult("canceled", preview)
|
||||
return result.Bail()
|
||||
return nil, result.Bail()
|
||||
}
|
||||
return nil
|
||||
return ex.deployment.newPlans.plan(), nil
|
||||
}
|
||||
|
||||
// refresh refreshes the state of the base checkpoint file for the current deployment in memory.
|
||||
|
|
|
@ -42,7 +42,7 @@ func TestPendingOperationsDeployment(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
_, err := NewDeployment(&plugin.Context{}, &Target{}, snap, &fixedSource{}, nil, false, nil)
|
||||
_, err := NewDeployment(&plugin.Context{}, &Target{}, snap, nil, &fixedSource{}, nil, false, nil)
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ func NewImportDeployment(ctx *plugin.Context, target *Target, projectName tokens
|
|||
source: NewErrorSource(projectName),
|
||||
preview: preview,
|
||||
providers: reg,
|
||||
newPlans: newResourcePlan(target.Config),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
89
pkg/resource/deploy/manifest.go
Normal file
89
pkg/resource/deploy/manifest.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2016-2018, Pulumi Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
||||
)
|
||||
|
||||
// Manifest captures versions for all binaries used to construct this snapshot.
|
||||
type Manifest struct {
|
||||
Time time.Time // the time this snapshot was taken.
|
||||
Magic string // a magic cookie.
|
||||
Version string // the pulumi command version.
|
||||
Plugins []workspace.PluginInfo // the plugin versions also loaded.
|
||||
}
|
||||
|
||||
// Serialize turns a manifest into a data structure suitable for serialization.
|
||||
func (m Manifest) Serialize() apitype.ManifestV1 {
|
||||
manifest := apitype.ManifestV1{
|
||||
Time: m.Time,
|
||||
Magic: m.Magic,
|
||||
Version: m.Version,
|
||||
}
|
||||
for _, plug := range m.Plugins {
|
||||
var version string
|
||||
if plug.Version != nil {
|
||||
version = plug.Version.String()
|
||||
}
|
||||
manifest.Plugins = append(manifest.Plugins, apitype.PluginInfoV1{
|
||||
Name: plug.Name,
|
||||
Path: plug.Path,
|
||||
Type: plug.Kind,
|
||||
Version: version,
|
||||
})
|
||||
}
|
||||
return manifest
|
||||
}
|
||||
|
||||
// DeserializeManifest deserializes a typed ManifestV1 into a `deploy.Manifest`.
|
||||
func DeserializeManifest(m apitype.ManifestV1) (*Manifest, error) {
|
||||
manifest := Manifest{
|
||||
Time: m.Time,
|
||||
Magic: m.Magic,
|
||||
Version: m.Version,
|
||||
}
|
||||
for _, plug := range m.Plugins {
|
||||
var version *semver.Version
|
||||
if v := plug.Version; v != "" {
|
||||
sv, err := semver.ParseTolerant(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version = &sv
|
||||
}
|
||||
manifest.Plugins = append(manifest.Plugins, workspace.PluginInfo{
|
||||
Name: plug.Name,
|
||||
Kind: plug.Type,
|
||||
Version: version,
|
||||
})
|
||||
}
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// NewMagic creates a magic cookie out of a manifest; this can be used to check for tampering. This ignores
|
||||
// any existing magic value already stored on the manifest.
|
||||
func (m Manifest) NewMagic() string {
|
||||
if m.Version == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(m.Version)))
|
||||
}
|
496
pkg/resource/deploy/plan.go
Normal file
496
pkg/resource/deploy/plan.go
Normal file
|
@ -0,0 +1,496 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
|
||||
"github.com/pulumi/pulumi/pkg/v3/version"
|
||||
"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/contract"
|
||||
)
|
||||
|
||||
// A Plan is a mapping from URNs to ResourcePlans. The plan defines an expected set of resources and the expected
|
||||
// inputs and operations for each. The inputs and operations are treated as constraints, and may allow for inputs or
|
||||
// operations that do not exactly match those recorded in the plan. In the case of inputs, unknown values in the plan
|
||||
// accept any value (including no value) as valid. For operations, a same step is allowed in place of an update or
|
||||
// a replace step, and an update is allowed in place of a replace step. All resource options are required to match
|
||||
// exactly.
|
||||
type Plan struct {
|
||||
ResourcePlans map[resource.URN]*ResourcePlan
|
||||
Manifest Manifest
|
||||
// Any environment variables that were set when the plan was created. Values are encrypted.
|
||||
EnvironmentVariables map[string][]byte
|
||||
// The configuration in use during the plan.
|
||||
Config config.Map
|
||||
}
|
||||
|
||||
func NewPlan(config config.Map) Plan {
|
||||
manifest := Manifest{
|
||||
Time: time.Now(),
|
||||
Version: version.Version,
|
||||
// Plugins: sm.plugins, - Explicitly dropped, since we don't use the plugin list in the manifest anymore.
|
||||
}
|
||||
manifest.Magic = manifest.NewMagic()
|
||||
|
||||
return Plan{
|
||||
ResourcePlans: make(map[resource.URN]*ResourcePlan),
|
||||
Manifest: manifest,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Goal is a desired state for a resource object. Normally it represents a subset of the resource's state expressed by
|
||||
// a program, however if Output is true, it represents a more complete, post-deployment view of the state.
|
||||
type GoalPlan struct {
|
||||
// the type of resource.
|
||||
Type tokens.Type
|
||||
// the name for the resource's URN.
|
||||
Name tokens.QName
|
||||
// true if this resource is custom, managed by a plugin.
|
||||
Custom bool
|
||||
// the resource's properties we expect to add.
|
||||
Adds resource.PropertyMap
|
||||
// the resource's properties we expect to delete.
|
||||
Deletes []resource.PropertyKey
|
||||
// the resource's properties we expect to update.
|
||||
Updates resource.PropertyMap
|
||||
// an optional parent URN for this resource.
|
||||
Parent resource.URN
|
||||
// true to protect this resource from deletion.
|
||||
Protect bool
|
||||
// dependencies of this resource object.
|
||||
Dependencies []resource.URN
|
||||
// the provider to use for this resource.
|
||||
Provider string
|
||||
// the set of dependencies that affect each property.
|
||||
PropertyDependencies map[resource.PropertyKey][]resource.URN
|
||||
// true if this resource should be deleted prior to replacement.
|
||||
DeleteBeforeReplace *bool
|
||||
// a list of property names to ignore during changes.
|
||||
IgnoreChanges []string
|
||||
// outputs that should always be treated as secrets.
|
||||
AdditionalSecretOutputs []resource.PropertyKey
|
||||
// additional URNs that should be aliased to this resource.
|
||||
Aliases []resource.URN
|
||||
// the expected ID of the resource, if any.
|
||||
ID resource.ID
|
||||
// an optional config object for resource options
|
||||
CustomTimeouts resource.CustomTimeouts
|
||||
}
|
||||
|
||||
func NewGoalPlan(oldOutputs resource.PropertyMap, goal *resource.Goal) *GoalPlan {
|
||||
if goal == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var adds resource.PropertyMap
|
||||
var deletes []resource.PropertyKey
|
||||
var updates resource.PropertyMap
|
||||
|
||||
if diff, hasDiff := oldOutputs.DiffIncludeUnknowns(goal.Properties); hasDiff {
|
||||
adds = diff.Adds
|
||||
updates = make(resource.PropertyMap)
|
||||
for k := range diff.Updates {
|
||||
updates[k] = diff.Updates[k].New
|
||||
}
|
||||
deletes = make([]resource.PropertyKey, len(diff.Deletes))
|
||||
i := 0
|
||||
for k := range diff.Deletes {
|
||||
deletes[i] = k
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
return &GoalPlan{
|
||||
Type: goal.Type,
|
||||
Name: goal.Name,
|
||||
Custom: goal.Custom,
|
||||
Adds: adds,
|
||||
Deletes: deletes,
|
||||
Updates: updates,
|
||||
Parent: goal.Parent,
|
||||
Protect: goal.Protect,
|
||||
Dependencies: goal.Dependencies,
|
||||
Provider: goal.Provider,
|
||||
PropertyDependencies: goal.PropertyDependencies,
|
||||
DeleteBeforeReplace: goal.DeleteBeforeReplace,
|
||||
IgnoreChanges: goal.IgnoreChanges,
|
||||
AdditionalSecretOutputs: goal.AdditionalSecretOutputs,
|
||||
Aliases: goal.Aliases,
|
||||
ID: goal.ID,
|
||||
CustomTimeouts: goal.CustomTimeouts,
|
||||
}
|
||||
}
|
||||
|
||||
// A ResourcePlan represents the planned goal state and resource operations for a single resource. The operations are
|
||||
// ordered.
|
||||
type ResourcePlan struct {
|
||||
Goal *GoalPlan
|
||||
Ops []StepOp
|
||||
Outputs resource.PropertyMap
|
||||
}
|
||||
|
||||
func (rp *ResourcePlan) diffURNs(a, b []resource.URN) (message string, changed bool) {
|
||||
stringsA := make([]string, len(a))
|
||||
for i, urn := range a {
|
||||
stringsA[i] = string(urn)
|
||||
}
|
||||
stringsB := make([]string, len(a))
|
||||
for i, urn := range b {
|
||||
stringsB[i] = string(urn)
|
||||
}
|
||||
return rp.diffStrings(stringsA, stringsB)
|
||||
}
|
||||
|
||||
func (rp *ResourcePlan) diffPropertyKeys(a, b []resource.PropertyKey) (message string, changed bool) {
|
||||
stringsA := make([]string, len(a))
|
||||
for i, key := range a {
|
||||
stringsA[i] = string(key)
|
||||
}
|
||||
stringsB := make([]string, len(a))
|
||||
for i, key := range b {
|
||||
stringsB[i] = string(key)
|
||||
}
|
||||
return rp.diffStrings(stringsA, stringsB)
|
||||
}
|
||||
|
||||
func (rp *ResourcePlan) diffStrings(a, b []string) (message string, changed bool) {
|
||||
setA := map[string]struct{}{}
|
||||
for _, s := range a {
|
||||
setA[s] = struct{}{}
|
||||
}
|
||||
|
||||
setB := map[string]struct{}{}
|
||||
for _, s := range b {
|
||||
setB[s] = struct{}{}
|
||||
}
|
||||
|
||||
var adds, deletes []string
|
||||
for s := range setA {
|
||||
if _, has := setB[s]; !has {
|
||||
deletes = append(deletes, s)
|
||||
}
|
||||
}
|
||||
for s := range setB {
|
||||
if _, has := setA[s]; !has {
|
||||
adds = append(adds, s)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(adds)
|
||||
sort.Strings(deletes)
|
||||
|
||||
if len(adds) == 0 && len(deletes) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if len(adds) != 0 {
|
||||
message = fmt.Sprintf("added %v", strings.Join(adds, ", "))
|
||||
}
|
||||
if len(deletes) != 0 {
|
||||
if len(adds) != 0 {
|
||||
message += "; "
|
||||
}
|
||||
message += fmt.Sprintf("deleted %v", strings.Join(deletes, ", "))
|
||||
}
|
||||
return message, true
|
||||
}
|
||||
|
||||
func (rp *ResourcePlan) diffPropertyDependencies(a, b map[resource.PropertyKey][]resource.URN) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is similar to ResourcePlan.checkGoal but for the case we're we don't have a goal saved.
|
||||
// This simple checks that we're not changing anything.
|
||||
func checkMissingPlan(
|
||||
oldState *resource.State,
|
||||
newInputs resource.PropertyMap,
|
||||
programGoal *resource.Goal) error {
|
||||
|
||||
// We new up a fake ResourcePlan that matches the old state and then simply call checkGoal on it.
|
||||
goal := &GoalPlan{
|
||||
Type: oldState.Type,
|
||||
Name: oldState.URN.Name(),
|
||||
Custom: oldState.Custom,
|
||||
Adds: nil,
|
||||
Deletes: nil,
|
||||
Updates: nil,
|
||||
Parent: oldState.Parent,
|
||||
Protect: oldState.Protect,
|
||||
Dependencies: oldState.Dependencies,
|
||||
Provider: oldState.Provider,
|
||||
PropertyDependencies: oldState.PropertyDependencies,
|
||||
DeleteBeforeReplace: nil,
|
||||
IgnoreChanges: nil,
|
||||
AdditionalSecretOutputs: oldState.AdditionalSecretOutputs,
|
||||
Aliases: oldState.Aliases,
|
||||
ID: "",
|
||||
CustomTimeouts: oldState.CustomTimeouts,
|
||||
}
|
||||
|
||||
rp := ResourcePlan{Goal: goal}
|
||||
return rp.checkGoal(oldState.Outputs, newInputs, programGoal)
|
||||
}
|
||||
|
||||
func (rp *ResourcePlan) checkGoal(
|
||||
oldOutputs resource.PropertyMap,
|
||||
newInputs resource.PropertyMap,
|
||||
programGoal *resource.Goal) error {
|
||||
|
||||
contract.Assert(programGoal != nil)
|
||||
contract.Assert(newInputs != nil)
|
||||
// rp.Goal may be nil, but if it isn't Type and Name should match
|
||||
contract.Assert(rp.Goal == nil || rp.Goal.Type == programGoal.Type)
|
||||
contract.Assert(rp.Goal == nil || rp.Goal.Name == programGoal.Name)
|
||||
|
||||
if rp.Goal == nil {
|
||||
// If the plan goal is nil it expected a delete
|
||||
return fmt.Errorf("resource unexpectedly not deleted")
|
||||
}
|
||||
|
||||
// Check that either both resources are custom resources or both are component resources.
|
||||
if programGoal.Custom != rp.Goal.Custom {
|
||||
// TODO(pdg-plan): wording?
|
||||
expected := "custom"
|
||||
if !rp.Goal.Custom {
|
||||
expected = "component"
|
||||
}
|
||||
return fmt.Errorf("resource kind changed (expected %v)", expected)
|
||||
}
|
||||
|
||||
// Check that the provider is identical.
|
||||
if rp.Goal.Provider != programGoal.Provider {
|
||||
// Provider references are a combination of URN and ID, the latter of which may be unknown. Check for that
|
||||
// case here.
|
||||
expected, err := providers.ParseReference(rp.Goal.Provider)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse provider reference %v: %w", rp.Goal.Provider, err)
|
||||
}
|
||||
actual, err := providers.ParseReference(programGoal.Provider)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse provider reference %v: %w", programGoal.Provider, err)
|
||||
}
|
||||
if expected.URN() != actual.URN() || expected.ID() != providers.UnknownID {
|
||||
return fmt.Errorf("provider changed (expected %v)", rp.Goal.Provider)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the parent is identical.
|
||||
if programGoal.Parent != rp.Goal.Parent {
|
||||
return fmt.Errorf("parent changed (expected %v)", rp.Goal.Parent)
|
||||
}
|
||||
|
||||
// Check that the protect bit is identical.
|
||||
if programGoal.Protect != rp.Goal.Protect {
|
||||
return fmt.Errorf("protect changed (expected %v)", rp.Goal.Protect)
|
||||
}
|
||||
|
||||
// Check that the DBR bit is identical.
|
||||
switch {
|
||||
case rp.Goal.DeleteBeforeReplace == nil && programGoal.DeleteBeforeReplace == nil:
|
||||
// OK
|
||||
case rp.Goal.DeleteBeforeReplace != nil && programGoal.DeleteBeforeReplace != nil:
|
||||
if *rp.Goal.DeleteBeforeReplace != *programGoal.DeleteBeforeReplace {
|
||||
return fmt.Errorf("deleteBeforeReplace changed (expected %v)", *rp.Goal.DeleteBeforeReplace)
|
||||
}
|
||||
default:
|
||||
expected := "no value"
|
||||
if rp.Goal.DeleteBeforeReplace != nil {
|
||||
expected = fmt.Sprintf("%v", *rp.Goal.DeleteBeforeReplace)
|
||||
}
|
||||
return fmt.Errorf("deleteBeforeReplace changed (expected %v)", expected)
|
||||
}
|
||||
|
||||
// Check that the import ID is identical.
|
||||
if rp.Goal.ID != programGoal.ID {
|
||||
return fmt.Errorf("importID changed (expected %v)", rp.Goal.ID)
|
||||
}
|
||||
|
||||
// Check that the timeouts are identical.
|
||||
switch {
|
||||
case rp.Goal.CustomTimeouts.Create != programGoal.CustomTimeouts.Create:
|
||||
return fmt.Errorf("create timeout changed (expected %v)", rp.Goal.CustomTimeouts.Create)
|
||||
case rp.Goal.CustomTimeouts.Update != programGoal.CustomTimeouts.Update:
|
||||
return fmt.Errorf("update timeout changed (expected %v)", rp.Goal.CustomTimeouts.Update)
|
||||
case rp.Goal.CustomTimeouts.Delete != programGoal.CustomTimeouts.Delete:
|
||||
return fmt.Errorf("delete timeout changed (expected %v)", rp.Goal.CustomTimeouts.Delete)
|
||||
}
|
||||
|
||||
// Check that the ignoreChanges sets are identical.
|
||||
if message, changed := rp.diffStrings(rp.Goal.IgnoreChanges, programGoal.IgnoreChanges); changed {
|
||||
return fmt.Errorf("ignoreChanges changed: %v", message)
|
||||
}
|
||||
|
||||
// Check that the additionalSecretOutputs sets are identical.
|
||||
if message, changed := rp.diffPropertyKeys(
|
||||
rp.Goal.AdditionalSecretOutputs, programGoal.AdditionalSecretOutputs); changed {
|
||||
return fmt.Errorf("additionalSecretOutputs changed: %v", message)
|
||||
}
|
||||
|
||||
// Check that the alias sets are identical.
|
||||
if message, changed := rp.diffURNs(rp.Goal.Aliases, programGoal.Aliases); changed {
|
||||
return fmt.Errorf("aliases changed: %v", message)
|
||||
}
|
||||
|
||||
// Check that the dependencies match.
|
||||
if message, changed := rp.diffURNs(rp.Goal.Dependencies, programGoal.Dependencies); changed {
|
||||
return fmt.Errorf("dependencies changed: %v", message)
|
||||
}
|
||||
|
||||
// Check that the property diffs meet the constraints set in the plan.
|
||||
changes := []string{}
|
||||
var diff *resource.ObjectDiff
|
||||
var hasDiff bool
|
||||
if diff, hasDiff = oldOutputs.DiffIncludeUnknowns(newInputs); hasDiff {
|
||||
// Check that any adds are in the goal for adds
|
||||
for k := range diff.Adds {
|
||||
if expected, has := rp.Goal.Adds[k]; has {
|
||||
actual := diff.Adds[k]
|
||||
if !expected.DeepEqualsIncludeUnknowns(actual) {
|
||||
// diff wants to add this with value X but constraint wants to add with value Y
|
||||
changes = append(changes, "+"+string(k))
|
||||
}
|
||||
} else {
|
||||
// diff wants to add this, but not listed as an add in the constraints
|
||||
changes = append(changes, "+"+string(k))
|
||||
}
|
||||
}
|
||||
|
||||
// Check that any removes are in the goal for removes
|
||||
for k := range diff.Deletes {
|
||||
found := false
|
||||
for i := range rp.Goal.Deletes {
|
||||
if rp.Goal.Deletes[i] == k {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// diff wants to delete this, but not listed as a delete in the constraints
|
||||
changes = append(changes, "-"+string(k))
|
||||
}
|
||||
}
|
||||
|
||||
// Check that any changes are in the goal for changes or adds
|
||||
// "or adds" is because if our constraint says to add K=V and someone has already
|
||||
// added K=W we don't consider it a constraint violation to update K to V.
|
||||
// This is similar to how if we have a Create resource constraint we don't consider it
|
||||
// a violation to just update it instead of creating it.
|
||||
for k := range diff.Updates {
|
||||
actual := diff.Updates[k].New
|
||||
if expected, has := rp.Goal.Updates[k]; has {
|
||||
if !expected.DeepEqualsIncludeUnknowns(actual) {
|
||||
// diff wants to change this with value X but constraint wants to change with value Y
|
||||
changes = append(changes, "~"+string(k))
|
||||
}
|
||||
} else if expected, has := rp.Goal.Adds[k]; has {
|
||||
if !expected.DeepEqualsIncludeUnknowns(actual) {
|
||||
// diff wants to change this with value X but constraint wants to add with value Y
|
||||
changes = append(changes, "~"+string(k))
|
||||
}
|
||||
} else {
|
||||
// diff wants to update this, but not listed as an update in the constraints
|
||||
changes = append(changes, "~"+string(k))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No diff, just new up an empty ObjectDiff for checks below
|
||||
diff = &resource.ObjectDiff{}
|
||||
}
|
||||
|
||||
// Symmetric check, check that the constraints didn't expect things to happen that aren't in the new inputs
|
||||
|
||||
for k := range rp.Goal.Adds {
|
||||
// We expected an add, make sure the value is in the new inputs.
|
||||
// That means it's either an add, update, or a same, both are ok for an add constraint.
|
||||
expected := rp.Goal.Adds[k]
|
||||
|
||||
// If this is in diff.Adds or diff.Updates we'll of already checked it
|
||||
_, inAdds := diff.Adds[k]
|
||||
_, inUpdates := diff.Updates[k]
|
||||
|
||||
if !inAdds && !inUpdates {
|
||||
// It wasn't in the diff as an add or update so check we have a same
|
||||
if actual, has := newInputs[k]; has {
|
||||
if !expected.DeepEqualsIncludeUnknowns(actual) {
|
||||
// diff wants to same this with value X but constraint wants to add with value Y
|
||||
changes = append(changes, "~"+string(k))
|
||||
}
|
||||
} else {
|
||||
// Not a same, update or an add but constraint wants to add it
|
||||
changes = append(changes, "-"+string(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k := range rp.Goal.Updates {
|
||||
// We expected an update, make sure the value is in the new inputs as an update (not an add)
|
||||
expected := rp.Goal.Updates[k]
|
||||
|
||||
// If this is in diff.Updates we'll of already checked it
|
||||
_, inUpdates := diff.Updates[k]
|
||||
|
||||
if !inUpdates {
|
||||
// Check if this was in adds, it's not ok to have an update constraint but actually do an add
|
||||
_, inAdds := diff.Adds[k]
|
||||
if inAdds {
|
||||
// Constraint wants to update it, but diff wants to add it
|
||||
changes = append(changes, "+"+string(k))
|
||||
} else if actual, has := newInputs[k]; has {
|
||||
// It wasn't in the diff as an add so check we have a same
|
||||
if !expected.DeepEqualsIncludeUnknowns(actual) {
|
||||
// diff wants to same this with value X but constraint wants to update with value Y
|
||||
changes = append(changes, "~"+string(k))
|
||||
}
|
||||
} else {
|
||||
// Not a same or an update but constraint wants to update it
|
||||
changes = append(changes, "-"+string(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range rp.Goal.Deletes {
|
||||
// We expected a delete, make sure its not present
|
||||
k := rp.Goal.Deletes[i]
|
||||
|
||||
// If this is in diff.Deletes we'll of already checked it
|
||||
_, inDeletes := diff.Deletes[k]
|
||||
if !inDeletes {
|
||||
// See if this is an add, update, or same
|
||||
if _, has := diff.Adds[k]; has {
|
||||
// Constraint wants to delete this but diff wants to add it
|
||||
changes = append(changes, "+"+string(k))
|
||||
} else if _, has := diff.Updates[k]; has {
|
||||
// Constraint wants to delete this but diff wants to update it
|
||||
changes = append(changes, "~"+string(k))
|
||||
} else if _, has := diff.Sames[k]; has {
|
||||
// Constraint wants to delete this but diff wants to leave it same
|
||||
changes = append(changes, "~"+string(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
// Sort changes, mostly so it's easy to write tests against determinstic strings
|
||||
sort.Strings(changes)
|
||||
return fmt.Errorf("properties changed: %v", strings.Join(changes, ", "))
|
||||
}
|
||||
|
||||
// Check that the property dependencies match. Note that because it is legal for a property that is unknown in the
|
||||
// plan to be unset in the program, we allow the omission of a property from the program's dependency set.
|
||||
for k, urns := range rp.Goal.PropertyDependencies {
|
||||
if programDeps, ok := programGoal.PropertyDependencies[k]; ok {
|
||||
if message, changed := rp.diffURNs(urns, programDeps); changed {
|
||||
return fmt.Errorf("dependencies for %v changed: %v", k, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -15,15 +15,12 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
|
||||
"github.com/pulumi/pulumi/pkg/v3/secrets"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
||||
)
|
||||
|
||||
// Snapshot is a view of a collection of resources in an stack at a point in time. It describes resources; their
|
||||
|
@ -36,23 +33,6 @@ type Snapshot struct {
|
|||
PendingOperations []resource.Operation // all currently pending resource operations.
|
||||
}
|
||||
|
||||
// Manifest captures versions for all binaries used to construct this snapshot.
|
||||
type Manifest struct {
|
||||
Time time.Time // the time this snapshot was taken.
|
||||
Magic string // a magic cookie.
|
||||
Version string // the pulumi command version.
|
||||
Plugins []workspace.PluginInfo // the plugin versions also loaded.
|
||||
}
|
||||
|
||||
// NewMagic creates a magic cookie out of a manifest; this can be used to check for tampering. This ignores
|
||||
// any existing magic value already stored on the manifest.
|
||||
func (m Manifest) NewMagic() string {
|
||||
if m.Version == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(m.Version)))
|
||||
}
|
||||
|
||||
// NewSnapshot creates a snapshot from the given arguments. The resources must be in topologically sorted order.
|
||||
// This property is not checked; for verification, please refer to the VerifyIntegrity function below.
|
||||
func NewSnapshot(manifest Manifest, secretsManager secrets.Manager,
|
||||
|
|
|
@ -1166,6 +1166,28 @@ func (op StepOp) Suffix() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// ConstrainedTo returns true if this operation is no more impactful than the constraint.
|
||||
func (op StepOp) ConstrainedTo(constraint StepOp) bool {
|
||||
var allowed []StepOp
|
||||
switch constraint {
|
||||
case OpSame, OpDelete, OpRead, OpReadReplacement, OpRefresh, OpReadDiscard, OpDiscardReplaced,
|
||||
OpRemovePendingReplace, OpImport, OpImportReplacement:
|
||||
allowed = []StepOp{constraint}
|
||||
case OpCreate:
|
||||
allowed = []StepOp{OpSame, OpCreate}
|
||||
case OpUpdate:
|
||||
allowed = []StepOp{OpSame, OpUpdate}
|
||||
case OpReplace, OpCreateReplacement, OpDeleteReplaced:
|
||||
allowed = []StepOp{OpSame, OpUpdate, constraint}
|
||||
}
|
||||
for _, candidate := range allowed {
|
||||
if candidate == op {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getProvider fetches the provider for the given step.
|
||||
func getProvider(s Step) (plugin.Provider, error) {
|
||||
if providers.IsProviderType(s.Type()) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/util/result"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -147,7 +148,7 @@ func (se *stepExecutor) ExecuteParallel(antichain antichain) completionToken {
|
|||
}
|
||||
|
||||
// ExecuteRegisterResourceOutputs services a RegisterResourceOutputsEvent synchronously on the calling goroutine.
|
||||
func (se *stepExecutor) ExecuteRegisterResourceOutputs(e RegisterResourceOutputsEvent) {
|
||||
func (se *stepExecutor) ExecuteRegisterResourceOutputs(e RegisterResourceOutputsEvent) result.Result {
|
||||
// Look up the final state in the pending registration list.
|
||||
urn := e.URN()
|
||||
value, has := se.pendingNews.Load(urn)
|
||||
|
@ -160,7 +161,27 @@ func (se *stepExecutor) ExecuteRegisterResourceOutputs(e RegisterResourceOutputs
|
|||
outs := e.Outputs()
|
||||
se.log(synchronousWorkerID,
|
||||
"registered resource outputs %s: old=#%d, new=#%d", urn, len(reg.New().Outputs), len(outs))
|
||||
reg.New().Outputs = e.Outputs()
|
||||
reg.New().Outputs = outs
|
||||
|
||||
// If a plan is present check that these outputs match what we recorded before
|
||||
if se.deployment.plan != nil {
|
||||
resourcePlan, ok := se.deployment.plan.ResourcePlans[urn]
|
||||
if !ok {
|
||||
return result.FromError(fmt.Errorf("no plan for resource %v", urn))
|
||||
}
|
||||
|
||||
if diffs, has := resourcePlan.Outputs.DiffIncludeUnknowns(outs); has {
|
||||
return result.FromError(fmt.Errorf("resource violates plan: %v", diffs))
|
||||
}
|
||||
}
|
||||
|
||||
// Save these new outputs to the plan
|
||||
if resourcePlan, ok := se.deployment.newPlans.get(urn); ok {
|
||||
resourcePlan.Outputs = outs
|
||||
} else {
|
||||
return result.FromError(fmt.Errorf("this should already have a plan from when we called register resources"))
|
||||
}
|
||||
|
||||
// If there is an event subscription for finishing the resource, execute them.
|
||||
if e := se.opts.Events; e != nil {
|
||||
if eventerr := e.OnResourceOutputs(reg); eventerr != nil {
|
||||
|
@ -176,10 +197,11 @@ func (se *stepExecutor) ExecuteRegisterResourceOutputs(e RegisterResourceOutputs
|
|||
diagMsg := diag.RawMessage(reg.URN(), outErr.Error())
|
||||
se.deployment.Diag().Errorf(diagMsg)
|
||||
se.cancelDueToError()
|
||||
return
|
||||
return nil
|
||||
}
|
||||
}
|
||||
e.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Errored returns whether or not this step executor saw a step whose execution ended in failure.
|
||||
|
@ -295,6 +317,11 @@ func (se *stepExecutor) executeStep(workerID int, step Step) error {
|
|||
if _, hasGoal := se.deployment.goals.get(newState.URN); hasGoal {
|
||||
se.deployment.news.set(newState.URN, newState)
|
||||
}
|
||||
|
||||
// Update the resource's outputs in the generated plan.
|
||||
if resourcePlan, ok := se.deployment.newPlans.get(newState.URN); ok {
|
||||
resourcePlan.Outputs = newState.Outputs
|
||||
}
|
||||
}
|
||||
|
||||
if events != nil {
|
||||
|
|
|
@ -179,6 +179,39 @@ func (sg *stepGenerator) GenerateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
contract.Assert(len(steps) == 0)
|
||||
return nil, res
|
||||
}
|
||||
|
||||
// Check each proposed step against the relevant resource plan, if any
|
||||
for _, s := range steps {
|
||||
if sg.deployment.plan != nil {
|
||||
if resourcePlan, ok := sg.deployment.plan.ResourcePlans[s.URN()]; ok {
|
||||
if len(resourcePlan.Ops) == 0 {
|
||||
return nil, result.Errorf("%v is not allowed by the plan: no more steps were expected for this resource", s.Op())
|
||||
}
|
||||
|
||||
constraint := resourcePlan.Ops[0]
|
||||
if !s.Op().ConstrainedTo(constraint) {
|
||||
return nil, result.Errorf("%v is not allowed by the plan: this resource is constrained to %v", s.Op(), constraint)
|
||||
}
|
||||
resourcePlan.Ops = resourcePlan.Ops[1:]
|
||||
} else {
|
||||
if !s.Op().ConstrainedTo(OpSame) {
|
||||
return nil, result.Errorf("%v is not allowed by the plan: no steps were expected for this resource", s.Op())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resource plan might be aliased
|
||||
urn, isAliased := sg.aliased[s.URN()]
|
||||
if !isAliased {
|
||||
urn = s.URN()
|
||||
}
|
||||
resourcePlan, ok := sg.deployment.newPlans.get(urn)
|
||||
if !ok {
|
||||
return nil, result.Errorf("Expected a new resource plan for %v", urn)
|
||||
}
|
||||
resourcePlan.Ops = append(resourcePlan.Ops, s.Op())
|
||||
}
|
||||
|
||||
if !sg.isTargetedUpdate() {
|
||||
return steps, nil
|
||||
}
|
||||
|
@ -272,6 +305,29 @@ func (sg *stepGenerator) generateSteps(event RegisterResourceEvent) ([]Step, res
|
|||
inputs = processedInputs
|
||||
}
|
||||
|
||||
// Generate the output goal plan
|
||||
// TODO(pdg-plan): using the program inputs means that non-determinism could sneak in as part of default
|
||||
// application. However, it is necessary in the face of computed inputs.
|
||||
newResourcePlan := &ResourcePlan{Goal: NewGoalPlan(oldOutputs, goal)}
|
||||
sg.deployment.newPlans.set(urn, newResourcePlan)
|
||||
|
||||
// If there is a plan for this resource, validate that the program goal conforms to the plan.
|
||||
// If theres no plan for this resource check that nothing has been changed.
|
||||
if sg.deployment.plan != nil {
|
||||
resourcePlan, ok := sg.deployment.plan.ResourcePlans[urn]
|
||||
if !ok {
|
||||
if old == nil {
|
||||
// We could error here, but we'll trigger an error later on anyway that Create isn't valid here
|
||||
} else if err := checkMissingPlan(old, inputs, goal); err != nil {
|
||||
return nil, result.FromError(fmt.Errorf("resource violates plan: %w", err))
|
||||
}
|
||||
} else {
|
||||
if err := resourcePlan.checkGoal(oldOutputs, inputs, goal); err != nil {
|
||||
return nil, result.FromError(fmt.Errorf("resource violates plan: %w", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Produce a new state object that we'll build up as operations are performed. Ultimately, this is what will
|
||||
// get serialized into the checkpoint file.
|
||||
new := resource.NewState(goal.Type, urn, goal.Custom, false, "", inputs, nil, goal.Parent, goal.Protect, false,
|
||||
|
@ -646,6 +702,13 @@ func (sg *stepGenerator) generateStepsFromDiff(
|
|||
continue
|
||||
}
|
||||
|
||||
if _, ok := sg.deployment.newPlans.get(dependentResource.URN); !ok {
|
||||
// We haven't see this resource before, create a new
|
||||
// resource plan for it with no goal (because it's going to be a delete)
|
||||
resourcePlan := &ResourcePlan{}
|
||||
sg.deployment.newPlans.set(dependentResource.URN, resourcePlan)
|
||||
}
|
||||
|
||||
sg.dependentReplaceKeys[dependentResource.URN] = toReplace[i].keys
|
||||
|
||||
logging.V(7).Infof("Planner decided to delete '%v' due to dependence on condemned resource '%v'",
|
||||
|
@ -751,6 +814,40 @@ func (sg *stepGenerator) GenerateDeletes(targetsOpt map[resource.URN]bool) ([]St
|
|||
}
|
||||
}
|
||||
|
||||
// Check each proposed delete against the relevant resource plan
|
||||
for _, s := range dels {
|
||||
if sg.deployment.plan != nil {
|
||||
if resourcePlan, ok := sg.deployment.plan.ResourcePlans[s.URN()]; ok {
|
||||
if len(resourcePlan.Ops) == 0 {
|
||||
return nil, result.Errorf("%v is not allowed by the plan: no more steps were expected for this resource", s.Op())
|
||||
}
|
||||
|
||||
constraint := resourcePlan.Ops[0]
|
||||
// We remove the Op from the list before doing the constraint check.
|
||||
// This is because we look at Ops at the end to see if any expected operations didn't attempt to happen.
|
||||
// This op has been attempted, it just might fail its constraint.
|
||||
resourcePlan.Ops = resourcePlan.Ops[1:]
|
||||
|
||||
if !s.Op().ConstrainedTo(constraint) {
|
||||
return nil, result.Errorf("%v is not allowed by the plan: this resource is constrained to %v", s.Op(), constraint)
|
||||
}
|
||||
} else {
|
||||
if !s.Op().ConstrainedTo(OpSame) {
|
||||
return nil, result.Errorf("%v is not allowed by the plan: no steps were expected for this resource", s.Op())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resourcePlan, ok := sg.deployment.newPlans.get(s.URN())
|
||||
if !ok {
|
||||
// TODO(pdg-plan): using the program inputs means that non-determinism could sneak in as part of default
|
||||
// application. However, it is necessary in the face of computed inputs.
|
||||
resourcePlan = &ResourcePlan{}
|
||||
sg.deployment.newPlans.set(s.URN(), resourcePlan)
|
||||
}
|
||||
resourcePlan.Ops = append(resourcePlan.Ops, s.Op())
|
||||
}
|
||||
|
||||
// If -target was provided to either `pulumi update` or `pulumi destroy` then only delete
|
||||
// resources that were specified.
|
||||
allowedResourcesToDelete, res := sg.determineAllowedResourcesToDeleteFromTargets(targetsOpt)
|
||||
|
|
|
@ -23,8 +23,6 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
|
||||
"github.com/pulumi/pulumi/pkg/v3/secrets"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
||||
|
@ -32,7 +30,6 @@ import (
|
|||
"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/util/contract"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
)
|
||||
|
||||
|
@ -105,23 +102,7 @@ func SerializeDeployment(snap *deploy.Snapshot, sm secrets.Manager, showSecrets
|
|||
contract.Require(snap != nil, "snap")
|
||||
|
||||
// Capture the version information into a manifest.
|
||||
manifest := apitype.ManifestV1{
|
||||
Time: snap.Manifest.Time,
|
||||
Magic: snap.Manifest.Magic,
|
||||
Version: snap.Manifest.Version,
|
||||
}
|
||||
for _, plug := range snap.Manifest.Plugins {
|
||||
var version string
|
||||
if plug.Version != nil {
|
||||
version = plug.Version.String()
|
||||
}
|
||||
manifest.Plugins = append(manifest.Plugins, apitype.PluginInfoV1{
|
||||
Name: plug.Name,
|
||||
Path: plug.Path,
|
||||
Type: plug.Kind,
|
||||
Version: version,
|
||||
})
|
||||
}
|
||||
manifest := snap.Manifest.Serialize()
|
||||
|
||||
// If a specific secrets manager was not provided, use the one in the snapshot, if present.
|
||||
if sm == nil {
|
||||
|
@ -223,25 +204,9 @@ func DeserializeUntypedDeployment(
|
|||
// DeserializeDeploymentV3 deserializes a typed DeploymentV3 into a `deploy.Snapshot`.
|
||||
func DeserializeDeploymentV3(deployment apitype.DeploymentV3, secretsProv SecretsProvider) (*deploy.Snapshot, error) {
|
||||
// Unpack the versions.
|
||||
manifest := deploy.Manifest{
|
||||
Time: deployment.Manifest.Time,
|
||||
Magic: deployment.Manifest.Magic,
|
||||
Version: deployment.Manifest.Version,
|
||||
}
|
||||
for _, plug := range deployment.Manifest.Plugins {
|
||||
var version *semver.Version
|
||||
if v := plug.Version; v != "" {
|
||||
sv, err := semver.ParseTolerant(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version = &sv
|
||||
}
|
||||
manifest.Plugins = append(manifest.Plugins, workspace.PluginInfo{
|
||||
Name: plug.Name,
|
||||
Kind: plug.Type,
|
||||
Version: version,
|
||||
})
|
||||
manifest, err := deploy.DeserializeManifest(deployment.Manifest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var secretsManager secrets.Manager
|
||||
|
@ -295,7 +260,7 @@ func DeserializeDeploymentV3(deployment apitype.DeploymentV3, secretsProv Secret
|
|||
ops = append(ops, desop)
|
||||
}
|
||||
|
||||
return deploy.NewSnapshot(manifest, secretsManager, resources, ops), nil
|
||||
return deploy.NewSnapshot(*manifest, secretsManager, resources, ops), nil
|
||||
}
|
||||
|
||||
// SerializeResource turns a resource into a structure suitable for serialization.
|
||||
|
|
162
pkg/resource/stack/plan.go
Normal file
162
pkg/resource/stack/plan.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
|
||||
)
|
||||
|
||||
func SerializeResourcePlan(
|
||||
plan *deploy.ResourcePlan,
|
||||
enc config.Encrypter,
|
||||
showSecrets bool) (apitype.ResourcePlanV1, error) {
|
||||
|
||||
adds, err := SerializeProperties(plan.Goal.Adds, enc, showSecrets)
|
||||
if err != nil {
|
||||
return apitype.ResourcePlanV1{}, err
|
||||
}
|
||||
|
||||
updates, err := SerializeProperties(plan.Goal.Adds, enc, showSecrets)
|
||||
if err != nil {
|
||||
return apitype.ResourcePlanV1{}, err
|
||||
}
|
||||
|
||||
deletes := make([]string, len(plan.Goal.Deletes))
|
||||
for i := range deletes {
|
||||
deletes[i] = string(plan.Goal.Deletes[i])
|
||||
}
|
||||
|
||||
var outputs map[string]interface{}
|
||||
if plan.Outputs != nil {
|
||||
outs, err := SerializeProperties(plan.Outputs, enc, showSecrets)
|
||||
if err != nil {
|
||||
return apitype.ResourcePlanV1{}, err
|
||||
}
|
||||
outputs = outs
|
||||
}
|
||||
|
||||
goal := apitype.GoalV1{
|
||||
Type: plan.Goal.Type,
|
||||
Name: plan.Goal.Name,
|
||||
Custom: plan.Goal.Custom,
|
||||
Adds: adds,
|
||||
Deletes: deletes,
|
||||
Updates: updates,
|
||||
Parent: plan.Goal.Parent,
|
||||
Protect: plan.Goal.Protect,
|
||||
Dependencies: plan.Goal.Dependencies,
|
||||
Provider: plan.Goal.Provider,
|
||||
PropertyDependencies: plan.Goal.PropertyDependencies,
|
||||
DeleteBeforeReplace: plan.Goal.DeleteBeforeReplace,
|
||||
IgnoreChanges: plan.Goal.IgnoreChanges,
|
||||
AdditionalSecretOutputs: plan.Goal.AdditionalSecretOutputs,
|
||||
Aliases: plan.Goal.Aliases,
|
||||
ID: plan.Goal.ID,
|
||||
CustomTimeouts: plan.Goal.CustomTimeouts,
|
||||
}
|
||||
|
||||
steps := make([]apitype.OpType, len(plan.Ops))
|
||||
for i, op := range plan.Ops {
|
||||
steps[i] = apitype.OpType(op)
|
||||
}
|
||||
|
||||
return apitype.ResourcePlanV1{
|
||||
Goal: goal,
|
||||
Steps: steps,
|
||||
Outputs: outputs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SerializePlan(plan *deploy.Plan, enc config.Encrypter, showSecrets bool) (apitype.DeploymentPlanV1, error) {
|
||||
resourcePlans := map[resource.URN]apitype.ResourcePlanV1{}
|
||||
for urn, plan := range plan.ResourcePlans {
|
||||
serializedPlan, err := SerializeResourcePlan(plan, enc, showSecrets)
|
||||
if err != nil {
|
||||
return apitype.DeploymentPlanV1{}, err
|
||||
}
|
||||
resourcePlans[urn] = serializedPlan
|
||||
}
|
||||
|
||||
return apitype.DeploymentPlanV1{
|
||||
Manifest: plan.Manifest.Serialize(),
|
||||
ResourcePlans: resourcePlans,
|
||||
Config: plan.Config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DeserializeResourcePlan(
|
||||
plan apitype.ResourcePlanV1,
|
||||
dec config.Decrypter,
|
||||
enc config.Encrypter) (*deploy.ResourcePlan, error) {
|
||||
|
||||
adds, err := DeserializeProperties(plan.Goal.Adds, dec, enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updates, err := DeserializeProperties(plan.Goal.Updates, dec, enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var outputs resource.PropertyMap
|
||||
if plan.Outputs != nil {
|
||||
outs, err := DeserializeProperties(plan.Outputs, dec, enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputs = outs
|
||||
}
|
||||
|
||||
goal := &deploy.GoalPlan{
|
||||
Type: plan.Goal.Type,
|
||||
Name: plan.Goal.Name,
|
||||
Custom: plan.Goal.Custom,
|
||||
Adds: adds,
|
||||
Deletes: nil,
|
||||
Updates: updates,
|
||||
Parent: plan.Goal.Parent,
|
||||
Protect: plan.Goal.Protect,
|
||||
Dependencies: plan.Goal.Dependencies,
|
||||
Provider: plan.Goal.Provider,
|
||||
PropertyDependencies: plan.Goal.PropertyDependencies,
|
||||
DeleteBeforeReplace: plan.Goal.DeleteBeforeReplace,
|
||||
IgnoreChanges: plan.Goal.IgnoreChanges,
|
||||
AdditionalSecretOutputs: plan.Goal.AdditionalSecretOutputs,
|
||||
Aliases: plan.Goal.Aliases,
|
||||
ID: plan.Goal.ID,
|
||||
CustomTimeouts: plan.Goal.CustomTimeouts,
|
||||
}
|
||||
|
||||
ops := make([]deploy.StepOp, len(plan.Steps))
|
||||
for i, op := range plan.Steps {
|
||||
ops[i] = deploy.StepOp(op)
|
||||
}
|
||||
|
||||
return &deploy.ResourcePlan{
|
||||
Goal: goal,
|
||||
Ops: ops,
|
||||
Outputs: outputs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DeserializePlan(plan apitype.DeploymentPlanV1, dec config.Decrypter, enc config.Encrypter) (*deploy.Plan, error) {
|
||||
manifest, err := deploy.DeserializeManifest(plan.Manifest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deserializedPlan := &deploy.Plan{
|
||||
Config: plan.Config,
|
||||
Manifest: *manifest,
|
||||
}
|
||||
for urn, resourcePlan := range plan.ResourcePlans {
|
||||
deserializedResourcePlan, err := DeserializeResourcePlan(resourcePlan, dec, enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deserializedPlan.ResourcePlans[urn] = deserializedResourcePlan
|
||||
}
|
||||
return deserializedPlan, nil
|
||||
}
|
|
@ -62,6 +62,7 @@ require (
|
|||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.3 // indirect
|
||||
github.com/src-d/gcfg v1.4.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.2.1 // indirect
|
||||
|
|
79
sdk/go/common/apitype/plan.go
Normal file
79
sdk/go/common/apitype/plan.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package apitype
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// GoalV1 is the serializable version of a resource goal state.
|
||||
type GoalV1 struct {
|
||||
// the type of resource.
|
||||
Type tokens.Type `json:"type"`
|
||||
// the name for the resource's URN.
|
||||
Name tokens.QName `json:"name"`
|
||||
// true if this resource is custom, managed by a plugin.
|
||||
Custom bool `json:"custom"`
|
||||
// the resource properties that will be added.
|
||||
Adds map[string]interface{} `json:"adds,omitempty"`
|
||||
// the resource properties that will be deleted.
|
||||
Deletes []string `json:"deletes,omitempty"`
|
||||
// the resource properties that will be updated.
|
||||
Updates map[string]interface{} `json:"updates,omitempty"`
|
||||
// an optional parent URN for this resource.
|
||||
Parent resource.URN `json:"parent,omitempty"`
|
||||
// true to protect this resource from deletion.
|
||||
Protect bool `json:"protect"`
|
||||
// dependencies of this resource object.
|
||||
Dependencies []resource.URN `json:"dependencies,omitempty"`
|
||||
// the provider to use for this resource.
|
||||
Provider string `json:"provider,omitempty"`
|
||||
// the set of dependencies that affect each property.
|
||||
PropertyDependencies map[resource.PropertyKey][]resource.URN `json:"propertyDependencies,omitempty"`
|
||||
// true if this resource should be deleted prior to replacement.
|
||||
DeleteBeforeReplace *bool `json:"deleteBeforeReplace,omitempty"`
|
||||
// a list of property names to ignore during changes.
|
||||
IgnoreChanges []string `json:"ignoreChanges,omitempty"`
|
||||
// outputs that should always be treated as secrets.
|
||||
AdditionalSecretOutputs []resource.PropertyKey `json:"additionalSecretOutputs,omitempty"`
|
||||
// additional URNs that should be aliased to this resource.
|
||||
Aliases []resource.URN `json:"aliases,omitempty"`
|
||||
// the expected ID of the resource, if any.
|
||||
ID resource.ID `json:"id,omitempty"`
|
||||
// an optional config object for resource options
|
||||
CustomTimeouts resource.CustomTimeouts `json:"customTimeouts,omitempty"`
|
||||
}
|
||||
|
||||
// ResourcePlanV1 is the serializable version of a resource plan.
|
||||
type ResourcePlanV1 struct {
|
||||
// The goal state for the resource.
|
||||
Goal GoalV1 `json:"goal"`
|
||||
// The steps to be performed on the resource.
|
||||
Steps []OpType `json:"steps,omitempty"`
|
||||
// The proposed outputs for the resource, if any. Purely advisory.
|
||||
Outputs map[string]interface{} `json:"state"`
|
||||
}
|
||||
|
||||
// VersionedDeploymentPlan is a version number plus a JSON document. The version number describes what
|
||||
// version of the DeploymentPlan structure the DeploymentPlan member's JSON document can decode into.
|
||||
type VersionedDeploymentPlan struct {
|
||||
Version int `json:"version"`
|
||||
Plan json.RawMessage `json:"plan"`
|
||||
}
|
||||
|
||||
// DeploymentPlanV1 is the serializable version of a deployment plan.
|
||||
type DeploymentPlanV1 struct {
|
||||
// TODO(pdg-plan): should there be a message here?
|
||||
|
||||
// Manifest contains metadata about this plan.
|
||||
Manifest ManifestV1 `json:"manifest" yaml:"manifest"`
|
||||
// Any environment variables that were set when the plan was created. Values are encrypted.
|
||||
EnvironmentVariables map[string][]byte `json:"environmentVariables,omitempty"`
|
||||
// The configuration in use during the plan.
|
||||
Config config.Map `json:"config,omitempty"`
|
||||
|
||||
// The set of resource plans.
|
||||
ResourcePlans map[resource.URN]ResourcePlanV1 `json:"resourcePlans,omitempty"`
|
||||
}
|
|
@ -646,3 +646,71 @@ const OutputValueSig = "d0e6a833031e9bbcd3f4e8bde6ca49a4"
|
|||
func IsInternalPropertyKey(key PropertyKey) bool {
|
||||
return strings.HasPrefix(string(key), "__")
|
||||
}
|
||||
|
||||
// DeepCopy creates a new copy of this property map.
|
||||
func (m PropertyMap) DeepCopy() PropertyMap {
|
||||
copy := PropertyMap{}
|
||||
for k, v := range m {
|
||||
copy[k] = v.DeepCopy()
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
// DeepCopy creates a new copy of this property.
|
||||
func (v PropertyValue) DeepCopy() PropertyValue {
|
||||
switch {
|
||||
case v.IsArray():
|
||||
copy := make([]PropertyValue, len(v.ArrayValue()))
|
||||
for i, v := range v.ArrayValue() {
|
||||
copy[i] = v.DeepCopy()
|
||||
}
|
||||
return NewArrayProperty(copy)
|
||||
case v.IsObject():
|
||||
return NewObjectProperty(v.ObjectValue().DeepCopy())
|
||||
case v.IsSecret():
|
||||
return MakeSecret(v.SecretValue().Element.DeepCopy())
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// FindUnknowns returns the set of paths to unknown values nested inside this property map.
|
||||
func (m PropertyMap) FindUnknowns() []PropertyPath {
|
||||
var paths []PropertyPath
|
||||
for k, v := range m {
|
||||
if v.IsComputed() || v.IsOutput() {
|
||||
paths = append(paths, PropertyPath{string(k)})
|
||||
} else {
|
||||
for _, p := range v.FindUnknowns() {
|
||||
p = append(PropertyPath{string(k)}, p...)
|
||||
paths = append(paths, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// FindUnknowns returns the set of paths to unknown values nested inside this property map.
|
||||
func (v PropertyValue) FindUnknowns() []PropertyPath {
|
||||
switch {
|
||||
case v.IsArray():
|
||||
var paths []PropertyPath
|
||||
for i, v := range v.ArrayValue() {
|
||||
if v.IsComputed() || v.IsOutput() {
|
||||
paths = append(paths, PropertyPath{i})
|
||||
} else {
|
||||
for _, p := range v.FindUnknowns() {
|
||||
p = append(PropertyPath{i}, p...)
|
||||
paths = append(paths, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths
|
||||
case v.IsObject():
|
||||
return v.ObjectValue().FindUnknowns()
|
||||
case v.IsSecret():
|
||||
return v.SecretValue().Element.FindUnknowns()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ func (diff *ObjectDiff) Keys() []PropertyKey {
|
|||
type ValueDiff struct {
|
||||
Old PropertyValue // the old value.
|
||||
New PropertyValue // the new value.
|
||||
Secret *ValueDiff // the secret value diff (only for secrets)
|
||||
Array *ArrayDiff // the array's detailed diffs (only for arrays).
|
||||
Object *ObjectDiff // the object's detailed diffs (only for objects).
|
||||
}
|
||||
|
@ -119,8 +120,7 @@ func (diff *ArrayDiff) Len() int {
|
|||
// IgnoreKeyFunc is the callback type for Diff's ignore option.
|
||||
type IgnoreKeyFunc func(key PropertyKey) bool
|
||||
|
||||
// Diff returns a diffset by comparing the property map to another; it returns nil if there are no diffs.
|
||||
func (props PropertyMap) Diff(other PropertyMap, ignoreKeys ...IgnoreKeyFunc) *ObjectDiff {
|
||||
func (props PropertyMap) diff(other PropertyMap, ignoreUnknowns bool, ignoreKeys []IgnoreKeyFunc) *ObjectDiff {
|
||||
adds := make(PropertyMap)
|
||||
deletes := make(PropertyMap)
|
||||
sames := make(PropertyMap)
|
||||
|
@ -137,7 +137,7 @@ func (props PropertyMap) Diff(other PropertyMap, ignoreKeys ...IgnoreKeyFunc) *O
|
|||
|
||||
// First find any updates or deletes.
|
||||
for k, old := range props {
|
||||
if ignore(k) {
|
||||
if ignore(k) || ignoreUnknowns && (old.IsComputed() || old.IsOutput()) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ func (props PropertyMap) Diff(other PropertyMap, ignoreKeys ...IgnoreKeyFunc) *O
|
|||
// If a new exists, use it; for output properties, however, ignore differences.
|
||||
if new.IsOutput() {
|
||||
sames[k] = old
|
||||
} else if diff := old.Diff(new, ignoreKeys...); diff != nil {
|
||||
} else if diff := old.diff(new, ignoreUnknowns, ignoreKeys); diff != nil {
|
||||
if !old.HasValue() {
|
||||
adds[k] = new
|
||||
} else if !new.HasValue() {
|
||||
|
@ -156,7 +156,7 @@ func (props PropertyMap) Diff(other PropertyMap, ignoreKeys ...IgnoreKeyFunc) *O
|
|||
} else {
|
||||
sames[k] = old
|
||||
}
|
||||
} else if old.HasValue() {
|
||||
} else if old.HasValue() && (!ignoreUnknowns || !old.IsComputed()) {
|
||||
// If there was no new property, it has been deleted.
|
||||
deletes[k] = old
|
||||
}
|
||||
|
@ -185,8 +185,12 @@ func (props PropertyMap) Diff(other PropertyMap, ignoreKeys ...IgnoreKeyFunc) *O
|
|||
}
|
||||
}
|
||||
|
||||
// Diff returns a diff by comparing a single property value to another; it returns nil if there are no diffs.
|
||||
func (v PropertyValue) Diff(other PropertyValue, ignoreKeys ...IgnoreKeyFunc) *ValueDiff {
|
||||
// Diff returns a diffset by comparing the property map to another; it returns nil if there are no diffs.
|
||||
func (props PropertyMap) Diff(other PropertyMap, ignoreKeys ...IgnoreKeyFunc) *ObjectDiff {
|
||||
return props.diff(other, false, ignoreKeys)
|
||||
}
|
||||
|
||||
func (v PropertyValue) diff(other PropertyValue, ignoreUnknowns bool, ignoreKeys []IgnoreKeyFunc) *ValueDiff {
|
||||
if v.IsArray() && other.IsArray() {
|
||||
old := v.ArrayValue()
|
||||
new := other.ArrayValue()
|
||||
|
@ -204,7 +208,7 @@ func (v PropertyValue) Diff(other PropertyValue, ignoreKeys ...IgnoreKeyFunc) *V
|
|||
sames := make(map[int]PropertyValue)
|
||||
updates := make(map[int]ValueDiff)
|
||||
for i := 0; i < len(old) && i < len(new); i++ {
|
||||
if diff := old[i].Diff(new[i]); diff != nil {
|
||||
if diff := old[i].diff(new[i], ignoreUnknowns, ignoreKeys); diff != nil {
|
||||
updates[i] = *diff
|
||||
} else {
|
||||
sames[i] = old[i]
|
||||
|
@ -228,7 +232,7 @@ func (v PropertyValue) Diff(other PropertyValue, ignoreKeys ...IgnoreKeyFunc) *V
|
|||
if v.IsObject() && other.IsObject() {
|
||||
old := v.ObjectValue()
|
||||
new := other.ObjectValue()
|
||||
if diff := old.Diff(new, ignoreKeys...); diff != nil {
|
||||
if diff := old.diff(new, ignoreUnknowns, ignoreKeys); diff != nil {
|
||||
return &ValueDiff{
|
||||
Old: v,
|
||||
New: other,
|
||||
|
@ -237,21 +241,51 @@ func (v PropertyValue) Diff(other PropertyValue, ignoreKeys ...IgnoreKeyFunc) *V
|
|||
}
|
||||
return nil
|
||||
}
|
||||
if v.IsSecret() && other.IsSecret() {
|
||||
old, new := v.SecretValue().Element, other.SecretValue().Element
|
||||
diff := old.diff(new, ignoreUnknowns, ignoreKeys)
|
||||
if diff != nil {
|
||||
return &ValueDiff{
|
||||
Old: v,
|
||||
New: other,
|
||||
Secret: diff,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if v.IsResourceReference() && other.IsResourceReference() {
|
||||
vr := v.ResourceReferenceValue()
|
||||
or := other.ResourceReferenceValue()
|
||||
|
||||
if vr.URN != or.URN {
|
||||
return &ValueDiff{Old: v, New: other}
|
||||
}
|
||||
|
||||
vid, oid := vr.ID, or.ID
|
||||
if vid.IsComputed() && ignoreUnknowns || vid.DeepEquals(oid) {
|
||||
return nil
|
||||
}
|
||||
return &ValueDiff{Old: v, New: other}
|
||||
}
|
||||
|
||||
// If we got here, either the values are primitives, or they weren't the same type; do a simple diff.
|
||||
if v.DeepEquals(other) {
|
||||
if v.DeepEquals(other) || ignoreUnknowns && (v.IsComputed() || v.IsOutput()) {
|
||||
return nil
|
||||
}
|
||||
return &ValueDiff{Old: v, New: other}
|
||||
}
|
||||
|
||||
// DeepEquals returns true if this property map is deeply equal to the other property map; and false otherwise.
|
||||
func (props PropertyMap) DeepEquals(other PropertyMap) bool {
|
||||
// Diff returns a diff by comparing a single property value to another; it returns nil if there are no diffs.
|
||||
func (v PropertyValue) Diff(other PropertyValue, ignoreKeys ...IgnoreKeyFunc) *ValueDiff {
|
||||
return v.diff(other, false, ignoreKeys)
|
||||
}
|
||||
|
||||
func (props PropertyMap) deepEquals(other PropertyMap, includeUnknowns bool) bool {
|
||||
// If any in props either doesn't exist, or is of a different value, return false.
|
||||
for _, k := range props.StableKeys() {
|
||||
v := props[k]
|
||||
if p, has := other[k]; has {
|
||||
if !v.DeepEquals(p) {
|
||||
if !v.deepEquals(p, includeUnknowns) {
|
||||
return false
|
||||
}
|
||||
} else if v.HasValue() {
|
||||
|
@ -269,8 +303,19 @@ func (props PropertyMap) DeepEquals(other PropertyMap) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// DeepEquals returns true if this property map is deeply equal to the other property map; and false otherwise.
|
||||
func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
||||
func (v PropertyValue) deepEquals(other PropertyValue, includeUnknowns bool) bool {
|
||||
// Computed values are always equal.
|
||||
if v.IsComputed() && other.IsComputed() {
|
||||
return true
|
||||
}
|
||||
|
||||
// If includeUnknowns is true then anything is equal to a computed
|
||||
if includeUnknowns {
|
||||
if v.IsComputed() || other.IsComputed() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Arrays are equal if they are both of the same size and elements are deeply equal.
|
||||
if v.IsArray() {
|
||||
if !other.IsArray() {
|
||||
|
@ -282,7 +327,7 @@ func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
|||
return false
|
||||
}
|
||||
for i, elem := range va {
|
||||
if !elem.DeepEquals(oa[i]) {
|
||||
if !elem.deepEquals(oa[i], includeUnknowns) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -309,7 +354,7 @@ func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
|||
}
|
||||
vo := v.ObjectValue()
|
||||
oa := other.ObjectValue()
|
||||
return vo.DeepEquals(oa)
|
||||
return vo.deepEquals(oa, includeUnknowns)
|
||||
}
|
||||
|
||||
// Secret are equal if the value they wrap are equal.
|
||||
|
@ -320,7 +365,7 @@ func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
|||
vs := v.SecretValue()
|
||||
os := other.SecretValue()
|
||||
|
||||
return vs.Element.DeepEquals(os.Element)
|
||||
return vs.Element.deepEquals(os.Element, includeUnknowns)
|
||||
}
|
||||
|
||||
// Resource references are equal if they refer to the same resource. The package version is ignored.
|
||||
|
@ -339,7 +384,7 @@ func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
|||
if vid.IsComputed() && oid.IsComputed() {
|
||||
return true
|
||||
}
|
||||
return vid.DeepEquals(oid)
|
||||
return vid.deepEquals(oid, includeUnknowns)
|
||||
}
|
||||
|
||||
// Outputs are equal if each of their fields is deeply equal.
|
||||
|
@ -367,9 +412,28 @@ func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
|||
}
|
||||
}
|
||||
|
||||
return vo.Element.DeepEquals(oo.Element)
|
||||
return vo.Element.deepEquals(oo.Element, includeUnknowns)
|
||||
}
|
||||
|
||||
// For all other cases, primitives are equal if their values are equal.
|
||||
return v.V == other.V
|
||||
}
|
||||
|
||||
// DeepEquals returns true if this property map is deeply equal to the other property map; and false otherwise.
|
||||
func (props PropertyMap) DeepEquals(other PropertyMap) bool {
|
||||
return props.deepEquals(other, false)
|
||||
}
|
||||
|
||||
// DeepEquals returns true if this property value is deeply equal to the other property value; and false otherwise.
|
||||
func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
||||
return v.deepEquals(other, false)
|
||||
}
|
||||
|
||||
func (props PropertyMap) DiffIncludeUnknowns(other PropertyMap) (*ObjectDiff, bool) {
|
||||
diff := props.diff(other, true, nil)
|
||||
return diff, diff != nil
|
||||
}
|
||||
|
||||
func (v PropertyValue) DeepEqualsIncludeUnknowns(other PropertyValue) bool {
|
||||
return v.deepEquals(other, true)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -291,3 +293,39 @@ func (p PropertyPath) Contains(other PropertyPath) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func requiresQuote(c rune) bool {
|
||||
return !(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_')
|
||||
}
|
||||
|
||||
func (p PropertyPath) String() string {
|
||||
var buf bytes.Buffer
|
||||
for i, k := range p {
|
||||
switch k := k.(type) {
|
||||
case string:
|
||||
var keyBuf bytes.Buffer
|
||||
quoted := false
|
||||
for _, c := range k {
|
||||
if requiresQuote(c) {
|
||||
quoted = true
|
||||
if c == '"' {
|
||||
keyBuf.WriteByte('\\')
|
||||
}
|
||||
}
|
||||
keyBuf.WriteRune(c)
|
||||
}
|
||||
if !quoted {
|
||||
if i == 0 {
|
||||
fmt.Fprintf(&buf, "%s", keyBuf.String())
|
||||
} else {
|
||||
fmt.Fprintf(&buf, ".%s", keyBuf.String())
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(&buf, `["%s"]`, keyBuf.String())
|
||||
}
|
||||
case int:
|
||||
fmt.Fprintf(&buf, "[%d]", k)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
|
|
@ -49,68 +49,84 @@ func TestPropertyPath(t *testing.T) {
|
|||
}))
|
||||
|
||||
cases := []struct {
|
||||
path string
|
||||
parsed PropertyPath
|
||||
path string
|
||||
parsed PropertyPath
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"root",
|
||||
PropertyPath{"root"},
|
||||
"root",
|
||||
},
|
||||
{
|
||||
"root.nested",
|
||||
PropertyPath{"root", "nested"},
|
||||
"root.nested",
|
||||
},
|
||||
{
|
||||
`root["nested"]`,
|
||||
PropertyPath{"root", "nested"},
|
||||
`root.nested`,
|
||||
},
|
||||
{
|
||||
"root.double.nest",
|
||||
PropertyPath{"root", "double", "nest"},
|
||||
"root.double.nest",
|
||||
},
|
||||
{
|
||||
`root["double"].nest`,
|
||||
PropertyPath{"root", "double", "nest"},
|
||||
`root.double.nest`,
|
||||
},
|
||||
{
|
||||
`root["double"]["nest"]`,
|
||||
PropertyPath{"root", "double", "nest"},
|
||||
`root.double.nest`,
|
||||
},
|
||||
{
|
||||
"root.array[0]",
|
||||
PropertyPath{"root", "array", 0},
|
||||
"root.array[0]",
|
||||
},
|
||||
{
|
||||
"root.array[1]",
|
||||
PropertyPath{"root", "array", 1},
|
||||
"root.array[1]",
|
||||
},
|
||||
{
|
||||
"root.array[0].nested",
|
||||
PropertyPath{"root", "array", 0, "nested"},
|
||||
"root.array[0].nested",
|
||||
},
|
||||
{
|
||||
"root.array2[0][1].nested",
|
||||
PropertyPath{"root", "array2", 0, 1, "nested"},
|
||||
"root.array2[0][1].nested",
|
||||
},
|
||||
{
|
||||
"root.nested.array[0].double[1]",
|
||||
PropertyPath{"root", "nested", "array", 0, "double", 1},
|
||||
"root.nested.array[0].double[1]",
|
||||
},
|
||||
{
|
||||
`root["key with \"escaped\" quotes"]`,
|
||||
PropertyPath{"root", `key with "escaped" quotes`},
|
||||
`root["key with \"escaped\" quotes"]`,
|
||||
},
|
||||
{
|
||||
`root["key with a ."]`,
|
||||
PropertyPath{"root", "key with a ."},
|
||||
`root["key with a ."]`,
|
||||
},
|
||||
{
|
||||
`["root key with \"escaped\" quotes"].nested`,
|
||||
PropertyPath{`root key with "escaped" quotes`, "nested"},
|
||||
`["root key with \"escaped\" quotes"].nested`,
|
||||
},
|
||||
{
|
||||
`["root key with a ."][1]`,
|
||||
PropertyPath{"root key with a .", 1},
|
||||
`["root key with a ."][1]`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -119,6 +135,7 @@ func TestPropertyPath(t *testing.T) {
|
|||
parsed, err := ParsePropertyPath(c.path)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.parsed, parsed)
|
||||
assert.Equal(t, c.expected, parsed.String())
|
||||
|
||||
v, ok := parsed.Get(value)
|
||||
assert.True(t, ok)
|
||||
|
|
Loading…
Reference in a new issue