diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index c6ca467e8..8418cd540 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -57,13 +57,13 @@ type Config struct { // Image BaseImageID string `mapstructure:"base_image_ocid"` - Shape string `mapstructure:"shape"` ImageName string `mapstructure:"image_name"` // Instance InstanceName string `mapstructure:"instance_name"` InstanceTags map[string]string `mapstructure:"instance_tags"` InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"` + Shape string `mapstructure:"shape"` // Metadata optionally contains custom metadata key/value pairs provided in the // configuration. While this can be used to set metadata["user_data"] the explicit @@ -77,7 +77,8 @@ type Config struct { UserDataFile string `mapstructure:"user_data_file"` // Networking - SubnetID string `mapstructure:"subnet_ocid"` + SubnetID string `mapstructure:"subnet_ocid"` + CreateVnicDetails map[string]interface{} `mapstructure:"create_vnic_details"` // Tagging Tags map[string]string `mapstructure:"tags"` diff --git a/builder/oracle/oci/config.hcl2spec.go b/builder/oracle/oci/config.hcl2spec.go index 7f5fe7d2a..b4853c51a 100644 --- a/builder/oracle/oci/config.hcl2spec.go +++ b/builder/oracle/oci/config.hcl2spec.go @@ -76,15 +76,16 @@ type FlatConfig struct { AvailabilityDomain *string `mapstructure:"availability_domain" cty:"availability_domain" hcl:"availability_domain"` CompartmentID *string `mapstructure:"compartment_ocid" cty:"compartment_ocid" hcl:"compartment_ocid"` BaseImageID *string `mapstructure:"base_image_ocid" cty:"base_image_ocid" hcl:"base_image_ocid"` - Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"` ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"` InstanceName *string `mapstructure:"instance_name" cty:"instance_name" hcl:"instance_name"` InstanceTags map[string]string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"` InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags" cty:"instance_defined_tags" hcl:"instance_defined_tags"` + Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"` Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"` UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"` UserDataFile *string `mapstructure:"user_data_file" cty:"user_data_file" hcl:"user_data_file"` SubnetID *string `mapstructure:"subnet_ocid" cty:"subnet_ocid" hcl:"subnet_ocid"` + CreateVnicDetails map[string]interface{} `mapstructure:"create_vnic_details" cty:"create_vnic_details" hcl:"create_vnic_details"` Tags map[string]string `mapstructure:"tags" cty:"tags" hcl:"tags"` DefinedTags map[string]map[string]interface{} `mapstructure:"defined_tags" cty:"defined_tags" hcl:"defined_tags"` } @@ -168,15 +169,16 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "availability_domain": &hcldec.AttrSpec{Name: "availability_domain", Type: cty.String, Required: false}, "compartment_ocid": &hcldec.AttrSpec{Name: "compartment_ocid", Type: cty.String, Required: false}, "base_image_ocid": &hcldec.AttrSpec{Name: "base_image_ocid", Type: cty.String, Required: false}, - "shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, "instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.Map(cty.String), Required: false}, "instance_defined_tags": &hcldec.AttrSpec{Name: "instance_defined_tags", Type: cty.Map(cty.String), Required: false}, + "shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false}, "metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, "subnet_ocid": &hcldec.AttrSpec{Name: "subnet_ocid", Type: cty.String, Required: false}, + "create_vnic_details": &hcldec.AttrSpec{Name: "create_vnic_details", Type: cty.Map(cty.String), Required: false}, "tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false}, "defined_tags": &hcldec.AttrSpec{Name: "defined_tags", Type: cty.Map(cty.String), Required: false}, } diff --git a/builder/oracle/oci/driver_oci.go b/builder/oracle/oci/driver_oci.go index 4605f3bca..d0e6b96a8 100644 --- a/builder/oracle/oci/driver_oci.go +++ b/builder/oracle/oci/driver_oci.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "time" core "github.com/oracle/oci-go-sdk/core" @@ -67,6 +68,15 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin instanceDetails.DisplayName = &d.cfg.InstanceName } + // Pass VNIC details, if specified, to the instance + if len(d.cfg.CreateVnicDetails) > 0 { + CreateVnicDetails, err := mapToCreateVnicDetails(d.cfg.CreateVnicDetails) + if err != nil { + return "", err + } + instanceDetails.CreateVnicDetails = &CreateVnicDetails + } + instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{LaunchInstanceDetails: instanceDetails}) if err != nil { @@ -217,3 +227,69 @@ func stringSliceContains(slice []string, value string) bool { } return false } + +// interfaceToBool converts a variable of type interface to type bool +func interfaceToBool(b interface{}) (bool, error) { + var boolVal bool + var err error + + switch t := b.(type) { + case bool: + boolVal = t + case string: + boolVal, err = strconv.ParseBool(t) + default: + boolVal, err = false, fmt.Errorf("failed to convert %v to boolean type", b) + } + + return boolVal, err +} + +// mapToCreateVnicDetails creates variable of type core.CreateVnicDetails from map +func mapToCreateVnicDetails(m map[string]interface{}) (core.CreateVnicDetails, error) { + result := core.CreateVnicDetails{} + + if val, ok := m["assign_public_ip"]; ok { + boolVal, err := interfaceToBool(val) + if err != nil { + return result, fmt.Errorf("assign_public_ip is incorrect type: %v", err) + } + result.AssignPublicIp = &boolVal + } + if val, ok := m["display_name"]; ok { + tmp := val.(string) + result.DisplayName = &tmp + } + if val, ok := m["hostname_label"]; ok { + tmp := val.(string) + result.HostnameLabel = &tmp + } + if val, ok := m["nsg_ids"]; ok { + tmp, tmpok := val.([]interface{}) + if !tmpok { + return result, errors.New("nsg_ids not in correct list format") + } + valStr := make([]string, len(tmp)) //convert []interface{} to []string + for i, v := range tmp { + valStr[i] = fmt.Sprint(v) + } + result.NsgIds = valStr + } + if val, ok := m["private_ip"]; ok { + tmp := val.(string) + result.PrivateIp = &tmp + } + if val, ok := m["skip_source_dest_check"]; ok { + boolVal, err := interfaceToBool(val) + if err != nil { + return result, fmt.Errorf("skip_source_dest_check is incorrect type: %v", err) + } + result.SkipSourceDestCheck = &boolVal + } + if val, ok := m["subnet_id"]; ok { + tmp := val.(string) + result.SubnetId = &tmp + } + + return result, nil +}