1package plans
2
3import (
4	"sort"
5
6	"github.com/hashicorp/terraform/addrs"
7	"github.com/hashicorp/terraform/configs/configschema"
8	"github.com/zclconf/go-cty/cty"
9)
10
11// Plan is the top-level type representing a planned set of changes.
12//
13// A plan is a summary of the set of changes required to move from a current
14// state to a goal state derived from configuration. The described changes
15// are not applied directly, but contain an approximation of the final
16// result that will be completed during apply by resolving any values that
17// cannot be predicted.
18//
19// A plan must always be accompanied by the state and configuration it was
20// built from, since the plan does not itself include all of the information
21// required to make the changes indicated.
22type Plan struct {
23	VariableValues  map[string]DynamicValue
24	Changes         *Changes
25	TargetAddrs     []addrs.Targetable
26	ProviderSHA256s map[string][]byte
27	Backend         Backend
28}
29
30// Backend represents the backend-related configuration and other data as it
31// existed when a plan was created.
32type Backend struct {
33	// Type is the type of backend that the plan will apply against.
34	Type string
35
36	// Config is the configuration of the backend, whose schema is decided by
37	// the backend Type.
38	Config DynamicValue
39
40	// Workspace is the name of the workspace that was active when the plan
41	// was created. It is illegal to apply a plan created for one workspace
42	// to the state of another workspace.
43	// (This constraint is already enforced by the statefile lineage mechanism,
44	// but storing this explicitly allows us to return a better error message
45	// in the situation where the user has the wrong workspace selected.)
46	Workspace string
47}
48
49func NewBackend(typeName string, config cty.Value, configSchema *configschema.Block, workspaceName string) (*Backend, error) {
50	dv, err := NewDynamicValue(config, configSchema.ImpliedType())
51	if err != nil {
52		return nil, err
53	}
54
55	return &Backend{
56		Type:      typeName,
57		Config:    dv,
58		Workspace: workspaceName,
59	}, nil
60}
61
62// ProviderAddrs returns a list of all of the provider configuration addresses
63// referenced throughout the receiving plan.
64//
65// The result is de-duplicated so that each distinct address appears only once.
66func (p *Plan) ProviderAddrs() []addrs.AbsProviderConfig {
67	if p == nil || p.Changes == nil {
68		return nil
69	}
70
71	m := map[string]addrs.AbsProviderConfig{}
72	for _, rc := range p.Changes.Resources {
73		m[rc.ProviderAddr.String()] = rc.ProviderAddr
74	}
75	if len(m) == 0 {
76		return nil
77	}
78
79	// This is mainly just so we'll get stable results for testing purposes.
80	keys := make([]string, 0, len(m))
81	for k := range m {
82		keys = append(keys, k)
83	}
84	sort.Strings(keys)
85
86	ret := make([]addrs.AbsProviderConfig, len(keys))
87	for i, key := range keys {
88		ret[i] = m[key]
89	}
90
91	return ret
92}
93