File: config.go

package info (click to toggle)
packer 1.6.6%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 33,156 kB
  • sloc: sh: 1,154; python: 619; makefile: 251; ruby: 205; xml: 97
file content (316 lines) | stat: -rw-r--r-- 12,725 bytes parent folder | download | duplicates (2)
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config

package cloudstack

import (
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/hashicorp/packer/packer-plugin-sdk/common"
	"github.com/hashicorp/packer/packer-plugin-sdk/communicator"
	"github.com/hashicorp/packer/packer-plugin-sdk/multistep/commonsteps"
	packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
	"github.com/hashicorp/packer/packer-plugin-sdk/template/config"
	"github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate"
	"github.com/hashicorp/packer/packer-plugin-sdk/uuid"
)

// Config holds all the details needed to configure the builder.
type Config struct {
	common.PackerConfig    `mapstructure:",squash"`
	commonsteps.HTTPConfig `mapstructure:",squash"`
	Comm                   communicator.Config `mapstructure:",squash"`

	// The CloudStack API endpoint we will connect to. It can
	// also be specified via environment variable CLOUDSTACK_API_URL, if set.
	APIURL string `mapstructure:"api_url" required:"true"`
	// The API key used to sign all API requests. It can also
	// be specified via environment variable CLOUDSTACK_API_KEY, if set.
	APIKey string `mapstructure:"api_key" required:"true"`
	// The secret key used to sign all API requests. It
	// can also be specified via environment variable CLOUDSTACK_SECRET_KEY, if
	// set.
	SecretKey string `mapstructure:"secret_key" required:"true"`
	// The time duration to wait for async calls to
	// finish. Defaults to 30m.
	AsyncTimeout time.Duration `mapstructure:"async_timeout" required:"false"`
	// Some cloud providers only allow HTTP GET calls
	// to their CloudStack API. If using such a provider, you need to set this to
	// true in order for the provider to only make GET calls and no POST calls.
	HTTPGetOnly bool `mapstructure:"http_get_only" required:"false"`
	// Set to true to skip SSL verification.
	// Defaults to false.
	SSLNoVerify bool `mapstructure:"ssl_no_verify" required:"false"`
	// List of CIDR's that will have access to the new
	// instance. This is needed in order for any provisioners to be able to
	// connect to the instance. Defaults to [ "0.0.0.0/0" ]. Only required when
	// use_local_ip_address is false.
	CIDRList []string `mapstructure:"cidr_list" required:"false"`
	// If true a temporary security group
	// will be created which allows traffic towards the instance from the
	// cidr_list. This option will be ignored if security_groups is also
	// defined. Requires expunge set to true. Defaults to false.
	CreateSecurityGroup bool `mapstructure:"create_security_group" required:"false"`
	// The name or ID of the disk offering used for the
	// instance. This option is only available (and also required) when using
	// source_iso.
	DiskOffering string `mapstructure:"disk_offering" required:"false"`
	// The size (in GB) of the root disk of the new
	// instance. This option is only available when using source_template.
	DiskSize int64 `mapstructure:"disk_size" required:"false"`
	// If `true` make a call to the CloudStack API, after loading image to
	// cache, requesting to check and detach ISO file (if any) currently
	// attached to a virtual machine. Defaults to `false`. This option is only
	// available when using `source_iso`.
	EjectISO bool `mapstructure:"eject_iso"`
	// Configure the duration time to wait, making sure virtual machine is able
	// to finish installing OS before it ejects safely. Requires `eject_iso`
	// set to `true` and this option is only available when using `source_iso`.
	EjectISODelay time.Duration `mapstructure:"eject_iso_delay"`
	// Set to true to expunge the instance when it is
	// destroyed. Defaults to false.
	Expunge bool `mapstructure:"expunge" required:"false"`
	// The target hypervisor (e.g. XenServer, KVM) for
	// the new template. This option is required when using source_iso.
	Hypervisor string `mapstructure:"hypervisor" required:"false"`
	// The name of the instance. Defaults to
	// "packer-UUID" where UUID is dynamically generated.
	InstanceName string `mapstructure:"instance_name" required:"false"`
	// The display name of the instance. Defaults to "Created by Packer".
	InstanceDisplayName string `mapstructure:"instance_display_name" required:"false"`
	// The name or ID of the network to connect the instance
	// to.
	Network string `mapstructure:"network" required:"true"`
	// The name or ID of the project to deploy the instance
	// to.
	Project string `mapstructure:"project" required:"false"`
	// The public IP address or it's ID used for
	// connecting any provisioners to. If not provided, a temporary public IP
	// address will be associated and released during the Packer run.
	PublicIPAddress string `mapstructure:"public_ip_address" required:"false"`
	// The fixed port you want to configure in the port
	// forwarding rule. Set this attribute if you do not want to use the a random
	// public port.
	PublicPort int `mapstructure:"public_port" required:"false"`
	// A list of security group IDs or
	// names to associate the instance with.
	SecurityGroups []string `mapstructure:"security_groups" required:"false"`
	// The name or ID of the service offering used
	// for the instance.
	ServiceOffering string `mapstructure:"service_offering" required:"true"`
	// Set to true to prevent network
	// ACLs or firewall rules creation. Defaults to false.
	PreventFirewallChanges bool `mapstructure:"prevent_firewall_changes" required:"false"`
	// The name or ID of an ISO that will be mounted
	// before booting the instance. This option is mutually exclusive with
	// source_template. When using source_iso, both disk_offering and
	// hypervisor are required.
	SourceISO string `mapstructure:"source_iso" required:"true"`
	// The name or ID of the template used as base
	// template for the instance. This option is mutually exclusive with
	// source_iso.
	SourceTemplate string `mapstructure:"source_template" required:"true"`
	// The name of the temporary SSH key pair
	// to generate. By default, Packer generates a name that looks like
	// `packer_<UUID>`, where `<UUID>` is a 36 character unique identifier.
	TemporaryKeypairName string `mapstructure:"temporary_keypair_name" required:"false"`
	// Set to true to indicate that the
	// provisioners should connect to the local IP address of the instance.
	UseLocalIPAddress bool `mapstructure:"use_local_ip_address" required:"false"`
	// User data to launch with the instance. This is a
	// template engine; see "User Data" bellow for
	// more details. Packer will not automatically wait for a user script to
	// finish before shutting down the instance this must be handled in a
	// provisioner.
	UserData string `mapstructure:"user_data" required:"false"`
	// Path to a file that will be used for the user
	// data when launching the instance. This file will be parsed as a template
	// engine see User Data bellow for more
	// details.
	UserDataFile string `mapstructure:"user_data_file" required:"false"`
	// The name or ID of the zone where the instance will be
	// created.
	Zone string `mapstructure:"zone" required:"true"`
	// The name of the new template. Defaults to
	// `packer-{{timestamp}}` where timestamp will be the current time.
	TemplateName string `mapstructure:"template_name" required:"false"`
	// The display text of the new template.
	// Defaults to the template_name.
	TemplateDisplayText string `mapstructure:"template_display_text" required:"false"`
	// The name or ID of the template OS for the new
	// template that will be created.
	TemplateOS string `mapstructure:"template_os" required:"true"`
	// Set to true to indicate that the template
	// is featured. Defaults to false.
	TemplateFeatured bool `mapstructure:"template_featured" required:"false"`
	// Set to true to indicate that the template
	// is available for all accounts. Defaults to false.
	TemplatePublic bool `mapstructure:"template_public" required:"false"`
	// Set to true to indicate the
	// template should be password enabled. Defaults to false.
	TemplatePasswordEnabled bool `mapstructure:"template_password_enabled" required:"false"`
	// Set to true to indicate the template
	// requires hardware-assisted virtualization. Defaults to false.
	TemplateRequiresHVM bool `mapstructure:"template_requires_hvm" required:"false"`
	// Set to true to indicate that the template
	// contains tools to support dynamic scaling of VM cpu/memory. Defaults to
	// false.
	TemplateScalable bool `mapstructure:"template_scalable" required:"false"`
	//
	TemplateTag string `mapstructure:"template_tag"`

	Tags map[string]string `mapstructure:"tags"`

	ctx interpolate.Context
}

// NewConfig parses and validates the given config.
func (c *Config) Prepare(raws ...interface{}) error {
	err := config.Decode(c, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &c.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{
				"user_data",
			},
		},
	}, raws...)
	if err != nil {
		return err
	}

	var errs *packersdk.MultiError

	// Set some defaults.
	if c.APIURL == "" {
		// Default to environment variable for api_url, if it exists
		c.APIURL = os.Getenv("CLOUDSTACK_API_URL")
	}

	if c.APIKey == "" {
		// Default to environment variable for api_key, if it exists
		c.APIKey = os.Getenv("CLOUDSTACK_API_KEY")
	}

	if c.SecretKey == "" {
		// Default to environment variable for secret_key, if it exists
		c.SecretKey = os.Getenv("CLOUDSTACK_SECRET_KEY")
	}

	if c.AsyncTimeout == 0 {
		c.AsyncTimeout = 30 * time.Minute
	}

	if len(c.CIDRList) == 0 {
		c.CIDRList = []string{"0.0.0.0/0"}
	}

	if c.InstanceName == "" {
		c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
	}

	if c.InstanceDisplayName == "" {
		c.InstanceDisplayName = "Created by Packer"
	}

	if c.TemplateName == "" {
		name, err := interpolate.Render("packer-{{timestamp}}", nil)
		if err != nil {
			errs = packersdk.MultiErrorAppend(errs,
				fmt.Errorf("Unable to parse template name: %s ", err))
		}

		c.TemplateName = name
	}

	if c.TemplateDisplayText == "" {
		c.TemplateDisplayText = c.TemplateName
	}

	// If we are not given an explicit keypair, ssh_password or ssh_private_key_file,
	// then create a temporary one, but only if the temporary_keypair_name has not
	// been provided.
	if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" &&
		c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" {
		c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
	}

	// Process required parameters.
	if c.APIURL == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a api_url must be specified"))
	}

	if c.APIKey == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a api_key must be specified"))
	}

	if c.SecretKey == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a secret_key must be specified"))
	}

	if c.Network == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a network must be specified"))
	}

	if c.CreateSecurityGroup && !c.Expunge {
		errs = packersdk.MultiErrorAppend(errs, errors.New("auto creating a temporary security group requires expunge"))
	}

	if c.ServiceOffering == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a service_offering must be specified"))
	}

	if c.SourceISO == "" && c.SourceTemplate == "" {
		errs = packersdk.MultiErrorAppend(
			errs, errors.New("either source_iso or source_template must be specified"))
	}

	if c.SourceISO != "" && c.SourceTemplate != "" {
		errs = packersdk.MultiErrorAppend(
			errs, errors.New("only one of source_iso or source_template can be specified"))
	}

	if c.SourceISO != "" && c.DiskOffering == "" {
		errs = packersdk.MultiErrorAppend(
			errs, errors.New("a disk_offering must be specified when using source_iso"))
	}

	if c.SourceISO != "" && c.Hypervisor == "" {
		errs = packersdk.MultiErrorAppend(
			errs, errors.New("a hypervisor must be specified when using source_iso"))
	}

	if c.TemplateOS == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a template_os must be specified"))
	}

	if c.UserData != "" && c.UserDataFile != "" {
		errs = packersdk.MultiErrorAppend(
			errs, errors.New("only one of user_data or user_data_file can be specified"))
	}

	if c.UserDataFile != "" {
		if _, err := os.Stat(c.UserDataFile); err != nil {
			errs = packersdk.MultiErrorAppend(
				errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
		}
	}

	if c.Zone == "" {
		errs = packersdk.MultiErrorAppend(errs, errors.New("a zone must be specified"))
	}

	if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
		errs = packersdk.MultiErrorAppend(errs, es...)
	}

	// Check for errors and return if we have any.
	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}

	return nil
}