From 8fb4e5ce6ec8b02217bd5eb3b5c77c0d1b55bd8d Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Fri, 1 Mar 2019 13:59:57 -0800 Subject: [PATCH] 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. --- command/jsonplan/plan.go | 6 +++++- command/show.go | 29 ++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/command/jsonplan/plan.go b/command/jsonplan/plan.go index 43cb9064f..8adc8dd62 100644 --- a/command/jsonplan/plan.go +++ b/command/jsonplan/plan.go @@ -91,7 +91,11 @@ func Marshal( p *plans.Plan, sf *statefile.File, schemas *terraform.Schemas, + stateSchemas *terraform.Schemas, ) ([]byte, error) { + if stateSchemas == nil { + stateSchemas = schemas + } output := newPlan() output.TerraformVersion = version.String() @@ -120,7 +124,7 @@ func Marshal( } // output.PriorState - output.PriorState, err = jsonstate.Marshal(sf, schemas) + output.PriorState, err = jsonstate.Marshal(sf, stateSchemas) if err != nil { return nil, fmt.Errorf("error marshaling prior state: %s", err) } diff --git a/command/show.go b/command/show.go index 1831fb58e..7180ab394 100644 --- a/command/show.go +++ b/command/show.go @@ -145,7 +145,30 @@ func (c *ShowCommand) Run(args []string) int { if plan != nil { if jsonOutput == true { 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 { c.Ui.Error(fmt.Sprintf("Failed to marshal plan to json: %s", err)) 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) } - if err := stateStore.RefreshState(); err != nil { - return nil, fmt.Errorf("Failed to load state: %s", err) - } - sf := statemgr.Export(stateStore) return sf, nil