packer-cn/builder/cloudstack/config.go

316 lines
12 KiB
Go
Raw Normal View History

//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config
2016-01-11 06:22:41 -05:00
package cloudstack
import (
"errors"
"fmt"
"os"
"time"
2017-04-04 16:39:01 -04:00
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
2016-01-11 06:22:41 -05:00
)
// Config holds all the details needed to configure the builder.
type Config struct {
common.PackerConfig `mapstructure:",squash"`
common.HTTPConfig `mapstructure:",squash"`
2016-01-11 06:22:41 -05:00
Comm communicator.Config `mapstructure:",squash"`
// The CloudStack API endpoint we will connect to. It can
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// finish. Defaults to 30m.
AsyncTimeout time.Duration `mapstructure:"async_timeout" required:"false"`
// Some cloud providers only allow HTTP GET calls
2019-06-06 10:29:25 -04:00
// 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.
2019-06-06 10:29:25 -04:00
// Defaults to false.
SSLNoVerify bool `mapstructure:"ssl_no_verify" required:"false"`
// List of CIDR's that will have access to the new
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// instance. This option is only available when using source_template.
DiskSize int64 `mapstructure:"disk_size" required:"false"`
2019-09-20 09:04:01 -04:00
// 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"`
2019-09-20 09:04:01 -04:00
// 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
2019-06-06 10:29:25 -04:00
// destroyed. Defaults to false.
Expunge bool `mapstructure:"expunge" required:"false"`
// The target hypervisor (e.g. XenServer, KVM) for
2019-06-06 10:29:25 -04:00
// the new template. This option is required when using source_iso.
Hypervisor string `mapstructure:"hypervisor" required:"false"`
// The name of the instance. Defaults to
2019-06-06 10:29:25 -04:00
// "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
2019-06-06 10:29:25 -04:00
// to.
Network string `mapstructure:"network" required:"true"`
// The name or ID of the project to deploy the instance
2019-06-06 10:29:25 -04:00
// to.
Project string `mapstructure:"project" required:"false"`
// The public IP address or it's ID used for
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// names to associate the instance with.
SecurityGroups []string `mapstructure:"security_groups" required:"false"`
// The name or ID of the service offering used
2019-06-06 10:29:25 -04:00
// for the instance.
ServiceOffering string `mapstructure:"service_offering" required:"true"`
// Set to true to prevent network
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// 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
2019-06-06 10:29:25 -04:00
// created.
Zone string `mapstructure:"zone" required:"true"`
// The name of the new template. Defaults to
2019-06-06 10:29:25 -04:00
// "packer-{{timestamp}}" where timestamp will be the current time.
TemplateName string `mapstructure:"template_name" required:"false"`
// The display text of the new template.
2019-06-06 10:29:25 -04:00
// Defaults to the template_name.
TemplateDisplayText string `mapstructure:"template_display_text" required:"false"`
// The name or ID of the template OS for the new
2019-06-06 10:29:25 -04:00
// template that will be created.
TemplateOS string `mapstructure:"template_os" required:"true"`
// Set to true to indicate that the template
2019-06-06 10:29:25 -04:00
// is featured. Defaults to false.
TemplateFeatured bool `mapstructure:"template_featured" required:"false"`
// Set to true to indicate that the template
2019-06-06 10:29:25 -04:00
// is available for all accounts. Defaults to false.
TemplatePublic bool `mapstructure:"template_public" required:"false"`
// Set to true to indicate the
2019-06-06 10:29:25 -04:00
// template should be password enabled. Defaults to false.
TemplatePasswordEnabled bool `mapstructure:"template_password_enabled" required:"false"`
// Set to true to indicate the template
2019-06-06 10:29:25 -04:00
// requires hardware-assisted virtualization. Defaults to false.
TemplateRequiresHVM bool `mapstructure:"template_requires_hvm" required:"false"`
// Set to true to indicate that the template
2019-06-06 10:29:25 -04:00
// 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"`
2016-01-11 06:22:41 -05:00
2019-04-18 10:44:33 -04:00
Tags map[string]string `mapstructure:"tags"`
ctx interpolate.Context
2016-01-11 06:22:41 -05:00
}
// NewConfig parses and validates the given config.
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
func (c *Config) Prepare(raws ...interface{}) error {
2016-01-11 06:22:41 -05:00
err := config.Decode(c, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"user_data",
},
},
2016-01-11 06:22:41 -05:00
}, raws...)
if err != nil {
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
return err
2016-01-11 06:22:41 -05:00
}
var errs *packer.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")
}
2016-01-11 06:22:41 -05:00
if c.AsyncTimeout == 0 {
c.AsyncTimeout = 30 * time.Minute
}
if len(c.CIDRList) == 0 {
c.CIDRList = []string{"0.0.0.0/0"}
}
2016-01-11 06:22:41 -05:00
if c.InstanceName == "" {
c.InstanceName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
}
if c.InstanceDisplayName == "" {
c.InstanceDisplayName = "Created by Packer"
}
2016-01-11 06:22:41 -05:00
if c.TemplateName == "" {
name, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Unable to parse template name: %s ", err))
}
c.TemplateName = name
}
if c.TemplateDisplayText == "" {
c.TemplateDisplayText = c.TemplateName
}
2017-07-26 15:34:11 -04:00
// 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.
2018-08-29 05:23:59 -04:00
if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" &&
2018-08-23 10:35:07 -04:00
c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" {
2018-08-28 11:47:19 -04:00
c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
2016-01-11 06:22:41 -05:00
// Process required parameters.
if c.APIURL == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a api_url must be specified"))
}
if c.APIKey == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a api_key must be specified"))
}
if c.SecretKey == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a secret_key must be specified"))
}
if c.Network == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a network must be specified"))
}
if c.CreateSecurityGroup && !c.Expunge {
errs = packer.MultiErrorAppend(errs, errors.New("auto creating a temporary security group requires expunge"))
}
2016-01-11 06:22:41 -05:00
if c.ServiceOffering == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a service_offering must be specified"))
}
if c.SourceISO == "" && c.SourceTemplate == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("either source_iso or source_template must be specified"))
}
if c.SourceISO != "" && c.SourceTemplate != "" {
errs = packer.MultiErrorAppend(
errs, errors.New("only one of source_iso or source_template can be specified"))
}
if c.SourceISO != "" && c.DiskOffering == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a disk_offering must be specified when using source_iso"))
}
if c.SourceISO != "" && c.Hypervisor == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("a hypervisor must be specified when using source_iso"))
}
if c.TemplateOS == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a template_os must be specified"))
}
if c.UserData != "" && c.UserDataFile != "" {
errs = packer.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 = packer.MultiErrorAppend(
errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
}
}
if c.Zone == "" {
errs = packer.MultiErrorAppend(errs, errors.New("a zone must be specified"))
}
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
// Check for errors and return if we have any.
if errs != nil && len(errs.Errors) > 0 {
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
return errs
2016-01-11 06:22:41 -05:00
}
build using HCL2 (#8423) This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ```
2019-12-17 05:25:56 -05:00
return nil
2016-01-11 06:22:41 -05:00
}