mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 18:10:30 -04:00
204 lines
6.5 KiB
Go
204 lines
6.5 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package tfstackdata1
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/msgpack"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/lang/marks"
|
|
"github.com/hashicorp/terraform/internal/plans"
|
|
"github.com/hashicorp/terraform/internal/plans/planfile"
|
|
"github.com/hashicorp/terraform/internal/plans/planproto"
|
|
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
)
|
|
|
|
func ResourceInstanceObjectStateToTFStackData1(objSrc *states.ResourceInstanceObjectSrc, providerConfigAddr addrs.AbsProviderConfig) *StateResourceInstanceObjectV1 {
|
|
if objSrc == nil {
|
|
// This is presumably representing the absense of any prior state,
|
|
// such as when an object is being planned for creation.
|
|
return nil
|
|
}
|
|
|
|
// Hack: we'll borrow NewDynamicValue's treatment of the sensitive
|
|
// attribute paths here just so we don't need to reimplement the
|
|
// slice-of-paths conversion in yet another place. We don't
|
|
// actually do anything with the value part of this.
|
|
protoValue := stacks.NewDynamicValue(plans.DynamicValue(nil), objSrc.AttrSensitivePaths)
|
|
rawMsg := &StateResourceInstanceObjectV1{
|
|
SchemaVersion: objSrc.SchemaVersion,
|
|
ValueJson: objSrc.AttrsJSON,
|
|
SensitivePaths: Terraform1ToPlanProtoAttributePaths(protoValue.Sensitive),
|
|
CreateBeforeDestroy: objSrc.CreateBeforeDestroy,
|
|
ProviderConfigAddr: providerConfigAddr.String(),
|
|
ProviderSpecificData: objSrc.Private,
|
|
}
|
|
switch objSrc.Status {
|
|
case states.ObjectReady:
|
|
rawMsg.Status = StateResourceInstanceObjectV1_READY
|
|
case states.ObjectTainted:
|
|
rawMsg.Status = StateResourceInstanceObjectV1_DAMAGED
|
|
default:
|
|
rawMsg.Status = StateResourceInstanceObjectV1_UNKNOWN
|
|
}
|
|
|
|
rawMsg.Dependencies = make([]string, len(objSrc.Dependencies))
|
|
for i, addr := range objSrc.Dependencies {
|
|
rawMsg.Dependencies[i] = addr.String()
|
|
}
|
|
|
|
return rawMsg
|
|
}
|
|
|
|
func Terraform1ToStackDataDynamicValue(value *stacks.DynamicValue) *DynamicValue {
|
|
return &DynamicValue{
|
|
Value: &planproto.DynamicValue{
|
|
Msgpack: value.Msgpack,
|
|
},
|
|
SensitivePaths: Terraform1ToPlanProtoAttributePaths(value.Sensitive),
|
|
}
|
|
}
|
|
|
|
func DynamicValueFromTFStackData1(protoVal *DynamicValue, ty cty.Type) (cty.Value, error) {
|
|
raw := protoVal.Value.Msgpack
|
|
|
|
unmarkedV, err := msgpack.Unmarshal(raw, ty)
|
|
if err != nil {
|
|
return cty.NilVal, err
|
|
}
|
|
|
|
var markses []cty.PathValueMarks
|
|
if len(protoVal.SensitivePaths) != 0 {
|
|
markses = make([]cty.PathValueMarks, 0, len(protoVal.SensitivePaths))
|
|
marks := cty.NewValueMarks(marks.Sensitive)
|
|
for _, protoPath := range protoVal.SensitivePaths {
|
|
path, err := planfile.PathFromProto(protoPath)
|
|
if err != nil {
|
|
return cty.NilVal, fmt.Errorf("invalid sensitive value path: %w", err)
|
|
}
|
|
markses = append(markses, cty.PathValueMarks{
|
|
Path: path,
|
|
Marks: marks,
|
|
})
|
|
}
|
|
}
|
|
return unmarkedV.MarkWithPaths(markses), nil
|
|
}
|
|
|
|
func Terraform1ToPlanProtoAttributePaths(paths []*stacks.AttributePath) []*planproto.Path {
|
|
if len(paths) == 0 {
|
|
return nil
|
|
}
|
|
ret := make([]*planproto.Path, len(paths))
|
|
for i, tf1Path := range paths {
|
|
ret[i] = Terraform1ToPlanProtoAttributePath(tf1Path)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func Terraform1ToPlanProtoAttributePath(path *stacks.AttributePath) *planproto.Path {
|
|
if path == nil {
|
|
return nil
|
|
}
|
|
ret := &planproto.Path{}
|
|
if len(path.Steps) == 0 {
|
|
return ret
|
|
}
|
|
ret.Steps = make([]*planproto.Path_Step, len(path.Steps))
|
|
for i, tf1Step := range path.Steps {
|
|
ret.Steps[i] = Terraform1ToPlanProtoAttributePathStep(tf1Step)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func Terraform1ToPlanProtoAttributePathStep(step *stacks.AttributePath_Step) *planproto.Path_Step {
|
|
if step == nil {
|
|
return nil
|
|
}
|
|
ret := &planproto.Path_Step{}
|
|
switch sel := step.Selector.(type) {
|
|
case *stacks.AttributePath_Step_AttributeName:
|
|
ret.Selector = &planproto.Path_Step_AttributeName{
|
|
AttributeName: sel.AttributeName,
|
|
}
|
|
case *stacks.AttributePath_Step_ElementKeyInt:
|
|
encInt, err := msgpack.Marshal(cty.NumberIntVal(sel.ElementKeyInt), cty.Number)
|
|
if err != nil {
|
|
// This should not be possible because all integers have a cty msgpack encoding
|
|
panic(fmt.Sprintf("unencodable element index: %s", err))
|
|
}
|
|
ret.Selector = &planproto.Path_Step_ElementKey{
|
|
ElementKey: &planproto.DynamicValue{
|
|
Msgpack: encInt,
|
|
},
|
|
}
|
|
case *stacks.AttributePath_Step_ElementKeyString:
|
|
encStr, err := msgpack.Marshal(cty.StringVal(sel.ElementKeyString), cty.String)
|
|
if err != nil {
|
|
// This should not be possible because all strings have a cty msgpack encoding
|
|
panic(fmt.Sprintf("unencodable element key: %s", err))
|
|
}
|
|
ret.Selector = &planproto.Path_Step_ElementKey{
|
|
ElementKey: &planproto.DynamicValue{
|
|
Msgpack: encStr,
|
|
},
|
|
}
|
|
default:
|
|
// Should not get here, because the above cases should be exhaustive
|
|
// for all possible *terraform1.AttributePath_Step selector types.
|
|
panic(fmt.Sprintf("unsupported path step selector type %T", sel))
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func DecodeProtoResourceInstanceObject(protoObj *StateResourceInstanceObjectV1) (*states.ResourceInstanceObjectSrc, error) {
|
|
objSrc := &states.ResourceInstanceObjectSrc{
|
|
SchemaVersion: protoObj.SchemaVersion,
|
|
AttrsJSON: protoObj.ValueJson,
|
|
CreateBeforeDestroy: protoObj.CreateBeforeDestroy,
|
|
Private: protoObj.ProviderSpecificData,
|
|
}
|
|
|
|
switch protoObj.Status {
|
|
case StateResourceInstanceObjectV1_READY:
|
|
objSrc.Status = states.ObjectReady
|
|
case StateResourceInstanceObjectV1_DAMAGED:
|
|
objSrc.Status = states.ObjectTainted
|
|
default:
|
|
return nil, fmt.Errorf("unsupported status %s", protoObj.Status.String())
|
|
}
|
|
|
|
paths := make([]cty.Path, 0, len(protoObj.SensitivePaths))
|
|
for _, p := range protoObj.SensitivePaths {
|
|
path, err := planfile.PathFromProto(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
paths = append(paths, path)
|
|
}
|
|
objSrc.AttrSensitivePaths = paths
|
|
|
|
if len(protoObj.Dependencies) != 0 {
|
|
objSrc.Dependencies = make([]addrs.ConfigResource, len(protoObj.Dependencies))
|
|
for i, raw := range protoObj.Dependencies {
|
|
instAddr, diags := addrs.ParseAbsResourceInstanceStr(raw)
|
|
if diags.HasErrors() {
|
|
return nil, fmt.Errorf("invalid dependency %q", raw)
|
|
}
|
|
// We used the resource instance address parser here but we
|
|
// actually want the "config resource" subset of that syntax only.
|
|
configAddr := instAddr.ConfigResource()
|
|
if configAddr.String() != instAddr.String() {
|
|
return nil, fmt.Errorf("invalid dependency %q", raw)
|
|
}
|
|
objSrc.Dependencies[i] = configAddr
|
|
}
|
|
}
|
|
|
|
return objSrc, nil
|
|
}
|