github.phpd.cn/hashicorp/packer@v1.3.2/builder/digitalocean/config.go (about) 1 package digitalocean 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "regexp" 8 "time" 9 10 "github.com/hashicorp/packer/common" 11 "github.com/hashicorp/packer/common/uuid" 12 "github.com/hashicorp/packer/helper/communicator" 13 "github.com/hashicorp/packer/helper/config" 14 "github.com/hashicorp/packer/packer" 15 "github.com/hashicorp/packer/template/interpolate" 16 "github.com/mitchellh/mapstructure" 17 ) 18 19 type Config struct { 20 common.PackerConfig `mapstructure:",squash"` 21 Comm communicator.Config `mapstructure:",squash"` 22 23 APIToken string `mapstructure:"api_token"` 24 APIURL string `mapstructure:"api_url"` 25 26 Region string `mapstructure:"region"` 27 Size string `mapstructure:"size"` 28 Image string `mapstructure:"image"` 29 30 PrivateNetworking bool `mapstructure:"private_networking"` 31 Monitoring bool `mapstructure:"monitoring"` 32 IPv6 bool `mapstructure:"ipv6"` 33 SnapshotName string `mapstructure:"snapshot_name"` 34 SnapshotRegions []string `mapstructure:"snapshot_regions"` 35 StateTimeout time.Duration `mapstructure:"state_timeout"` 36 DropletName string `mapstructure:"droplet_name"` 37 UserData string `mapstructure:"user_data"` 38 UserDataFile string `mapstructure:"user_data_file"` 39 Tags []string `mapstructure:"tags"` 40 41 ctx interpolate.Context 42 } 43 44 func NewConfig(raws ...interface{}) (*Config, []string, error) { 45 c := new(Config) 46 47 var md mapstructure.Metadata 48 err := config.Decode(c, &config.DecodeOpts{ 49 Metadata: &md, 50 Interpolate: true, 51 InterpolateContext: &c.ctx, 52 InterpolateFilter: &interpolate.RenderFilter{ 53 Exclude: []string{ 54 "run_command", 55 }, 56 }, 57 }, raws...) 58 if err != nil { 59 return nil, nil, err 60 } 61 62 // Defaults 63 if c.APIToken == "" { 64 // Default to environment variable for api_token, if it exists 65 c.APIToken = os.Getenv("DIGITALOCEAN_API_TOKEN") 66 } 67 if c.APIURL == "" { 68 c.APIURL = os.Getenv("DIGITALOCEAN_API_URL") 69 } 70 if c.SnapshotName == "" { 71 def, err := interpolate.Render("packer-{{timestamp}}", nil) 72 if err != nil { 73 panic(err) 74 } 75 76 // Default to packer-{{ unix timestamp (utc) }} 77 c.SnapshotName = def 78 } 79 80 if c.DropletName == "" { 81 // Default to packer-[time-ordered-uuid] 82 c.DropletName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) 83 } 84 85 if c.StateTimeout == 0 { 86 // Default to 6 minute timeouts waiting for 87 // desired state. i.e waiting for droplet to become active 88 c.StateTimeout = 6 * time.Minute 89 } 90 91 var errs *packer.MultiError 92 if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { 93 errs = packer.MultiErrorAppend(errs, es...) 94 } 95 if c.APIToken == "" { 96 // Required configurations that will display errors if not set 97 errs = packer.MultiErrorAppend( 98 errs, errors.New("api_token for auth must be specified")) 99 } 100 101 if c.Region == "" { 102 errs = packer.MultiErrorAppend( 103 errs, errors.New("region is required")) 104 } 105 106 if c.Size == "" { 107 errs = packer.MultiErrorAppend( 108 errs, errors.New("size is required")) 109 } 110 111 if c.Image == "" { 112 errs = packer.MultiErrorAppend( 113 errs, errors.New("image is required")) 114 } 115 116 if c.UserData != "" && c.UserDataFile != "" { 117 errs = packer.MultiErrorAppend( 118 errs, errors.New("only one of user_data or user_data_file can be specified")) 119 } else if c.UserDataFile != "" { 120 if _, err := os.Stat(c.UserDataFile); err != nil { 121 errs = packer.MultiErrorAppend( 122 errs, errors.New(fmt.Sprintf("user_data_file not found: %s", c.UserDataFile))) 123 } 124 } 125 126 if c.Tags == nil { 127 c.Tags = make([]string, 0) 128 } 129 tagRe := regexp.MustCompile("^[[:alnum:]:_-]{1,255}$") 130 131 for _, t := range c.Tags { 132 if !tagRe.MatchString(t) { 133 errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("invalid tag: %s", t))) 134 } 135 } 136 137 if errs != nil && len(errs.Errors) > 0 { 138 return nil, nil, errs 139 } 140 141 packer.LogSecretFilter.Set(c.APIToken) 142 return c, nil, nil 143 }