/
types.packer_config.go
169 lines (153 loc) · 5.57 KB
/
types.packer_config.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package hcl2template
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/packer"
"github.com/zclconf/go-cty/cty"
)
// PackerConfig represents a loaded Packer HCL config. It will contain
// references to all possible blocks of the allowed configuration.
type PackerConfig struct {
// Directory where the config files are defined
Basedir string
// Available Source blocks
Sources map[SourceRef]*SourceBlock
// InputVariables and LocalVariables are the list of defined input and
// local variables. They are of the same type but are not used in the same
// way. Local variables will not be decoded from any config file, env var,
// or ect. Like the Input variables will.
InputVariables Variables
LocalVariables Variables
// Builds is the list of Build blocks defined in the config files.
Builds Builds
}
// EvalContext returns the *hcl.EvalContext that will be passed to an hcl
// decoder in order to tell what is the actual value of a var or a local and
// the list of defined functions.
func (cfg *PackerConfig) EvalContext() *hcl.EvalContext {
ectx := &hcl.EvalContext{
Functions: Functions(cfg.Basedir),
Variables: map[string]cty.Value{
"var": cty.ObjectVal(cfg.InputVariables.Values()),
"local": cty.ObjectVal(cfg.LocalVariables.Values()),
},
}
return ectx
}
// getCoreBuildProvisioners takes a list of provisioner block, starts according
// provisioners and sends parsed HCL2 over to it.
func (p *Parser) getCoreBuildProvisioners(blocks []*ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]string) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) {
var diags hcl.Diagnostics
res := []packer.CoreBuildProvisioner{}
for _, pb := range blocks {
provisioner, moreDiags := p.startProvisioner(pb, ectx, generatedVars)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
res = append(res, packer.CoreBuildProvisioner{
PType: pb.PType,
PName: pb.PName,
Provisioner: provisioner,
})
}
return res, diags
}
// getCoreBuildProvisioners takes a list of post processor block, starts
// according provisioners and sends parsed HCL2 over to it.
func (p *Parser) getCoreBuildPostProcessors(blocks []*PostProcessorBlock, ectx *hcl.EvalContext, generatedVars map[string]string) ([]packer.CoreBuildPostProcessor, hcl.Diagnostics) {
var diags hcl.Diagnostics
res := []packer.CoreBuildPostProcessor{}
for _, ppb := range blocks {
postProcessor, moreDiags := p.startPostProcessor(ppb, ectx, generatedVars)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
res = append(res, packer.CoreBuildPostProcessor{
PostProcessor: postProcessor,
PName: ppb.PName,
PType: ppb.PType,
})
}
return res, diags
}
// getBuilds will return a list of packer Build based on the HCL2 parsed build
// blocks. All Builders, Provisioners and Post Processors will be started and
// configured.
func (p *Parser) getBuilds(cfg *PackerConfig) ([]packer.Build, hcl.Diagnostics) {
res := []packer.Build{}
var diags hcl.Diagnostics
for _, build := range cfg.Builds {
for _, from := range build.Sources {
src, found := cfg.Sources[from]
if !found {
diags = append(diags, &hcl.Diagnostic{
Summary: "Unknown " + sourceLabel + " " + from.String(),
Subject: build.HCL2Ref.DefRange.Ptr(),
Severity: hcl.DiagError,
})
continue
}
builder, moreDiags, generatedVars := p.startBuilder(src, cfg.EvalContext())
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
// If the builder has provided a list of to-be-generated variables that
// should be made accessible to provisioners, pass that list into
// the provisioner prepare() so that the provisioner can appropriately
// validate user input against what will become available. Otherwise,
// only pass the default variables, using the basic placeholder data.
generatedPlaceholderMap := packer.BasicPlaceholderData()
if generatedVars != nil {
for _, k := range generatedVars {
generatedPlaceholderMap[k] = fmt.Sprintf("Build_%s. "+
common.PlaceholderMsg, k)
}
}
provisioners, moreDiags := p.getCoreBuildProvisioners(build.ProvisionerBlocks, cfg.EvalContext(), generatedPlaceholderMap)
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
postProcessors, moreDiags := p.getCoreBuildPostProcessors(build.PostProcessors, cfg.EvalContext(), generatedPlaceholderMap)
pps := [][]packer.CoreBuildPostProcessor{}
if len(postProcessors) > 0 {
pps = [][]packer.CoreBuildPostProcessor{postProcessors}
}
diags = append(diags, moreDiags...)
if moreDiags.HasErrors() {
continue
}
pcb := &packer.CoreBuild{
Type: src.Type,
Builder: builder,
Provisioners: provisioners,
PostProcessors: pps,
Prepared: true,
}
res = append(res, pcb)
}
}
return res, diags
}
// Parse will parse HCL file(s) in path. Path can be a folder or a file.
//
// Parse will first parse variables and then the rest; so that interpolation
// can happen.
//
// For each build block a packer.Build will be started, and for each builder,
// all provisioners and post-processors will be started.
//
// Parse then return a slice of packer.Builds; which are what packer core uses
// to run builds.
func (p *Parser) Parse(path string, vars map[string]string) ([]packer.Build, hcl.Diagnostics) {
cfg, diags := p.parse(path, vars)
if diags.HasErrors() {
return nil, diags
}
builds, moreDiags := p.getBuilds(cfg)
return builds, append(diags, moreDiags...)
}