diff --git a/env.sh b/env.sh new file mode 100755 index 000000000..12794be4d --- /dev/null +++ b/env.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +!!! \ No newline at end of file diff --git a/examples/resources/cloud_project_instance/example_1.tf b/examples/resources/cloud_project_instance/example_1.tf index d38f6b216..e4939bbf3 100644 --- a/examples/resources/cloud_project_instance/example_1.tf +++ b/examples/resources/cloud_project_instance/example_1.tf @@ -16,3 +16,61 @@ resource "ovh_cloud_project_instance" "instance" { public = true } } + + +## with Multiple Network Interfaces + +resource "ovh_cloud_project_network_private" "net_front" { + service_name = "XXX" + name = "public-front" + regions = ["GRA11"] +} + +resource "ovh_cloud_project_subnet" "subnet_front" { + service_name = "XXX" + network_id = ovh_cloud_project_network_private.net_front.id + region = "GRA11" + network = "10.0.1.0/24" + dhcp = true +} + +resource "ovh_cloud_project_network_private" "net_back" { + service_name = "XXX" + name = "private-back" + regions = ["GRA11"] +} + +resource "ovh_cloud_project_subnet" "subnet_back" { + service_name = "XXX" + network_id = private.net_back.id + region = "GRA11" + network = "10.0.2.0/24" + dhcp = true +} + +resource "ovh_cloud_project_instance" "multinic_instance" { + service_name = "XXX" + region = "GRA11" + name = "multi-nic-instance" + flavor_id = "UUID" + image_id = "UUID" + + network { + public = true + + private { + network { + id = private.net_front.id + subnet_id = subnet.subnet_front.id + } + ip = "10.0.1.10" + } + + private { + network { + id = private.net_back.id + subnet_id = subnet.subnet_back.id + } + } + } +} diff --git a/ovh/resource_cloud_project_instance.go b/ovh/resource_cloud_project_instance.go index 5f28b566d..3282e17c8 100644 --- a/ovh/resource_cloud_project_instance.go +++ b/ovh/resource_cloud_project_instance.go @@ -184,7 +184,7 @@ func resourceCloudProjectInstance() *schema.Resource { ForceNew: true, }, "network": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, ForceNew: true, MaxItems: 1, @@ -197,17 +197,15 @@ func resourceCloudProjectInstance() *schema.Resource { Optional: true, }, "private": { - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, ForceNew: true, - MaxItems: 1, Description: "Private network information", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "floating_ip": { Type: schema.TypeSet, Optional: true, - MaxItems: 1, Description: "Existing floating IP", ForceNew: true, Elem: &schema.Resource{ @@ -425,12 +423,14 @@ func resourceCloudProjectInstance() *schema.Resource { }, }, } + } func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { config := meta.(*Config) serviceName := d.Get("service_name").(string) region := d.Get("region").(string) + params := new(CloudProjectInstanceCreateOpts) params.FromResource(d) diff --git a/ovh/resource_cloud_project_instance_test.go b/ovh/resource_cloud_project_instance_test.go index 1c1f9750a..3c2c9ad89 100644 --- a/ovh/resource_cloud_project_instance_test.go +++ b/ovh/resource_cloud_project_instance_test.go @@ -380,3 +380,118 @@ func TestAccCloudProjectInstance_privateNetworkAlreadyExists(t *testing.T) { }, }) } + +func TestAccCloudProjectInstance_multiNIC(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + region := os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST") + flavor, image, err := getFlavorAndImage(serviceName, region) + if err != nil { + t.Skipf("failed to retrieve a flavor and an image: %s", err) + } + + rName := acctest.RandomWithPrefix(test_prefix) + netName1 := fmt.Sprintf("%s_net1", rName) + netName2 := fmt.Sprintf("%s_net2", rName) + + vlanID1 := rand.Intn(2000) + 2 + vlanID2 := vlanID1 + 1 + + var testCreateInstanceMultiNIC = fmt.Sprintf(` + resource "private" "net1" { + service_name = "%s" + name = "%s" + regions = ["%s"] + vlan_id = %d + } + + resource "ovh_cloud_project_subnet" "sub1" { + service_name = "%s" + network_id = private.net1.id + region = "%s" + network = "192.168.1.0/24" + dhcp = true + start = "192.168.1.100" + end = "192.168.1.200" + no_gateway = false + } + + resource "private" "net2" { + service_name = "%s" + name = "%s" + regions = ["%s"] + vlan_id = %d + } + + resource "ovh_cloud_project_subnet" "sub2" { + service_name = "%s" + network_id = private.net2.id + region = "%s" + network = "192.168.2.0/24" + dhcp = true + start = "192.168.2.100" + end = "192.168.2.200" + no_gateway = false + } + + resource "ovh_cloud_project_instance" "multi_nic" { + service_name = "%s" + region = "%s" + name = "%s" + billing_period = "hourly" + + flavor { + flavor_id = "%s" + } + boot_from { + image_id = "%s" + } + + network { + # Interface Publique + public = true + + # Interface Privée 1 + private { + network { + id = private.net1.id + subnet_id = ovh_cloud_project_subnet.sub1.id + } + } + + # Interface Privée 2 + private { + network { + id = private.net2.id + subnet_id = ovh_cloud_project_subnet.sub2.id + } + } + } + } + `, + serviceName, netName1, region, vlanID1, + serviceName, region, + serviceName, netName2, region, vlanID2, + serviceName, region, + serviceName, region, rName, flavor, image, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckCloud(t) + testAccCheckCloudProjectExists(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testCreateInstanceMultiNIC, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.multi_nic", "id"), + resource.TestCheckResourceAttr("ovh_cloud_project_instance.multi_nic", "network.0.private.#", "2"), + resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.multi_nic", "addresses.0.ip"), + resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.multi_nic", "addresses.1.ip"), + resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.multi_nic", "addresses.2.ip"), + ), + }, + }, + }) +} diff --git a/ovh/types_cloud_project_instance.go b/ovh/types_cloud_project_instance.go index 8c405f822..9cdbe4378 100644 --- a/ovh/types_cloud_project_instance.go +++ b/ovh/types_cloud_project_instance.go @@ -40,8 +40,8 @@ type SshKeyCreate struct { } type Network struct { - Public bool `json:"public"` - Private *PrivateNetwork `json:"private,omitempty"` + Public bool `json:"public"` + Private []*PrivateNetwork `json:"private,omitempty"` } type PrivateNetwork struct { @@ -279,102 +279,111 @@ func GetSshKeyCreate(i interface{}) *SshKeyCreate { return &sshCreateOutput } -func GetNetwork(i interface{}) *Network { - if i == nil { - return nil +func GetNetwork(mapping map[string]interface{}) *Network { + networkOutput := &Network{} + + if v, ok := mapping["public"]; ok { + networkOutput.Public = v.(bool) } - networkOutput := Network{} - networkSet := i.(*schema.Set).List() - for _, network := range networkSet { - mapping := network.(map[string]interface{}) - networkOutput.Public = mapping["public"].(bool) + privateRaw, ok := mapping["private"] + if !ok || privateRaw == nil { + return networkOutput + } - privateNet, ok := mapping["private"] - if !ok || privateNet == nil || privateNet.(*schema.Set).Len() == 0 { - continue - } + privateSet := privateRaw.(*schema.Set).List() + if len(privateSet) == 0 { + return networkOutput + } - for _, priv := range privateNet.(*schema.Set).List() { - networkOutput.Private = &PrivateNetwork{} - private := priv.(map[string]interface{}) + networkOutput.Private = make([]*PrivateNetwork, 0, len(privateSet)) - if floatingIP, ok := private["floating_ip"]; ok { - for _, float := range floatingIP.(*schema.Set).List() { - params := float.(map[string]interface{}) - networkOutput.Private.FloatingIP = &PrivateNetworkFloatingIP{ - ID: params["id"].(string), - } - } + for _, priv := range privateSet { + privateMap := priv.(map[string]interface{}) + pn := &PrivateNetwork{} + + if v, ok := privateMap["ip"]; ok && v != nil { + if ipVal, ok := v.(string); ok && ipVal != "" { + pn.IP = ipVal } + } - if floatingIPCreate, ok := private["floating_ip_create"]; ok { - for _, float := range floatingIPCreate.(*schema.Set).List() { - params := float.(map[string]interface{}) - networkOutput.Private.FloatingIPCreate = &PrivateNetworkFloatingIPCreate{ - Description: params["description"].(string), - } + if v, ok := privateMap["network"]; ok && v != nil { + for _, net := range v.(*schema.Set).List() { + params := net.(map[string]interface{}) + pn.Network = &PrivateNetworkInformation{ + ID: params["id"].(string), + SubnetID: params["subnet_id"].(string), } } + } - if gateway, ok := private["gateway"]; ok { - for _, gateway := range gateway.(*schema.Set).List() { - params := gateway.(map[string]interface{}) - networkOutput.Private.Gateway = &PrivateNetworkGateway{ - ID: params["id"].(string), - } + if v, ok := privateMap["network_create"]; ok && v != nil { + for _, net := range v.(*schema.Set).List() { + params := net.(map[string]interface{}) + pn.NetworkCreate = &PrivateNetworkInformationCreate{ + Name: params["name"].(string), } - } - if gatewayCreate, ok := private["gateway_create"]; ok { - for _, gateway := range gatewayCreate.(*schema.Set).List() { - params := gateway.(map[string]interface{}) - networkOutput.Private.GatewayCreate = &PrivateNetworkGatewayCreate{ - Model: params["model"].(string), - Name: params["name"].(string), + if vlanID, ok := params["vlan_id"]; ok && vlanID != nil { + val := vlanID.(int) + pn.NetworkCreate.VlanId = &val + } + + if subnetVal, ok := params["subnet"]; ok && subnetVal != nil { + for _, subnet := range subnetVal.(*schema.Set).List() { + sn := subnet.(map[string]interface{}) + pn.NetworkCreate.Subnet = PrivateNetworkInformationSubnetCreate{ + CIDR: sn["cidr"].(string), + EnableDHCP: sn["enable_dhcp"].(bool), + IPVersion: sn["ip_version"].(int), + } } } } + } - networkOutput.Private.IP = private["ip"].(string) - - if network, ok := private["network"]; ok { - for _, net := range network.(*schema.Set).List() { - params := net.(map[string]interface{}) - networkOutput.Private.Network = &PrivateNetworkInformation{ - ID: params["id"].(string), - SubnetID: params["subnet_id"].(string), - } + if v, ok := privateMap["floating_ip"]; ok && v != nil { + for _, float := range v.(*schema.Set).List() { + params := float.(map[string]interface{}) + pn.FloatingIP = &PrivateNetworkFloatingIP{ + ID: params["id"].(string), } } + } - if networkCreate, ok := private["network_create"]; ok { - for _, net := range networkCreate.(*schema.Set).List() { - params := net.(map[string]interface{}) - networkOutput.Private.NetworkCreate = &PrivateNetworkInformationCreate{ - Name: params["name"].(string), - } + if v, ok := privateMap["floating_ip_create"]; ok && v != nil { + for _, float := range v.(*schema.Set).List() { + params := float.(map[string]interface{}) + pn.FloatingIPCreate = &PrivateNetworkFloatingIPCreate{ + Description: params["description"].(string), + } + } + } - if vlanID, ok := params["vlan_id"]; ok { - intVlanID := vlanID.(int) - networkOutput.Private.NetworkCreate.VlanId = &intVlanID - } + if v, ok := privateMap["gateway"]; ok && v != nil { + for _, g := range v.(*schema.Set).List() { + params := g.(map[string]interface{}) + pn.Gateway = &PrivateNetworkGateway{ + ID: params["id"].(string), + } + } + } - for _, subnet := range params["subnet"].(*schema.Set).List() { - subnetParams := subnet.(map[string]interface{}) - networkOutput.Private.NetworkCreate.Subnet = PrivateNetworkInformationSubnetCreate{ - CIDR: subnetParams["cidr"].(string), - EnableDHCP: subnetParams["enable_dhcp"].(bool), - IPVersion: subnetParams["ip_version"].(int), - } - } + if v, ok := privateMap["gateway_create"]; ok && v != nil { + for _, gc := range v.(*schema.Set).List() { + params := gc.(map[string]interface{}) + pn.GatewayCreate = &PrivateNetworkGatewayCreate{ + Model: params["model"].(string), + Name: params["name"].(string), } } } + networkOutput.Private = append(networkOutput.Private, pn) } - return &networkOutput + return networkOutput } func (cpir *CloudProjectInstanceCreateOpts) FromResource(d *schema.ResourceData) { @@ -384,7 +393,14 @@ func (cpir *CloudProjectInstanceCreateOpts) FromResource(d *schema.ResourceData) cpir.Group = GetGroup(d.Get("group")) cpir.SshKey = GetSshKey(d.Get("ssh_key")) cpir.SshKeyCreate = GetSshKeyCreate(d.Get("ssh_key_create")) - cpir.Network = GetNetwork(d.Get("network")) + + if v := d.Get("network"); v != nil { + netSet := v.(*schema.Set).List() + if len(netSet) > 0 { + cpir.Network = GetNetwork(netSet[0].(map[string]interface{})) + } + } + cpir.BillingPeriod = d.Get("billing_period").(string) cpir.Name = d.Get("name").(string) cpir.AvailabilityZone = helpers.GetNilStringPointerFromData(d, "availability_zone")