diff options
Diffstat (limited to 'vendor/github.com/mitchellh/packer/builder/oracle/oci/client/config.go')
-rw-r--r-- | vendor/github.com/mitchellh/packer/builder/oracle/oci/client/config.go | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/vendor/github.com/mitchellh/packer/builder/oracle/oci/client/config.go b/vendor/github.com/mitchellh/packer/builder/oracle/oci/client/config.go new file mode 100644 index 00000000..580cd9f7 --- /dev/null +++ b/vendor/github.com/mitchellh/packer/builder/oracle/oci/client/config.go @@ -0,0 +1,240 @@ +package oci + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io/ioutil" + "os" + + "github.com/go-ini/ini" + "github.com/mitchellh/go-homedir" +) + +// Config API authentication and target configuration +type Config struct { + // User OCID e.g. ocid1.user.oc1..aaaaaaaadcshyehbkvxl7arse3lv7z5oknexjgfhnhwidtugsxhlm4247 + User string `ini:"user"` + + // User's Tenancy OCID e.g. ocid1.tenancy.oc1..aaaaaaaagtgvshv6opxzjyzkupkt64ymd32n6kbomadanpcg43d + Tenancy string `ini:"tenancy"` + + // Bare metal region identifier (e.g. us-phoenix-1) + Region string `ini:"region"` + + // Hex key fingerprint (e.g. b5:a0:62:57:28:0d:fd:c9:59:16:eb:d4:51:9f:70:e4) + Fingerprint string `ini:"fingerprint"` + + // Path to OCI config file (e.g. ~/.oci/config) + KeyFile string `ini:"key_file"` + + // Passphrase used for the key, if it is encrypted. + PassPhrase string `ini:"pass_phrase"` + + // Private key (loaded via LoadPrivateKey or ParsePrivateKey) + Key *rsa.PrivateKey + + // Used to override base API URL. + baseURL string +} + +// getBaseURL returns either the specified base URL or builds the appropriate +// URL based on service, region, and API version. +func (c *Config) getBaseURL(service string) string { + if c.baseURL != "" { + return c.baseURL + } + return fmt.Sprintf(baseURLPattern, service, c.Region, apiVersion) +} + +// LoadConfigsFromFile loads all oracle oci configurations from a file +// (generally ~/.oci/config). +func LoadConfigsFromFile(path string) (map[string]*Config, error) { + if _, err := os.Stat(path); err != nil { + return nil, fmt.Errorf("Oracle OCI config file is missing: %s", path) + } + + cfgFile, err := ini.Load(path) + if err != nil { + err := fmt.Errorf("Failed to parse config file %s: %s", path, err.Error()) + return nil, err + } + + configs := make(map[string]*Config) + + // Load DEFAULT section to populate defaults for all other configs + config, err := loadConfigSection(cfgFile, "DEFAULT", nil) + if err != nil { + return nil, err + } + configs["DEFAULT"] = config + + // Load other sections. + for _, sectionName := range cfgFile.SectionStrings() { + if sectionName == "DEFAULT" { + continue + } + + // Map to Config struct with defaults from DEFAULT section. + config, err := loadConfigSection(cfgFile, sectionName, configs["DEFAULT"]) + if err != nil { + return nil, err + } + configs[sectionName] = config + } + + return configs, nil +} + +// Loads an individual Config object from a ini.Section in the Oracle OCI config +// file. +func loadConfigSection(f *ini.File, sectionName string, config *Config) (*Config, error) { + if config == nil { + config = &Config{} + } + + section, err := f.GetSection(sectionName) + if err != nil { + return nil, fmt.Errorf("Config file does not contain a %s section", sectionName) + } + + if err := section.MapTo(config); err != nil { + return nil, err + } + + config.Key, err = LoadPrivateKey(config) + if err != nil { + return nil, err + } + + return config, err +} + +// LoadPrivateKey loads private key from disk and parses it. +func LoadPrivateKey(config *Config) (*rsa.PrivateKey, error) { + // Expand '~' to $HOME + path, err := homedir.Expand(config.KeyFile) + if err != nil { + return nil, err + } + + // Read and parse API signing key + keyContent, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + key, err := ParsePrivateKey(keyContent, []byte(config.PassPhrase)) + + return key, err +} + +// ParsePrivateKey parses a PEM encoded array of bytes into an rsa.PrivateKey. +// Attempts to decrypt the PEM encoded array of bytes with the given password +// if the PEM encoded byte array is encrypted. +func ParsePrivateKey(content, password []byte) (*rsa.PrivateKey, error) { + keyBlock, _ := pem.Decode(content) + + if keyBlock == nil { + return nil, errors.New("could not decode PEM private key") + } + + var der []byte + var err error + if x509.IsEncryptedPEMBlock(keyBlock) { + if len(password) < 1 { + return nil, errors.New("encrypted private key but no pass phrase provided") + } + der, err = x509.DecryptPEMBlock(keyBlock, password) + if err != nil { + return nil, err + } + } else { + der = keyBlock.Bytes + } + + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + + key, err := x509.ParsePKCS8PrivateKey(der) + if err == nil { + switch key := key.(type) { + case *rsa.PrivateKey: + return key, nil + default: + return nil, errors.New("Private key is not an RSA private key") + } + } + return nil, fmt.Errorf("Failed to parse private key :%s", err) +} + +// BaseTestConfig creates the base (DEFAULT) config including a temporary key +// file. +// NOTE: Caller is responsible for removing temporary key file. +func BaseTestConfig() (*ini.File, *os.File, error) { + keyFile, err := generateRSAKeyFile() + if err != nil { + return nil, keyFile, err + } + // Build ini + cfg := ini.Empty() + section, _ := cfg.NewSection("DEFAULT") + section.NewKey("region", "us-phoenix-1") + section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + section.NewKey("fingerprint", "3c:b6:44:d7:49:1a:ac:bf:de:7d:76:22:a7:f5:df:55") + section.NewKey("key_file", keyFile.Name()) + + return cfg, keyFile, nil +} + +// WriteTestConfig writes a ini.File to a temporary file for use in unit tests. +// NOTE: Caller is responsible for removing temporary file. +func WriteTestConfig(cfg *ini.File) (*os.File, error) { + confFile, err := ioutil.TempFile("", "config_file") + if err != nil { + return nil, err + } + + _, err = cfg.WriteTo(confFile) + if err != nil { + os.Remove(confFile.Name()) + return nil, err + } + + return confFile, nil +} + +// generateRSAKeyFile generates an RSA key file for use in unit tests. +// NOTE: The caller is responsible for deleting the temporary file. +func generateRSAKeyFile() (*os.File, error) { + // Create temporary file for the key + f, err := ioutil.TempFile("", "key") + if err != nil { + return nil, err + } + + // Generate key + priv, err := rsa.GenerateKey(rand.Reader, 2014) + if err != nil { + return nil, err + } + + // ASN.1 DER encoded form + privDer := x509.MarshalPKCS1PrivateKey(priv) + privBlk := pem.Block{ + Type: "RSA PRIVATE KEY", + Headers: nil, + Bytes: privDer, + } + + // Write the key out + if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil { + return nil, err + } + + return f, nil +} |