Validate outputs don't change
This commit is contained in:
parent
76f3e938aa
commit
202c0f0ae4
|
@ -3379,3 +3379,50 @@ func TestPlannedUpdateChangedStack(t *testing.T) {
|
|||
})
|
||||
assert.Equal(t, expected, snap.Resources[1].Outputs)
|
||||
}
|
||||
|
||||
func TestPlannedOutputChanges(t *testing.T) {
|
||||
loaders := []*deploytest.ProviderLoader{
|
||||
deploytest.NewProviderLoader("pkgA", semver.MustParse("1.0.0"), func() (plugin.Provider, error) {
|
||||
return &deploytest.Provider{
|
||||
CreateF: func(urn resource.URN, news resource.PropertyMap, timeout float64,
|
||||
preview bool) (resource.ID, resource.PropertyMap, resource.Status, error) {
|
||||
return resource.ID("created-id-" + urn.Name()), news, resource.StatusOK, nil
|
||||
},
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
|
||||
outs := resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"frob": "baz",
|
||||
})
|
||||
program := deploytest.NewLanguageRuntime(func(_ plugin.RunInfo, monitor *deploytest.ResourceMonitor) error {
|
||||
urn, _, _, err := monitor.RegisterResource("pkgA:m:typA", "resA", true, deploytest.ResourceOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
monitor.RegisterResourceOutputs(urn, outs)
|
||||
|
||||
return nil
|
||||
})
|
||||
host := deploytest.NewPluginHost(nil, nil, program, loaders...)
|
||||
|
||||
p := &TestPlan{
|
||||
Options: UpdateOptions{Host: host},
|
||||
}
|
||||
|
||||
project := p.GetProject()
|
||||
|
||||
// Create an initial plan to create resA and the outputs
|
||||
plan, res := TestOp(Update).Plan(project, p.GetTarget(t, nil), p.Options, p.BackendClient, nil)
|
||||
assert.NotNil(t, plan)
|
||||
assert.Nil(t, res)
|
||||
|
||||
// Now change the runtime to not return property "frob", this should error
|
||||
outs = resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
})
|
||||
p.Options.Plan = ClonePlan(t, plan)
|
||||
snap, res := TestOp(Update).Run(project, p.GetTarget(t, nil), p.Options, false, p.BackendClient, nil)
|
||||
assert.NotNil(t, snap)
|
||||
assert.NotNil(t, res)
|
||||
}
|
||||
|
|
|
@ -385,8 +385,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 {
|
||||
|
|
|
@ -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[urn]
|
||||
if !ok {
|
||||
return result.FromError(fmt.Errorf("no plan for resource %v", urn))
|
||||
} else {
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue