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"`
|
2017-07-06 05:59:02 -04:00
|
|
|
common.HTTPConfig `mapstructure:",squash"`
|
2016-01-11 06:22:41 -05:00
|
|
|
Comm communicator.Config `mapstructure:",squash"`
|
|
|
|
|
|
|
|
APIURL string `mapstructure:"api_url"`
|
|
|
|
APIKey string `mapstructure:"api_key"`
|
|
|
|
SecretKey string `mapstructure:"secret_key"`
|
|
|
|
AsyncTimeout time.Duration `mapstructure:"async_timeout"`
|
|
|
|
HTTPGetOnly bool `mapstructure:"http_get_only"`
|
|
|
|
SSLNoVerify bool `mapstructure:"ssl_no_verify"`
|
|
|
|
|
2017-07-26 15:34:11 -04:00
|
|
|
CIDRList []string `mapstructure:"cidr_list"`
|
2017-07-18 03:08:11 -04:00
|
|
|
CreateSecurityGroup bool `mapstructure:"create_security_group"`
|
2017-07-26 15:34:11 -04:00
|
|
|
DiskOffering string `mapstructure:"disk_offering"`
|
|
|
|
DiskSize int64 `mapstructure:"disk_size"`
|
|
|
|
Expunge bool `mapstructure:"expunge"`
|
|
|
|
Hypervisor string `mapstructure:"hypervisor"`
|
|
|
|
InstanceName string `mapstructure:"instance_name"`
|
|
|
|
Keypair string `mapstructure:"keypair"`
|
|
|
|
Network string `mapstructure:"network"`
|
|
|
|
Project string `mapstructure:"project"`
|
|
|
|
PublicIPAddress string `mapstructure:"public_ip_address"`
|
2017-07-18 03:08:11 -04:00
|
|
|
SecurityGroups []string `mapstructure:"security_groups"`
|
2017-07-26 15:34:11 -04:00
|
|
|
ServiceOffering string `mapstructure:"service_offering"`
|
|
|
|
SourceISO string `mapstructure:"source_iso"`
|
2017-07-18 03:08:11 -04:00
|
|
|
SourceTemplate string `mapstructure:"source_template"`
|
|
|
|
TemporaryKeypairName string `mapstructure:"temporary_keypair_name"`
|
|
|
|
UseLocalIPAddress bool `mapstructure:"use_local_ip_address"`
|
2017-07-26 15:34:11 -04:00
|
|
|
UserData string `mapstructure:"user_data"`
|
|
|
|
UserDataFile string `mapstructure:"user_data_file"`
|
|
|
|
Zone string `mapstructure:"zone"`
|
2016-01-11 06:22:41 -05:00
|
|
|
|
|
|
|
TemplateName string `mapstructure:"template_name"`
|
|
|
|
TemplateDisplayText string `mapstructure:"template_display_text"`
|
|
|
|
TemplateOS string `mapstructure:"template_os"`
|
|
|
|
TemplateFeatured bool `mapstructure:"template_featured"`
|
|
|
|
TemplatePublic bool `mapstructure:"template_public"`
|
|
|
|
TemplatePasswordEnabled bool `mapstructure:"template_password_enabled"`
|
|
|
|
TemplateRequiresHVM bool `mapstructure:"template_requires_hvm"`
|
|
|
|
TemplateScalable bool `mapstructure:"template_scalable"`
|
|
|
|
TemplateTag string `mapstructure:"template_tag"`
|
|
|
|
|
2017-07-17 03:05:12 -04:00
|
|
|
ctx interpolate.Context
|
2016-01-11 06:22:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewConfig parses and validates the given config.
|
|
|
|
func NewConfig(raws ...interface{}) (*Config, error) {
|
|
|
|
c := new(Config)
|
|
|
|
err := config.Decode(c, &config.DecodeOpts{
|
|
|
|
Interpolate: true,
|
|
|
|
InterpolateContext: &c.ctx,
|
2017-07-06 05:59:02 -04:00
|
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
|
|
|
Exclude: []string{
|
|
|
|
"user_data",
|
|
|
|
},
|
|
|
|
},
|
2016-01-11 06:22:41 -05:00
|
|
|
}, raws...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var errs *packer.MultiError
|
|
|
|
|
|
|
|
// Set some defaults.
|
2017-07-14 01:32:41 -04:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-07-18 03:08:11 -04:00
|
|
|
if len(c.CIDRList) == 0 {
|
2017-07-14 01:50:54 -04:00
|
|
|
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.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.
|
|
|
|
if c.Keypair == "" && c.TemporaryKeypairName == "" &&
|
2017-07-16 02:26:48 -04:00
|
|
|
c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" {
|
2017-07-26 15:34:11 -04:00
|
|
|
c.TemporaryKeypairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
|
2017-07-16 02:26:48 -04:00
|
|
|
}
|
|
|
|
|
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"))
|
|
|
|
}
|
|
|
|
|
2017-07-18 03:08:11 -04:00
|
|
|
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 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|