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  }