command/show: differentiate between state schemas and plan schemas. (#20516)

When a planfile is supplied to the `terraform show -json` command, the
context that loads only included schemas for resources in the plan. We
found an edge case where removing a data source from the configuration
(though only if there are no managed resources from the same provider)
would cause jsonstate.Marshal to fail because the provider schema wasn't
in the plan context.

jsonplan.Marshal now takes two schemas, one for plan and one for state.
If the state schema is nil it will simply use the plan schemas.
This commit is contained in:
Kristin Laemmert 2019-03-01 13:59:57 -08:00 committed by GitHub
parent c4151b7c7c
commit 8fb4e5ce6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 6 deletions

View File

@ -91,7 +91,11 @@ func Marshal(
p *plans.Plan, p *plans.Plan,
sf *statefile.File, sf *statefile.File,
schemas *terraform.Schemas, schemas *terraform.Schemas,
stateSchemas *terraform.Schemas,
) ([]byte, error) { ) ([]byte, error) {
if stateSchemas == nil {
stateSchemas = schemas
}
output := newPlan() output := newPlan()
output.TerraformVersion = version.String() output.TerraformVersion = version.String()
@ -120,7 +124,7 @@ func Marshal(
} }
// output.PriorState // output.PriorState
output.PriorState, err = jsonstate.Marshal(sf, schemas) output.PriorState, err = jsonstate.Marshal(sf, stateSchemas)
if err != nil { if err != nil {
return nil, fmt.Errorf("error marshaling prior state: %s", err) return nil, fmt.Errorf("error marshaling prior state: %s", err)
} }

View File

@ -145,7 +145,30 @@ func (c *ShowCommand) Run(args []string) int {
if plan != nil { if plan != nil {
if jsonOutput == true { if jsonOutput == true {
config := ctx.Config() config := ctx.Config()
jsonPlan, err := jsonplan.Marshal(config, plan, stateFile, schemas)
var err error
var jsonPlan []byte
// If there is no prior state, we have all the schemas needed.
if stateFile == nil {
jsonPlan, err = jsonplan.Marshal(config, plan, stateFile, schemas, nil)
} else {
// If there is state, we need the state-specific schemas, which
// may differ from the schemas loaded from the plan.
// This occurs if there is a data_source in the state that was
// removed from the configuration, because terraform core does
// not need to load the schema to remove a data source.
opReq.PlanFile = nil
ctx, _, ctxDiags := local.Context(opReq)
diags = diags.Append(ctxDiags)
if ctxDiags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
stateSchemas := ctx.Schemas()
jsonPlan, err = jsonplan.Marshal(config, plan, stateFile, schemas, stateSchemas)
}
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to marshal plan to json: %s", err)) c.Ui.Error(fmt.Sprintf("Failed to marshal plan to json: %s", err))
return 1 return 1
@ -236,10 +259,6 @@ func getStateFromEnv(b backend.Backend, env string) (*statefile.File, error) {
return nil, fmt.Errorf("Failed to load state manager: %s", err) return nil, fmt.Errorf("Failed to load state manager: %s", err)
} }
if err := stateStore.RefreshState(); err != nil {
return nil, fmt.Errorf("Failed to load state: %s", err)
}
sf := statemgr.Export(stateStore) sf := statemgr.Export(stateStore)
return sf, nil return sf, nil