2018-01-12 17:12:15 -05:00
|
|
|
package classic
|
|
|
|
|
|
|
|
import (
|
2018-02-08 17:12:39 -05:00
|
|
|
"encoding/json"
|
2018-01-12 17:12:15 -05:00
|
|
|
"fmt"
|
2018-02-08 17:12:39 -05:00
|
|
|
"io/ioutil"
|
2018-01-12 18:44:40 -05:00
|
|
|
"net/url"
|
2018-02-08 15:52:44 -05:00
|
|
|
"os"
|
2018-03-30 16:01:48 -04:00
|
|
|
"regexp"
|
2018-02-21 17:33:00 -05:00
|
|
|
"time"
|
2018-01-12 17:12:15 -05:00
|
|
|
|
|
|
|
"github.com/hashicorp/packer/common"
|
|
|
|
"github.com/hashicorp/packer/helper/communicator"
|
|
|
|
"github.com/hashicorp/packer/helper/config"
|
2018-01-18 18:52:31 -05:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2018-01-12 17:12:15 -05:00
|
|
|
"github.com/hashicorp/packer/template/interpolate"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
common.PackerConfig `mapstructure:",squash"`
|
2018-09-12 19:16:08 -04:00
|
|
|
PVConfig `mapstructure:",squash"`
|
2018-01-12 17:12:15 -05:00
|
|
|
Comm communicator.Config `mapstructure:",squash"`
|
2018-02-08 17:12:39 -05:00
|
|
|
attribs map[string]interface{}
|
2018-01-12 17:12:15 -05:00
|
|
|
|
|
|
|
// Access config overrides
|
|
|
|
Username string `mapstructure:"username"`
|
|
|
|
Password string `mapstructure:"password"`
|
|
|
|
IdentityDomain string `mapstructure:"identity_domain"`
|
|
|
|
APIEndpoint string `mapstructure:"api_endpoint"`
|
2018-01-12 18:44:40 -05:00
|
|
|
apiEndpointURL *url.URL
|
2018-01-12 17:12:15 -05:00
|
|
|
|
|
|
|
// Image
|
2018-02-21 17:33:00 -05:00
|
|
|
ImageName string `mapstructure:"image_name"`
|
|
|
|
Shape string `mapstructure:"shape"`
|
|
|
|
SourceImageList string `mapstructure:"source_image_list"`
|
|
|
|
SnapshotTimeout time.Duration `mapstructure:"snapshot_timeout"`
|
|
|
|
DestImageList string `mapstructure:"dest_image_list"`
|
2018-03-13 03:12:16 -04:00
|
|
|
// Attributes and Attributes file are both optional and mutually exclusive.
|
2018-02-08 15:52:44 -05:00
|
|
|
Attributes string `mapstructure:"attributes"`
|
|
|
|
AttributesFile string `mapstructure:"attributes_file"`
|
2018-01-29 19:50:53 -05:00
|
|
|
// Optional; if you don't enter anything, the image list description
|
|
|
|
// will read "Packer-built image list"
|
2018-01-31 15:48:40 -05:00
|
|
|
DestImageListDescription string `mapstructure:"image_description"`
|
2018-01-23 14:07:04 -05:00
|
|
|
// Optional. Describes what computers are allowed to reach your instance
|
|
|
|
// via SSH. This whitelist must contain the computer you're running Packer
|
|
|
|
// from. It defaults to public-internet, meaning that you can SSH into your
|
|
|
|
// instance from anywhere as long as you have the right keys
|
|
|
|
SSHSourceList string `mapstructure:"ssh_source_list"`
|
2018-01-12 17:12:15 -05:00
|
|
|
|
|
|
|
ctx interpolate.Context
|
|
|
|
}
|
|
|
|
|
2018-09-06 01:44:31 -04:00
|
|
|
func (c *Config) Identifier(s string) string {
|
|
|
|
return fmt.Sprintf("/Compute-%s/%s/%s", c.IdentityDomain, c.Username, s)
|
|
|
|
}
|
|
|
|
|
2018-01-12 17:12:15 -05:00
|
|
|
func NewConfig(raws ...interface{}) (*Config, error) {
|
|
|
|
c := &Config{}
|
|
|
|
|
|
|
|
// Decode from template
|
|
|
|
err := config.Decode(c, &config.DecodeOpts{
|
|
|
|
Interpolate: true,
|
|
|
|
InterpolateContext: &c.ctx,
|
|
|
|
}, raws...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err)
|
|
|
|
}
|
|
|
|
|
2018-01-12 18:44:40 -05:00
|
|
|
c.apiEndpointURL, err = url.Parse(c.APIEndpoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Error parsing API Endpoint: %s", err)
|
|
|
|
}
|
2018-01-26 17:28:27 -05:00
|
|
|
// Set default source list
|
2018-01-23 14:07:04 -05:00
|
|
|
if c.SSHSourceList == "" {
|
|
|
|
c.SSHSourceList = "seciplist:/oracle/public/public-internet"
|
|
|
|
}
|
2018-01-26 17:28:27 -05:00
|
|
|
// Use default oracle username with sudo privileges
|
2018-01-26 17:27:18 -05:00
|
|
|
if c.Comm.SSHUsername == "" {
|
|
|
|
c.Comm.SSHUsername = "opc"
|
|
|
|
}
|
|
|
|
|
2018-02-21 17:33:00 -05:00
|
|
|
if c.SnapshotTimeout == 0 {
|
|
|
|
c.SnapshotTimeout = 20 * time.Minute
|
|
|
|
}
|
|
|
|
|
2018-09-12 19:16:08 -04:00
|
|
|
// if using a persistent volume
|
|
|
|
|
2018-01-26 11:53:24 -05:00
|
|
|
// Validate that all required fields are present
|
2018-01-18 18:52:31 -05:00
|
|
|
var errs *packer.MultiError
|
2018-01-26 11:53:24 -05:00
|
|
|
required := map[string]string{
|
2018-01-29 19:50:53 -05:00
|
|
|
"username": c.Username,
|
|
|
|
"password": c.Password,
|
|
|
|
"api_endpoint": c.APIEndpoint,
|
|
|
|
"identity_domain": c.IdentityDomain,
|
|
|
|
"source_image_list": c.SourceImageList,
|
2018-01-31 13:47:19 -05:00
|
|
|
"dest_image_list": c.DestImageList,
|
2018-01-29 19:50:53 -05:00
|
|
|
"shape": c.Shape,
|
2018-01-26 11:53:24 -05:00
|
|
|
}
|
|
|
|
for k, v := range required {
|
|
|
|
if v == "" {
|
|
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("You must specify a %s.", k))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 16:01:48 -04:00
|
|
|
// Object names can contain only alphanumeric characters, hyphens, underscores, and periods
|
|
|
|
reValidObject := regexp.MustCompile("^[a-zA-Z0-9-._/]+$")
|
2018-04-02 14:12:07 -04:00
|
|
|
var objectValidation = []struct {
|
|
|
|
name string
|
|
|
|
value string
|
|
|
|
}{
|
|
|
|
{"dest_image_list", c.DestImageList},
|
|
|
|
{"image_name", c.ImageName},
|
|
|
|
}
|
|
|
|
for _, ov := range objectValidation {
|
|
|
|
if !reValidObject.MatchString(ov.value) {
|
|
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("%s can contain only alphanumeric characters, hyphens, underscores, and periods.", ov.name))
|
|
|
|
}
|
2018-03-30 16:01:48 -04:00
|
|
|
}
|
|
|
|
|
2018-02-08 15:52:44 -05:00
|
|
|
if c.Attributes != "" && c.AttributesFile != "" {
|
|
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
|
|
|
|
} else if c.AttributesFile != "" {
|
|
|
|
if _, err := os.Stat(c.AttributesFile); err != nil {
|
|
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("attributes_file not found: %s", c.AttributesFile))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-26 11:53:24 -05:00
|
|
|
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
|
|
|
errs = packer.MultiErrorAppend(errs, es...)
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:27:18 -05:00
|
|
|
if errs != nil && len(errs.Errors) > 0 {
|
|
|
|
return nil, errs
|
|
|
|
}
|
|
|
|
|
2018-02-08 17:12:39 -05:00
|
|
|
// unpack attributes from json into config
|
|
|
|
var data map[string]interface{}
|
|
|
|
|
|
|
|
if c.Attributes != "" {
|
|
|
|
err := json.Unmarshal([]byte(c.Attributes), &data)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("Problem parsing json from attributes: %s", err)
|
|
|
|
packer.MultiErrorAppend(errs, err)
|
|
|
|
}
|
|
|
|
c.attribs = data
|
|
|
|
} else if c.AttributesFile != "" {
|
|
|
|
fidata, err := ioutil.ReadFile(c.AttributesFile)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("Problem reading attributes_file: %s", err)
|
|
|
|
packer.MultiErrorAppend(errs, err)
|
|
|
|
}
|
|
|
|
err = json.Unmarshal(fidata, &data)
|
|
|
|
c.attribs = data
|
|
|
|
if err != nil {
|
2018-03-13 03:12:16 -04:00
|
|
|
err = fmt.Errorf("Problem parsing json from attributes_file: %s", err)
|
2018-02-08 17:12:39 -05:00
|
|
|
packer.MultiErrorAppend(errs, err)
|
|
|
|
}
|
|
|
|
c.attribs = data
|
|
|
|
}
|
|
|
|
|
2018-01-12 17:12:15 -05:00
|
|
|
return c, nil
|
|
|
|
}
|