-
Notifications
You must be signed in to change notification settings - Fork 9.5k
/
writer.go
139 lines (123 loc) · 4.03 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package planfile
import (
"archive/zip"
"fmt"
"os"
"time"
"github.com/hashicorp/terraform/internal/configs/configload"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/states/statefile"
)
type CreateArgs struct {
// ConfigSnapshot is a snapshot of the configuration that the plan
// was created from.
ConfigSnapshot *configload.Snapshot
// PreviousRunStateFile is a representation of the state snapshot we used
// as the original input when creating this plan, containing the same
// information as recorded at the end of the previous apply except for
// upgrading managed resource instance data to the provider's latest
// schema versions.
PreviousRunStateFile *statefile.File
// BaseStateFile is a representation of the state snapshot we used to
// create the plan, which is the result of asking the providers to refresh
// all previously-stored objects to match the current situation in the
// remote system. (If this plan was created with refreshing disabled,
// this should be the same as PreviousRunStateFile.)
StateFile *statefile.File
// Plan records the plan itself, which is the main artifact inside a
// saved plan file.
Plan *plans.Plan
// DependencyLocks records the dependency lock information that we
// checked prior to creating the plan, so we can make sure that all of the
// same dependencies are still available when applying the plan.
DependencyLocks *depsfile.Locks
}
// Create creates a new plan file with the given filename, overwriting any
// file that might already exist there.
//
// A plan file contains both a snapshot of the configuration and of the latest
// state file in addition to the plan itself, so that Terraform can detect
// if the world has changed since the plan was created and thus refuse to
// apply it.
func Create(filename string, args CreateArgs) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
zw := zip.NewWriter(f)
defer zw.Close()
// tfplan file
{
w, err := zw.CreateHeader(&zip.FileHeader{
Name: tfplanFilename,
Method: zip.Deflate,
Modified: time.Now(),
})
if err != nil {
return fmt.Errorf("failed to create tfplan file: %s", err)
}
err = writeTfplan(args.Plan, w)
if err != nil {
return fmt.Errorf("failed to write plan: %s", err)
}
}
// tfstate file
{
w, err := zw.CreateHeader(&zip.FileHeader{
Name: tfstateFilename,
Method: zip.Deflate,
Modified: time.Now(),
})
if err != nil {
return fmt.Errorf("failed to create embedded tfstate file: %s", err)
}
err = statefile.Write(args.StateFile, w)
if err != nil {
return fmt.Errorf("failed to write state snapshot: %s", err)
}
}
// tfstate-prev file
{
w, err := zw.CreateHeader(&zip.FileHeader{
Name: tfstatePreviousFilename,
Method: zip.Deflate,
Modified: time.Now(),
})
if err != nil {
return fmt.Errorf("failed to create embedded tfstate-prev file: %s", err)
}
err = statefile.Write(args.PreviousRunStateFile, w)
if err != nil {
return fmt.Errorf("failed to write previous state snapshot: %s", err)
}
}
// tfconfig directory
{
err := writeConfigSnapshot(args.ConfigSnapshot, zw)
if err != nil {
return fmt.Errorf("failed to write config snapshot: %s", err)
}
}
// .terraform.lock.hcl file, containing dependency lock information
if args.DependencyLocks != nil { // (this was a later addition, so not all callers set it, but main callers should)
src, diags := depsfile.SaveLocksToBytes(args.DependencyLocks)
if diags.HasErrors() {
return fmt.Errorf("failed to write embedded dependency lock file: %s", diags.Err().Error())
}
w, err := zw.CreateHeader(&zip.FileHeader{
Name: dependencyLocksFilename,
Method: zip.Deflate,
Modified: time.Now(),
})
if err != nil {
return fmt.Errorf("failed to create embedded dependency lock file: %s", err)
}
_, err = w.Write(src)
if err != nil {
return fmt.Errorf("failed to write embedded dependency lock file: %s", err)
}
}
return nil
}