command/show (json): marshal the state snapshot included with the plan file (#21597)
* command/show: marshal the state snapshot from the planfile The planfile contains a state snapshot with certain resources updated (outputs and datasources). Previously `terraform show -json PLANFILE` was using the current state instead of the state inside the plan as intended. This caused an issue when the state included a terraform_remote_state datasource. The datasource's state gets refreshed - and therefore upgraded to the current state version - during plan, but that won't persist to state until apply. * update comment to reflect new return
This commit is contained in:
parent
2f893f2b95
commit
b9f114aa25
|
@ -91,12 +91,7 @@ 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()
|
||||
|
||||
|
@ -125,7 +120,7 @@ func Marshal(
|
|||
|
||||
// output.PriorState
|
||||
if sf != nil && !sf.State.Empty() {
|
||||
output.PriorState, err = jsonstate.Marshal(sf, stateSchemas)
|
||||
output.PriorState, err = jsonstate.Marshal(sf, schemas)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshaling prior state: %s", err)
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
// if that fails, try to read the cli argument as a path to a statefile
|
||||
if len(args) > 0 {
|
||||
path := args[0]
|
||||
plan, planErr = getPlanFromPath(path)
|
||||
plan, stateFile, planErr = getPlanFromPath(path)
|
||||
if planErr != nil {
|
||||
stateFile, stateErr = getStateFromPath(path)
|
||||
if stateErr != nil {
|
||||
|
@ -129,9 +129,7 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if stateFile == nil {
|
||||
} else {
|
||||
env := c.Workspace()
|
||||
stateFile, stateErr = getStateFromEnv(b, env)
|
||||
if err != nil {
|
||||
|
@ -143,29 +141,7 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
if plan != nil {
|
||||
if jsonOutput == true {
|
||||
config := ctx.Config()
|
||||
|
||||
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)
|
||||
}
|
||||
jsonPlan, err := jsonplan.Marshal(config, plan, stateFile, schemas)
|
||||
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to marshal plan to json: %s", err))
|
||||
|
@ -224,19 +200,21 @@ func (c *ShowCommand) Synopsis() string {
|
|||
return "Inspect Terraform state or plan"
|
||||
}
|
||||
|
||||
// getPlanFromPath returns a plan if the user-supplied path points to a planfile.
|
||||
// If both plan and error are nil, the path is likely a directory.
|
||||
// An error could suggest that the given path points to a statefile.
|
||||
func getPlanFromPath(path string) (*plans.Plan, error) {
|
||||
// getPlanFromPath returns a plan and statefile if the user-supplied path points
|
||||
// to a planfile. If both plan and error are nil, the path is likely a
|
||||
// directory. An error could suggest that the given path points to a statefile.
|
||||
func getPlanFromPath(path string) (*plans.Plan, *statefile.File, error) {
|
||||
pr, err := planfile.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
plan, err := pr.ReadPlan()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return plan, nil
|
||||
|
||||
stateFile, err := pr.ReadStateFile()
|
||||
return plan, stateFile, nil
|
||||
}
|
||||
|
||||
// getStateFromPath returns a statefile if the user-supplied path points to a statefile.
|
||||
|
|
|
@ -412,6 +412,11 @@ type plan struct {
|
|||
PlannedValues map[string]interface{} `json:"planned_values,omitempty"`
|
||||
ResourceChanges []interface{} `json:"resource_changes,omitempty"`
|
||||
OutputChanges map[string]interface{} `json:"output_changes,omitempty"`
|
||||
PriorState map[string]interface{} `json:"prior_state,omitempty"`
|
||||
PriorState priorState `json:"prior_state,omitempty"`
|
||||
Config map[string]interface{} `json:"configuration,omitempty"`
|
||||
}
|
||||
|
||||
type priorState struct {
|
||||
FormatVersion string `json:"format_version,omitempty"`
|
||||
Values map[string]interface{} `json:"values,omitempty"`
|
||||
}
|
||||
|
|
|
@ -53,6 +53,18 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"root_module": {}
|
||||
}
|
||||
},
|
||||
"resource_changes": [
|
||||
{
|
||||
"address": "test_instance.test[0]",
|
||||
|
|
|
@ -82,8 +82,13 @@
|
|||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"terraform_version": "0.12.0",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
|
|
|
@ -64,8 +64,13 @@
|
|||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"terraform_version": "0.12.0",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
|
|
|
@ -69,6 +69,18 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
"sensitive": false,
|
||||
"value": "baz"
|
||||
}
|
||||
},
|
||||
"root_module": {}
|
||||
}
|
||||
},
|
||||
"resource_changes": [
|
||||
{
|
||||
"address": "module.module_test_bar.test_instance.test",
|
||||
|
|
Loading…
Reference in New Issue