]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/plan.go
ea0884505a7874d4e276fb0f4f1ad95368284a4a
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / plan.go
1 package terraform
2
3 import (
4 "bytes"
5 "encoding/gob"
6 "errors"
7 "fmt"
8 "io"
9 "sync"
10
11 "github.com/hashicorp/terraform/config/module"
12 )
13
14 func init() {
15 gob.Register(make([]interface{}, 0))
16 gob.Register(make([]map[string]interface{}, 0))
17 gob.Register(make(map[string]interface{}))
18 gob.Register(make(map[string]string))
19 }
20
21 // Plan represents a single Terraform execution plan, which contains
22 // all the information necessary to make an infrastructure change.
23 //
24 // A plan has to contain basically the entire state of the world
25 // necessary to make a change: the state, diff, config, backend config, etc.
26 // This is so that it can run alone without any other data.
27 type Plan struct {
28 Diff *Diff
29 Module *module.Tree
30 State *State
31 Vars map[string]interface{}
32 Targets []string
33
34 // Backend is the backend that this plan should use and store data with.
35 Backend *BackendState
36
37 once sync.Once
38 }
39
40 // Context returns a Context with the data encapsulated in this plan.
41 //
42 // The following fields in opts are overridden by the plan: Config,
43 // Diff, State, Variables.
44 func (p *Plan) Context(opts *ContextOpts) (*Context, error) {
45 opts.Diff = p.Diff
46 opts.Module = p.Module
47 opts.State = p.State
48 opts.Targets = p.Targets
49
50 opts.Variables = make(map[string]interface{})
51 for k, v := range p.Vars {
52 opts.Variables[k] = v
53 }
54
55 return NewContext(opts)
56 }
57
58 func (p *Plan) String() string {
59 buf := new(bytes.Buffer)
60 buf.WriteString("DIFF:\n\n")
61 buf.WriteString(p.Diff.String())
62 buf.WriteString("\n\nSTATE:\n\n")
63 buf.WriteString(p.State.String())
64 return buf.String()
65 }
66
67 func (p *Plan) init() {
68 p.once.Do(func() {
69 if p.Diff == nil {
70 p.Diff = new(Diff)
71 p.Diff.init()
72 }
73
74 if p.State == nil {
75 p.State = new(State)
76 p.State.init()
77 }
78
79 if p.Vars == nil {
80 p.Vars = make(map[string]interface{})
81 }
82 })
83 }
84
85 // The format byte is prefixed into the plan file format so that we have
86 // the ability in the future to change the file format if we want for any
87 // reason.
88 const planFormatMagic = "tfplan"
89 const planFormatVersion byte = 1
90
91 // ReadPlan reads a plan structure out of a reader in the format that
92 // was written by WritePlan.
93 func ReadPlan(src io.Reader) (*Plan, error) {
94 var result *Plan
95 var err error
96 n := 0
97
98 // Verify the magic bytes
99 magic := make([]byte, len(planFormatMagic))
100 for n < len(magic) {
101 n, err = src.Read(magic[n:])
102 if err != nil {
103 return nil, fmt.Errorf("error while reading magic bytes: %s", err)
104 }
105 }
106 if string(magic) != planFormatMagic {
107 return nil, fmt.Errorf("not a valid plan file")
108 }
109
110 // Verify the version is something we can read
111 var formatByte [1]byte
112 n, err = src.Read(formatByte[:])
113 if err != nil {
114 return nil, err
115 }
116 if n != len(formatByte) {
117 return nil, errors.New("failed to read plan version byte")
118 }
119
120 if formatByte[0] != planFormatVersion {
121 return nil, fmt.Errorf("unknown plan file version: %d", formatByte[0])
122 }
123
124 dec := gob.NewDecoder(src)
125 if err := dec.Decode(&result); err != nil {
126 return nil, err
127 }
128
129 return result, nil
130 }
131
132 // WritePlan writes a plan somewhere in a binary format.
133 func WritePlan(d *Plan, dst io.Writer) error {
134 // Write the magic bytes so we can determine the file format later
135 n, err := dst.Write([]byte(planFormatMagic))
136 if err != nil {
137 return err
138 }
139 if n != len(planFormatMagic) {
140 return errors.New("failed to write plan format magic bytes")
141 }
142
143 // Write a version byte so we can iterate on version at some point
144 n, err = dst.Write([]byte{planFormatVersion})
145 if err != nil {
146 return err
147 }
148 if n != 1 {
149 return errors.New("failed to write plan version byte")
150 }
151
152 return gob.NewEncoder(dst).Encode(d)
153 }