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  }