github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/oracle/oci/config.go (about) 1 package oci 2 3 import ( 4 "encoding/base64" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "os" 10 "path/filepath" 11 12 "github.com/hashicorp/packer/common" 13 "github.com/hashicorp/packer/helper/communicator" 14 "github.com/hashicorp/packer/helper/config" 15 "github.com/hashicorp/packer/packer" 16 "github.com/hashicorp/packer/template/interpolate" 17 ocicommon "github.com/oracle/oci-go-sdk/common" 18 19 "github.com/mitchellh/go-homedir" 20 ) 21 22 type Config struct { 23 common.PackerConfig `mapstructure:",squash"` 24 Comm communicator.Config `mapstructure:",squash"` 25 26 ConfigProvider ocicommon.ConfigurationProvider 27 28 AccessCfgFile string `mapstructure:"access_cfg_file"` 29 AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"` 30 31 // Access config overrides 32 UserID string `mapstructure:"user_ocid"` 33 TenancyID string `mapstructure:"tenancy_ocid"` 34 Region string `mapstructure:"region"` 35 Fingerprint string `mapstructure:"fingerprint"` 36 KeyFile string `mapstructure:"key_file"` 37 PassPhrase string `mapstructure:"pass_phrase"` 38 UsePrivateIP bool `mapstructure:"use_private_ip"` 39 40 AvailabilityDomain string `mapstructure:"availability_domain"` 41 CompartmentID string `mapstructure:"compartment_ocid"` 42 43 // Image 44 BaseImageID string `mapstructure:"base_image_ocid"` 45 Shape string `mapstructure:"shape"` 46 ImageName string `mapstructure:"image_name"` 47 48 // Instance 49 InstanceName string `mapstructure:"instance_name"` 50 51 // UserData and UserDataFile file are both optional and mutually exclusive. 52 UserData string `mapstructure:"user_data"` 53 UserDataFile string `mapstructure:"user_data_file"` 54 55 // Networking 56 SubnetID string `mapstructure:"subnet_ocid"` 57 58 ctx interpolate.Context 59 } 60 61 func NewConfig(raws ...interface{}) (*Config, error) { 62 c := &Config{} 63 64 // Decode from template 65 err := config.Decode(c, &config.DecodeOpts{ 66 Interpolate: true, 67 InterpolateContext: &c.ctx, 68 }, raws...) 69 if err != nil { 70 return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) 71 } 72 73 // Determine where the SDK config is located 74 if c.AccessCfgFile == "" { 75 c.AccessCfgFile, err = getDefaultOCISettingsPath() 76 if err != nil { 77 log.Println("Default OCI settings file not found") 78 } 79 } 80 81 if c.AccessCfgFileAccount == "" { 82 c.AccessCfgFileAccount = "DEFAULT" 83 } 84 85 var keyContent []byte 86 if c.KeyFile != "" { 87 path, err := homedir.Expand(c.KeyFile) 88 if err != nil { 89 return nil, err 90 } 91 92 // Read API signing key 93 keyContent, err = ioutil.ReadFile(path) 94 if err != nil { 95 return nil, err 96 } 97 } 98 99 fileProvider, _ := ocicommon.ConfigurationProviderFromFileWithProfile(c.AccessCfgFile, c.AccessCfgFileAccount, c.PassPhrase) 100 if c.Region == "" { 101 var region string 102 if fileProvider != nil { 103 region, _ = fileProvider.Region() 104 } 105 if region == "" { 106 c.Region = "us-phoenix-1" 107 } 108 } 109 110 providers := []ocicommon.ConfigurationProvider{ 111 NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase), 112 } 113 114 if fileProvider != nil { 115 providers = append(providers, fileProvider) 116 } 117 118 // Load API access configuration from SDK 119 configProvider, err := ocicommon.ComposingConfigurationProvider(providers) 120 if err != nil { 121 return nil, err 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 if userOCID, _ := configProvider.UserOCID(); userOCID == "" { 130 errs = packer.MultiErrorAppend( 131 errs, errors.New("'user_ocid' must be specified")) 132 } 133 134 tenancyOCID, _ := configProvider.TenancyOCID() 135 if tenancyOCID == "" { 136 errs = packer.MultiErrorAppend( 137 errs, errors.New("'tenancy_ocid' must be specified")) 138 } 139 140 if fingerprint, _ := configProvider.KeyFingerprint(); fingerprint == "" { 141 errs = packer.MultiErrorAppend( 142 errs, errors.New("'fingerprint' must be specified")) 143 } 144 145 if _, err := configProvider.PrivateRSAKey(); err != nil { 146 errs = packer.MultiErrorAppend( 147 errs, errors.New("'key_file' must be specified")) 148 } 149 150 c.ConfigProvider = configProvider 151 152 if c.AvailabilityDomain == "" { 153 errs = packer.MultiErrorAppend( 154 errs, errors.New("'availability_domain' must be specified")) 155 } 156 157 if c.CompartmentID == "" && tenancyOCID != "" { 158 c.CompartmentID = tenancyOCID 159 } 160 161 if c.Shape == "" { 162 errs = packer.MultiErrorAppend( 163 errs, errors.New("'shape' must be specified")) 164 } 165 166 if c.SubnetID == "" { 167 errs = packer.MultiErrorAppend( 168 errs, errors.New("'subnet_ocid' must be specified")) 169 } 170 171 if c.BaseImageID == "" { 172 errs = packer.MultiErrorAppend( 173 errs, errors.New("'base_image_ocid' must be specified")) 174 } 175 176 if c.ImageName == "" { 177 name, err := interpolate.Render("packer-{{timestamp}}", nil) 178 if err != nil { 179 errs = packer.MultiErrorAppend(errs, 180 fmt.Errorf("unable to parse image name: %s", err)) 181 } else { 182 c.ImageName = name 183 } 184 } 185 186 // Optional UserData config 187 if c.UserData != "" && c.UserDataFile != "" { 188 errs = packer.MultiErrorAppend(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified.")) 189 } else if c.UserDataFile != "" { 190 if _, err := os.Stat(c.UserDataFile); err != nil { 191 errs = packer.MultiErrorAppend(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile)) 192 } 193 } 194 // read UserDataFile into string. 195 if c.UserDataFile != "" { 196 fiData, err := ioutil.ReadFile(c.UserDataFile) 197 if err != nil { 198 errs = packer.MultiErrorAppend(errs, fmt.Errorf("Problem reading user_data_file: %s", err)) 199 } 200 c.UserData = string(fiData) 201 } 202 // Test if UserData is encoded already, and if not, encode it 203 if c.UserData != "" { 204 if _, err := base64.StdEncoding.DecodeString(c.UserData); err != nil { 205 log.Printf("[DEBUG] base64 encoding user data...") 206 c.UserData = base64.StdEncoding.EncodeToString([]byte(c.UserData)) 207 } 208 } 209 210 if errs != nil && len(errs.Errors) > 0 { 211 return nil, errs 212 } 213 214 return c, nil 215 } 216 217 // getDefaultOCISettingsPath uses mitchellh/go-homedir to compute the default 218 // config file location ($HOME/.oci/config). 219 func getDefaultOCISettingsPath() (string, error) { 220 home, err := homedir.Dir() 221 if err != nil { 222 return "", err 223 } 224 225 path := filepath.Join(home, ".oci", "config") 226 if _, err := os.Stat(path); err != nil { 227 return "", err 228 } 229 230 return path, nil 231 }