@ -193,6 +193,121 @@ Plan: 1 to add, 0 to change, 1 to destroy.`
}
}
func TestLocal_planDeposedOnly ( t * testing . T ) {
b , cleanup := TestLocal ( t )
defer cleanup ( )
p := TestLocalProvider ( t , b , "test" , planFixtureSchema ( ) )
testStateFile ( t , b . StatePath , states . BuildState ( func ( ss * states . SyncState ) {
ss . SetResourceInstanceDeposed (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "test_instance" ,
Name : "foo" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
states . DeposedKey ( "00000000" ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : [ ] byte ( ` {
"ami" : "bar" ,
"network_interface" : [ {
"device_index" : 0 ,
"description" : "Main network interface"
} ]
} ` ) ,
} ,
addrs . ProviderConfig {
Type : "test" ,
} . Absolute ( addrs . RootModuleInstance ) ,
)
} ) )
b . CLI = cli . NewMockUi ( )
outDir := testTempDir ( t )
defer os . RemoveAll ( outDir )
planPath := filepath . Join ( outDir , "plan.tfplan" )
op , configCleanup := testOperationPlan ( t , "./test-fixtures/plan" )
defer configCleanup ( )
op . PlanRefresh = true
op . PlanOutPath = planPath
cfg := cty . ObjectVal ( map [ string ] cty . Value {
"path" : cty . StringVal ( b . StatePath ) ,
} )
cfgRaw , err := plans . NewDynamicValue ( cfg , cfg . Type ( ) )
if err != nil {
t . Fatal ( err )
}
op . PlanOutBackend = & plans . Backend {
// Just a placeholder so that we can generate a valid plan file.
Type : "local" ,
Config : cfgRaw ,
}
run , err := b . Operation ( context . Background ( ) , op )
if err != nil {
t . Fatalf ( "bad: %s" , err )
}
<- run . Done ( )
if run . Result != backend . OperationSuccess {
t . Fatalf ( "plan operation failed" )
}
if ! p . ReadResourceCalled {
t . Fatal ( "ReadResource should be called" )
}
if run . PlanEmpty {
t . Fatal ( "plan should not be empty" )
}
// The deposed object and the current object are distinct, so our
// plan includes separate actions for each of them. This strange situation
// is not common: it should arise only if Terraform fails during
// a create-before-destroy when the create hasn't completed yet but
// in a severe way that prevents the previous object from being restored
// as "current".
//
// However, that situation was more common in some earlier Terraform
// versions where deposed objects were not managed properly, so this
// can arise when upgrading from an older version with deposed objects
// already in the state.
//
// This is one of the few cases where we expose the idea of "deposed" in
// the UI, including the user-unfriendly "deposed key" (00000000 in this
// case) just so that users can correlate this with what they might
// see in `terraform show` and in the subsequent apply output, because
// it's also possible for there to be _multiple_ deposed objects, in the
// unlikely event that create_before_destroy _keeps_ crashing across
// subsequent runs.
expectedOutput := ` An execution plan has been generated and is shown below .
Resource actions are indicated with the following symbols :
+ create
- destroy
Terraform will perform the following actions :
# test_instance . foo will be created
+ resource "test_instance" "foo" {
+ ami = "bar"
+ network_interface {
+ description = "Main network interface"
+ device_index = 0
}
}
# test_instance . foo ( deposed object 00000000 ) will be destroyed
- resource "test_instance" "foo" {
- ami = "bar" - > null
- network_interface {
- description = "Main network interface" - > null
- device_index = 0 - > null
}
}
Plan : 1 to add , 0 to change , 1 to destroy . `
output := b . CLI . ( * cli . MockUi ) . OutputWriter . String ( )
if ! strings . Contains ( output , expectedOutput ) {
t . Fatalf ( "Unexpected output:\n%s\n\nwant output containing:\n%s" , output , expectedOutput )
}
}
func TestLocal_planTainted_createBeforeDestroy ( t * testing . T ) {
b , cleanup := TestLocal ( t )
defer cleanup ( )