// Copyright (c) The OpenTofu Authors // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2023 HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package jsonformat import ( "sort" ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/opentofu/opentofu/internal/command/jsonformat/computed" "github.com/opentofu/opentofu/internal/command/jsonformat/differ" "github.com/opentofu/opentofu/internal/command/jsonformat/structured" "github.com/opentofu/opentofu/internal/command/jsonprovider" "github.com/opentofu/opentofu/internal/command/jsonstate" ) type State struct { StateFormatVersion string `json:"state_format_version"` RootModule jsonstate.Module `json:"root"` RootModuleOutputs map[string]jsonstate.Output `json:"root_module_outputs"` ProviderFormatVersion string `json:"provider_format_version"` ProviderSchemas map[string]*jsonprovider.Provider `json:"provider_schemas"` } func (state State) Empty() bool { return len(state.RootModuleOutputs) == 0 && len(state.RootModule.Resources) == 0 && len(state.RootModule.ChildModules) == 0 } func (state State) GetSchema(resource jsonstate.Resource) *jsonprovider.Schema { switch resource.Mode { case jsonstate.ManagedResourceMode: return state.ProviderSchemas[resource.ProviderName].ResourceSchemas[resource.Type] case jsonstate.DataResourceMode: return state.ProviderSchemas[resource.ProviderName].DataSourceSchemas[resource.Type] default: panic("found unrecognized resource mode: " + resource.Mode) } } func (state State) renderHumanStateModule(renderer Renderer, module jsonstate.Module, opts computed.RenderHumanOpts, first bool) { if len(module.Resources) > 0 && !first { renderer.Streams.Println() } for _, resource := range module.Resources { if !first { renderer.Streams.Println() } if first { first = false } if len(resource.DeposedKey) > 0 { renderer.Streams.Printf("# %s: (deposed object %s)", resource.Address, resource.DeposedKey) } else if resource.Tainted { renderer.Streams.Printf("# %s: (tainted)", resource.Address) } else { renderer.Streams.Printf("# %s:", resource.Address) } renderer.Streams.Println() schema := state.GetSchema(resource) switch resource.Mode { case jsonstate.ManagedResourceMode: change := structured.FromJsonResource(resource) renderer.Streams.Printf("resource %q %q %s", resource.Type, resource.Name, differ.ComputeDiffForBlock(change, schema.Block).RenderHuman(0, opts)) case jsonstate.DataResourceMode: change := structured.FromJsonResource(resource) renderer.Streams.Printf("data %q %q %s", resource.Type, resource.Name, differ.ComputeDiffForBlock(change, schema.Block).RenderHuman(0, opts)) default: panic("found unrecognized resource mode: " + resource.Mode) } renderer.Streams.Println() } for _, child := range module.ChildModules { state.renderHumanStateModule(renderer, child, opts, first) } } func (state State) renderHumanStateOutputs(renderer Renderer, opts computed.RenderHumanOpts) { if len(state.RootModuleOutputs) > 0 { renderer.Streams.Printf("\n\nOutputs:\n\n") var keys []string for key := range state.RootModuleOutputs { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { output := state.RootModuleOutputs[key] change := structured.FromJsonOutput(output) ctype, err := ctyjson.UnmarshalType(output.Type) if err != nil { // We can actually do this without the type, so even if we fail // to work out the type let's just render this anyway. renderer.Streams.Printf("%s = %s\n", key, differ.ComputeDiffForOutput(change).RenderHuman(0, opts)) } else { renderer.Streams.Printf("%s = %s\n", key, differ.ComputeDiffForType(change, ctype).RenderHuman(0, opts)) } } } }