1package states
2
3import (
4	"github.com/zclconf/go-cty/cty"
5	ctyjson "github.com/zclconf/go-cty/cty/json"
6
7	"github.com/hashicorp/terraform/internal/addrs"
8)
9
10// ResourceInstanceObject is the local representation of a specific remote
11// object associated with a resource instance. In practice not all remote
12// objects are actually remote in the sense of being accessed over the network,
13// but this is the most common case.
14//
15// It is not valid to mutate a ResourceInstanceObject once it has been created.
16// Instead, create a new object and replace the existing one.
17type ResourceInstanceObject struct {
18	// Value is the object-typed value representing the remote object within
19	// Terraform.
20	Value cty.Value
21
22	// Private is an opaque value set by the provider when this object was
23	// last created or updated. Terraform Core does not use this value in
24	// any way and it is not exposed anywhere in the user interface, so
25	// a provider can use it for retaining any necessary private state.
26	Private []byte
27
28	// Status represents the "readiness" of the object as of the last time
29	// it was updated.
30	Status ObjectStatus
31
32	// Dependencies is a set of absolute address to other resources this
33	// instance dependeded on when it was applied. This is used to construct
34	// the dependency relationships for an object whose configuration is no
35	// longer available, such as if it has been removed from configuration
36	// altogether, or is now deposed.
37	Dependencies []addrs.ConfigResource
38
39	// CreateBeforeDestroy reflects the status of the lifecycle
40	// create_before_destroy option when this instance was last updated.
41	// Because create_before_destroy also effects the overall ordering of the
42	// destroy operations, we need to record the status to ensure a resource
43	// removed from the config will still be destroyed in the same manner.
44	CreateBeforeDestroy bool
45}
46
47// ObjectStatus represents the status of a RemoteObject.
48type ObjectStatus rune
49
50//go:generate go run golang.org/x/tools/cmd/stringer -type ObjectStatus
51
52const (
53	// ObjectReady is an object status for an object that is ready to use.
54	ObjectReady ObjectStatus = 'R'
55
56	// ObjectTainted is an object status representing an object that is in
57	// an unrecoverable bad state due to a partial failure during a create,
58	// update, or delete operation. Since it cannot be moved into the
59	// ObjectRead state, a tainted object must be replaced.
60	ObjectTainted ObjectStatus = 'T'
61
62	// ObjectPlanned is a special object status used only for the transient
63	// placeholder objects we place into state during the refresh and plan
64	// walks to stand in for objects that will be created during apply.
65	//
66	// Any object of this status must have a corresponding change recorded
67	// in the current plan, whose value must then be used in preference to
68	// the value stored in state when evaluating expressions. A planned
69	// object stored in state will be incomplete if any of its attributes are
70	// not yet known, and the plan must be consulted in order to "see" those
71	// unknown values, because the state is not able to represent them.
72	ObjectPlanned ObjectStatus = 'P'
73)
74
75// Encode marshals the value within the receiver to produce a
76// ResourceInstanceObjectSrc ready to be written to a state file.
77//
78// The given type must be the implied type of the resource type schema, and
79// the given value must conform to it. It is important to pass the schema
80// type and not the object's own type so that dynamically-typed attributes
81// will be stored correctly. The caller must also provide the version number
82// of the schema that the given type was derived from, which will be recorded
83// in the source object so it can be used to detect when schema migration is
84// required on read.
85//
86// The returned object may share internal references with the receiver and
87// so the caller must not mutate the receiver any further once once this
88// method is called.
89func (o *ResourceInstanceObject) Encode(ty cty.Type, schemaVersion uint64) (*ResourceInstanceObjectSrc, error) {
90	// If it contains marks, remove these marks before traversing the
91	// structure with UnknownAsNull, and save the PathValueMarks
92	// so we can save them in state.
93	val, pvm := o.Value.UnmarkDeepWithPaths()
94
95	// Our state serialization can't represent unknown values, so we convert
96	// them to nulls here. This is lossy, but nobody should be writing unknown
97	// values here and expecting to get them out again later.
98	//
99	// We get unknown values here while we're building out a "planned state"
100	// during the plan phase, but the value stored in the plan takes precedence
101	// for expression evaluation. The apply step should never produce unknown
102	// values, but if it does it's the responsibility of the caller to detect
103	// and raise an error about that.
104	val = cty.UnknownAsNull(val)
105
106	src, err := ctyjson.Marshal(val, ty)
107	if err != nil {
108		return nil, err
109	}
110
111	return &ResourceInstanceObjectSrc{
112		SchemaVersion:       schemaVersion,
113		AttrsJSON:           src,
114		AttrSensitivePaths:  pvm,
115		Private:             o.Private,
116		Status:              o.Status,
117		Dependencies:        o.Dependencies,
118		CreateBeforeDestroy: o.CreateBeforeDestroy,
119	}, nil
120}
121
122// AsTainted returns a deep copy of the receiver with the status updated to
123// ObjectTainted.
124func (o *ResourceInstanceObject) AsTainted() *ResourceInstanceObject {
125	if o == nil {
126		// A nil object can't be tainted, but we'll allow this anyway to
127		// avoid a crash, since we presumably intend to eventually record
128		// the object has having been deleted anyway.
129		return nil
130	}
131	ret := o.DeepCopy()
132	ret.Status = ObjectTainted
133	return ret
134}
135