Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions tfprotov5/state.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
package tfprotov5

import (
"errors"
"fmt"

"github.com/hashicorp/terraform-plugin-go/tfprotov5/tftypes"
)

// ErrUnknownRawStateType is returned when a RawState has no Flatmap or JSON
// bytes set. This should never be returned during the normal operation of a
// provider, and indicates one of the following:
//
// 1. terraform-plugin-go is out of sync with the protocol and should be
// updated.
//
// 2. terrafrom-plugin-go has a bug.
//
// 3. The `RawState` was generated or modified by something other than
// terraform-plugin-go and is no longer a valid value.
var ErrUnknownRawStateType = errors.New("RawState had no JSON or flatmap data set")

// RawState is the raw, undecoded state for providers to upgrade. It is
// undecoded as Terraform, for whatever reason, doesn't have the previous
// schema available to it, and so cannot decode the state itself and pushes
Expand All @@ -13,3 +33,47 @@ type RawState struct {
JSON []byte
Flatmap map[string]string
}

// Unmarshal returns a `tftypes.Value` that represents the information
// contained in the RawState in an easy-to-interact-with way. It is the
// main purpose of the RawState type, and is how provider developers should
// obtain state values from the UpgradeResourceState RPC call.
//
// Pass in the type you want the `Value` to be interpreted as. Terraform's type
// system encodes in a lossy manner, meaning the type information is not
// preserved losslessly when going over the wire. Sets, lists, and tuples all
// look the same. Objects and maps all look the same, as well, as do
// user-specified values when DynamicPseudoType is used in the schema.
// Fortunately, the provider should already know the type; it should be the
// type of the schema, or DynamicPseudoType if that's what's in the schema.
// `Unmarshal` will then parse the value as though it belongs to that type, if
// possible, and return a `tftypes.Value` with the appropriate information. If
// the data can't be interpreted as that type, an error will be returned saying
// so. In these cases, double check to make sure the schema is declaring the
// same type being passed into `Unmarshal`.
//
// In the event an ErrUnknownRawStateType is returned, one of three things
// has happened:
//
// 1. terraform-plugin-go is out of date and out of sync with the protocol, and
// an issue should be opened on its repo to get it updated.
//
// 2. terraform-plugin-go has a bug somewhere, and an issue should be opened on
// its repo to get it fixed.
//
// 3. The provider or a dependency has modified the `RawState` in an
// unsupported way, or has created one from scratch, and should treat it as
// opaque and not modify it, only calling `Unmarshal` on `RawState`s received
// from RPC requests.
//
// State files written before Terraform 0.12 that haven't been upgraded yet
// cannot be unmarshaled, and must have their Flatmap property read directly.
func (s RawState) Unmarshal(typ tftypes.Type) (tftypes.Value, error) {
if s.JSON != nil {
return jsonUnmarshal(s.JSON, typ, tftypes.AttributePath{})
}
if s.Flatmap != nil {
return tftypes.Value{}, fmt.Errorf("flatmap states cannot be unmarshaled, only states written by Terraform 0.12 and higher can be unmarshaled")
}
return tftypes.Value{}, ErrUnknownRawStateType
}