github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/oracle/oci/config.go (about) 1 package oci 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 9 client "github.com/hashicorp/packer/builder/oracle/oci/client" 10 "github.com/hashicorp/packer/common" 11 "github.com/hashicorp/packer/helper/communicator" 12 "github.com/hashicorp/packer/helper/config" 13 "github.com/hashicorp/packer/packer" 14 "github.com/hashicorp/packer/template/interpolate" 15 16 "github.com/mitchellh/go-homedir" 17 ) 18 19 type Config struct { 20 common.PackerConfig `mapstructure:",squash"` 21 Comm communicator.Config `mapstructure:",squash"` 22 23 AccessCfg *client.Config 24 25 AccessCfgFile string `mapstructure:"access_cfg_file"` 26 AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"` 27 28 // Access config overrides 29 UserID string `mapstructure:"user_ocid"` 30 TenancyID string `mapstructure:"tenancy_ocid"` 31 Region string `mapstructure:"region"` 32 Fingerprint string `mapstructure:"fingerprint"` 33 KeyFile string `mapstructure:"key_file"` 34 PassPhrase string `mapstructure:"pass_phrase"` 35 36 AvailabilityDomain string `mapstructure:"availability_domain"` 37 CompartmentID string `mapstructure:"compartment_ocid"` 38 39 // Image 40 BaseImageID string `mapstructure:"base_image_ocid"` 41 Shape string `mapstructure:"shape"` 42 ImageName string `mapstructure:"image_name"` 43 44 // Networking 45 SubnetID string `mapstructure:"subnet_ocid"` 46 47 ctx interpolate.Context 48 } 49 50 func NewConfig(raws ...interface{}) (*Config, error) { 51 c := &Config{} 52 53 // Decode from template 54 err := config.Decode(c, &config.DecodeOpts{ 55 Interpolate: true, 56 InterpolateContext: &c.ctx, 57 }, raws...) 58 if err != nil { 59 return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) 60 } 61 62 // Determine where the SDK config is located 63 var accessCfgFile string 64 if c.AccessCfgFile != "" { 65 accessCfgFile = c.AccessCfgFile 66 } else { 67 accessCfgFile, err = getDefaultOCISettingsPath() 68 if err != nil { 69 accessCfgFile = "" // Access cfg might be in template 70 } 71 } 72 73 accessCfg := &client.Config{} 74 75 if accessCfgFile != "" { 76 loadedAccessCfgs, err := client.LoadConfigsFromFile(accessCfgFile) 77 if err != nil { 78 return nil, fmt.Errorf("Invalid config file %s: %s", accessCfgFile, err) 79 } 80 cfgAccount := "DEFAULT" 81 if c.AccessCfgFileAccount != "" { 82 cfgAccount = c.AccessCfgFileAccount 83 } 84 85 var ok bool 86 accessCfg, ok = loadedAccessCfgs[cfgAccount] 87 if !ok { 88 return nil, fmt.Errorf("No account section '%s' found in config file %s", cfgAccount, accessCfgFile) 89 } 90 } 91 92 // Override SDK client config with any non-empty template properties 93 94 if c.UserID != "" { 95 accessCfg.User = c.UserID 96 } 97 98 if c.TenancyID != "" { 99 accessCfg.Tenancy = c.TenancyID 100 } 101 102 if c.Region != "" { 103 accessCfg.Region = c.Region 104 } else { 105 accessCfg.Region = "us-phoenix-1" 106 } 107 108 if c.Fingerprint != "" { 109 accessCfg.Fingerprint = c.Fingerprint 110 } 111 112 if c.PassPhrase != "" { 113 accessCfg.PassPhrase = c.PassPhrase 114 } 115 116 if c.KeyFile != "" { 117 accessCfg.KeyFile = c.KeyFile 118 accessCfg.Key, err = client.LoadPrivateKey(accessCfg) 119 if err != nil { 120 return nil, fmt.Errorf("Failed to load private key %s : %s", accessCfg.KeyFile, err) 121 } 122 } 123 124 var errs *packer.MultiError 125 if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { 126 errs = packer.MultiErrorAppend(errs, es...) 127 } 128 129 // Required AccessCfg configuration options 130 131 if accessCfg.User == "" { 132 errs = packer.MultiErrorAppend( 133 errs, errors.New("'user_ocid' must be specified")) 134 } 135 136 if accessCfg.Tenancy == "" { 137 errs = packer.MultiErrorAppend( 138 errs, errors.New("'tenancy_ocid' must be specified")) 139 } 140 141 if accessCfg.Region == "" { 142 errs = packer.MultiErrorAppend( 143 errs, errors.New("'region' must be specified")) 144 } 145 146 if accessCfg.Fingerprint == "" { 147 errs = packer.MultiErrorAppend( 148 errs, errors.New("'fingerprint' must be specified")) 149 } 150 151 if accessCfg.Key == nil { 152 errs = packer.MultiErrorAppend( 153 errs, errors.New("'key_file' must be specified")) 154 } 155 156 c.AccessCfg = accessCfg 157 158 // Required non AccessCfg configuration options 159 160 if c.AvailabilityDomain == "" { 161 errs = packer.MultiErrorAppend( 162 errs, errors.New("'availability_domain' must be specified")) 163 } 164 165 if c.CompartmentID == "" { 166 c.CompartmentID = accessCfg.Tenancy 167 } 168 169 if c.Shape == "" { 170 errs = packer.MultiErrorAppend( 171 errs, errors.New("'shape' must be specified")) 172 } 173 174 if c.SubnetID == "" { 175 errs = packer.MultiErrorAppend( 176 errs, errors.New("'subnet_ocid' must be specified")) 177 } 178 179 if c.BaseImageID == "" { 180 errs = packer.MultiErrorAppend( 181 errs, errors.New("'base_image_ocid' must be specified")) 182 } 183 184 if c.ImageName == "" { 185 name, err := interpolate.Render("packer-{{timestamp}}", nil) 186 if err != nil { 187 errs = packer.MultiErrorAppend(errs, 188 fmt.Errorf("unable to parse image name: %s", err)) 189 } else { 190 c.ImageName = name 191 } 192 } 193 194 if errs != nil && len(errs.Errors) > 0 { 195 return nil, errs 196 } 197 198 return c, nil 199 } 200 201 // getDefaultOCISettingsPath uses mitchellh/go-homedir to compute the default 202 // config file location ($HOME/.oci/config). 203 func getDefaultOCISettingsPath() (string, error) { 204 home, err := homedir.Dir() 205 if err != nil { 206 return "", err 207 } 208 209 path := filepath.Join(home, ".oci", "config") 210 if _, err := os.Stat(path); err != nil { 211 return "", err 212 } 213 214 return path, nil 215 }