File: template.go

package info (click to toggle)
packer 1.3.4%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 8,324 kB
  • sloc: python: 619; sh: 557; makefile: 111
file content (203 lines) | stat: -rw-r--r-- 4,945 bytes parent folder | download
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package template

import (
	"errors"
	"fmt"
	"time"

	multierror "github.com/hashicorp/go-multierror"
)

// Template represents the parsed template that is used to configure
// Packer builds.
type Template struct {
	// Path is the path to the template. This will be blank if Parse is
	// used, but will be automatically populated by ParseFile.
	Path string

	Description string
	MinVersion  string

	Variables          map[string]*Variable
	SensitiveVariables []*Variable
	Builders           map[string]*Builder
	Provisioners       []*Provisioner
	PostProcessors     [][]*PostProcessor
	Push               Push

	// RawContents is just the raw data for this template
	RawContents []byte
}

// Builder represents a builder configured in the template
type Builder struct {
	Name   string
	Type   string
	Config map[string]interface{}
}

// PostProcessor represents a post-processor within the template.
type PostProcessor struct {
	OnlyExcept `mapstructure:",squash"`

	Name              string
	Type              string
	KeepInputArtifact bool `mapstructure:"keep_input_artifact"`
	Config            map[string]interface{}
}

// Provisioner represents a provisioner within the template.
type Provisioner struct {
	OnlyExcept `mapstructure:",squash"`

	Type        string
	Config      map[string]interface{}
	Override    map[string]interface{}
	PauseBefore time.Duration `mapstructure:"pause_before"`
}

// Push represents the configuration for pushing the template to Atlas.
type Push struct {
	Name    string
	Address string
	BaseDir string `mapstructure:"base_dir"`
	Include []string
	Exclude []string
	Token   string
	VCS     bool
}

// Variable represents a variable within the template
type Variable struct {
	Default  string
	Required bool
}

// OnlyExcept is a struct that is meant to be embedded that contains the
// logic required for "only" and "except" meta-parameters.
type OnlyExcept struct {
	Only   []string
	Except []string
}

//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------

// Validate does some basic validation of the template on top of the
// validation that occurs while parsing. If possible, we try to defer
// validation to here. The validation errors that occur during parsing
// are the minimal necessary to make sure parsing builds a reasonable
// Template structure.
func (t *Template) Validate() error {
	var err error

	// At least one builder must be defined
	if len(t.Builders) == 0 {
		err = multierror.Append(err, errors.New(
			"at least one builder must be defined"))
	}

	// Verify that the provisioner overrides target builders that exist
	for i, p := range t.Provisioners {
		// Validate only/except
		if verr := p.OnlyExcept.Validate(t); verr != nil {
			for _, e := range multierror.Append(verr).Errors {
				err = multierror.Append(err, fmt.Errorf(
					"provisioner %d: %s", i+1, e))
			}
		}

		// Validate overrides
		for name := range p.Override {
			if _, ok := t.Builders[name]; !ok {
				err = multierror.Append(err, fmt.Errorf(
					"provisioner %d: override '%s' doesn't exist",
					i+1, name))
			}
		}
	}

	// Verify post-processors
	for i, chain := range t.PostProcessors {
		for j, p := range chain {
			// Validate only/except
			if verr := p.OnlyExcept.Validate(t); verr != nil {
				for _, e := range multierror.Append(verr).Errors {
					err = multierror.Append(err, fmt.Errorf(
						"post-processor %d.%d: %s", i+1, j+1, e))
				}
			}
		}
	}

	return err
}

// Skip says whether or not to skip the build with the given name.
func (o *OnlyExcept) Skip(n string) bool {
	if len(o.Only) > 0 {
		for _, v := range o.Only {
			if v == n {
				return false
			}
		}

		return true
	}

	if len(o.Except) > 0 {
		for _, v := range o.Except {
			if v == n {
				return true
			}
		}

		return false
	}

	return false
}

// Validate validates that the OnlyExcept settings are correct for a thing.
func (o *OnlyExcept) Validate(t *Template) error {
	if len(o.Only) > 0 && len(o.Except) > 0 {
		return errors.New("only one of 'only' or 'except' may be specified")
	}

	var err error
	for _, n := range o.Only {
		if _, ok := t.Builders[n]; !ok {
			err = multierror.Append(err, fmt.Errorf(
				"'only' specified builder '%s' not found", n))
		}
	}
	for _, n := range o.Except {
		if _, ok := t.Builders[n]; !ok {
			err = multierror.Append(err, fmt.Errorf(
				"'except' specified builder '%s' not found", n))
		}
	}

	return err
}

//-------------------------------------------------------------------
// GoStringer
//-------------------------------------------------------------------

func (b *Builder) GoString() string {
	return fmt.Sprintf("*%#v", *b)
}

func (p *Provisioner) GoString() string {
	return fmt.Sprintf("*%#v", *p)
}

func (p *PostProcessor) GoString() string {
	return fmt.Sprintf("*%#v", *p)
}

func (v *Variable) GoString() string {
	return fmt.Sprintf("*%#v", *v)
}