From 6f1e76d809df494a1183e524451c2f0cc894f323 Mon Sep 17 00:00:00 2001 From: Krauss Vesset Date: Wed, 19 Nov 2025 05:29:30 +0000 Subject: [PATCH 1/2] feat(instance) public cloud instances with multiple network interfaces --- env.sh | 12 ++ ovh/resource_cloud_project_instance.go | 101 ++++++++++++++-- ovh/types_cloud_project_instance.go | 156 +++++++++++++------------ 3 files changed, 190 insertions(+), 79 deletions(-) create mode 100755 env.sh diff --git a/env.sh b/env.sh new file mode 100755 index 000000000..1de1b52a4 --- /dev/null +++ b/env.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +export OVH_ENDPOINT="ovh-eu" +export OVH_APPLICATION_KEY="000693a9655db1b5" +export OVH_APPLICATION_SECRET="dda3dcae9b4fbd41461c07d100da6586" +export OVH_CONSUMER_KEY="055d118f9c3ca88b4fba9ae1b984fdf8" + +export OVH_DOMAIN_TEST="syk-edu.com" + +export TF_ACC=1 + +echo "[OK] OVH environment variables exported for acceptance tests." \ No newline at end of file diff --git a/ovh/resource_cloud_project_instance.go b/ovh/resource_cloud_project_instance.go index 5f28b566d..621ed7fec 100644 --- a/ovh/resource_cloud_project_instance.go +++ b/ovh/resource_cloud_project_instance.go @@ -184,11 +184,10 @@ func resourceCloudProjectInstance() *schema.Resource { ForceNew: true, }, "network": { - Type: schema.TypeSet, + Type: schema.TypeList, Required: true, ForceNew: true, - MaxItems: 1, - Description: "Network information", + Description: "Network information(multiple NICs)", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "public": { @@ -197,10 +196,9 @@ 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{ @@ -425,6 +423,7 @@ func resourceCloudProjectInstance() *schema.Resource { }, }, } + } func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -439,9 +438,24 @@ func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceD url.PathEscape(region), ) + payload := map[string]interface{}{ + "auto_backup": params.AutoBackup, + "availabilityZone": params.AvailabilityZone, + "billingPeriod": params.BillingPeriod, + "bootFrom": params.BootFrom, + "bulk": params.Bulk, + "flavor": params.Flavor, + "group": params.Group, + "name": params.Name, + "sshKey": params.SshKey, + "sshKeyCreate": params.SshKeyCreate, + "userData": params.UserData, + "network": networksToPayload(params.Networks), // multi NIC + } + r := &CloudProjectOperationResponse{} - if err := config.OVHClient.Post(endpoint, params, r); err != nil { - return diag.Errorf("calling %s with params %v:\n\t %q", endpoint, params, err) + if err := config.OVHClient.Post(endpoint, payload, r); err != nil { + return diag.Errorf("calling %s with params %v:\n\t %q", endpoint, payload, err) } instanceID, err := waitForCloudProjectOperation(ctx, config.OVHClient, serviceName, r.Id, "instance#create", d.Timeout(schema.TimeoutCreate)) @@ -528,3 +542,76 @@ func resourceCloudProjectInstanceDelete(ctx context.Context, d *schema.ResourceD log.Printf("[DEBUG] Deleted Public Cloud %s Instance %s", serviceName, id) return nil } + +func networksToPayload(networks []*Network) []interface{} { + payload := make([]interface{}, 0, len(networks)) + for _, net := range networks { + netMap := map[string]interface{}{ + "public": net.Public, + } + + if len(net.Private) > 0 { + privateList := make([]interface{}, 0, len(net.Private)) + for _, p := range net.Private { + pMap := map[string]interface{}{} + + if p.FloatingIP != nil { + pMap["floating_ip"] = []map[string]interface{}{ + {"id": p.FloatingIP.ID}, + } + } + + if p.FloatingIPCreate != nil { + pMap["floating_ip_create"] = []map[string]interface{}{ + {"description": p.FloatingIPCreate.Description}, + } + } + + if p.Gateway != nil { + pMap["gateway"] = []map[string]interface{}{ + {"id": p.Gateway.ID}, + } + } + + if p.GatewayCreate != nil { + pMap["gateway_create"] = []map[string]interface{}{ + {"model": p.GatewayCreate.Model, "name": p.GatewayCreate.Name}, + } + } + + if p.IP != "" { + pMap["ip"] = p.IP + } + + if p.Network != nil { + pMap["network"] = []map[string]interface{}{ + {"id": p.Network.ID, "subnet_id": p.Network.SubnetID}, + } + } + + if p.NetworkCreate != nil { + subnetList := []map[string]interface{}{ + { + "cidr": p.NetworkCreate.Subnet.CIDR, + "enable_dhcp": p.NetworkCreate.Subnet.EnableDHCP, + "ip_version": p.NetworkCreate.Subnet.IPVersion, + }, + } + pMap["network_create"] = []map[string]interface{}{ + { + "name": p.NetworkCreate.Name, + "vlan_id": p.NetworkCreate.VlanId, + "subnet": subnetList, + }, + } + } + + privateList = append(privateList, pMap) + } + netMap["private"] = privateList + } + + payload = append(payload, netMap) + } + return payload +} diff --git a/ovh/types_cloud_project_instance.go b/ovh/types_cloud_project_instance.go index 8c405f822..3f4905fd1 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 { @@ -100,7 +100,7 @@ type CloudProjectInstanceCreateOpts struct { SshKey *SshKey `json:"sshKey,omitempty"` SshKeyCreate *SshKeyCreate `json:"sshKeyCreate,omitempty"` UserData *string `json:"userData,omitempty"` - Network *Network `json:"network,omitempty"` + Networks []*Network `json:"network,omitempty"` } type Address struct { @@ -279,102 +279,99 @@ 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{} + + // Public NIC + 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) + // Private NICs + privateRaw, ok := mapping["private"] + if !ok || privateRaw == nil || privateRaw.(*schema.Set).Len() == 0 { + return networkOutput + } - privateNet, ok := mapping["private"] - if !ok || privateNet == nil || privateNet.(*schema.Set).Len() == 0 { - continue - } + privateSet := privateRaw.(*schema.Set).List() + networkOutput.Private = make([]*PrivateNetwork, 0, len(privateSet)) - for _, priv := range privateNet.(*schema.Set).List() { - networkOutput.Private = &PrivateNetwork{} - private := priv.(map[string]interface{}) + for _, priv := range privateSet { + private := priv.(map[string]interface{}) + pn := &PrivateNetwork{} - 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), - } - } + if floatingIP, ok := private["floating_ip"]; ok && floatingIP != nil { + for _, float := range floatingIP.(*schema.Set).List() { + params := float.(map[string]interface{}) + pn.FloatingIP = &PrivateNetworkFloatingIP{ID: params["id"].(string)} } + } - 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 floatingIPCreate, ok := private["floating_ip_create"]; ok && floatingIPCreate != nil { + for _, float := range floatingIPCreate.(*schema.Set).List() { + params := float.(map[string]interface{}) + pn.FloatingIPCreate = &PrivateNetworkFloatingIPCreate{Description: params["description"].(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 gateway, ok := private["gateway"]; ok && gateway != nil { + for _, g := range gateway.(*schema.Set).List() { + params := g.(map[string]interface{}) + pn.Gateway = &PrivateNetworkGateway{ID: params["id"].(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 gatewayCreate, ok := private["gateway_create"]; ok && gatewayCreate != nil { + for _, gc := range gatewayCreate.(*schema.Set).List() { + params := gc.(map[string]interface{}) + pn.GatewayCreate = &PrivateNetworkGatewayCreate{ + Model: params["model"].(string), + Name: params["name"].(string), } } + } - networkOutput.Private.IP = private["ip"].(string) + if ip, ok := private["ip"]; ok && ip != nil { + pn.IP = 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 networkMap, ok := private["network"]; ok && networkMap != nil { + for _, net := range networkMap.(*schema.Set).List() { + params := net.(map[string]interface{}) + pn.Network = &PrivateNetworkInformation{ + ID: params["id"].(string), + SubnetID: params["subnet_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 networkCreate, ok := private["network_create"]; ok && networkCreate != nil { + for _, net := range networkCreate.(*schema.Set).List() { + params := net.(map[string]interface{}) + pn.NetworkCreate = &PrivateNetworkInformationCreate{Name: params["name"].(string)} - if vlanID, ok := params["vlan_id"]; ok { - intVlanID := vlanID.(int) - networkOutput.Private.NetworkCreate.VlanId = &intVlanID - } + if vlanID, ok := params["vlan_id"]; ok && vlanID != nil { + intVlanID := vlanID.(int) + pn.NetworkCreate.VlanId = &intVlanID + } - 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 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 = append(networkOutput.Private, pn) } - return &networkOutput + return networkOutput } func (cpir *CloudProjectInstanceCreateOpts) FromResource(d *schema.ResourceData) { @@ -384,7 +381,7 @@ 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")) + cpir.Networks = expandNetworks(d.Get("network")) cpir.BillingPeriod = d.Get("billing_period").(string) cpir.Name = d.Get("name").(string) cpir.AvailabilityZone = helpers.GetNilStringPointerFromData(d, "availability_zone") @@ -413,3 +410,18 @@ func waitForCloudProjectInstance(ctx context.Context, c *ovhwrap.Client, service return err } + +func expandNetworks(i interface{}) []*Network { + if i == nil { + return nil + } + + networksSet := i.(*schema.Set).List() + networks := make([]*Network, 0, len(networksSet)) + + for _, n := range networksSet { + networks = append(networks, GetNetwork(n.(map[string]interface{}))) + } + + return networks +} From 5b90aea3ee19b9dbb5a1c2a710cb8cbedc246b32 Mon Sep 17 00:00:00 2001 From: thekrauss Date: Wed, 19 Nov 2025 08:05:44 +0100 Subject: [PATCH 2/2] feat(instance) public cloud instances with multiple interfaces Signed-off-by: Krauss Vesset --- env.sh | 11 +- .../cloud_project_instance/example_1.tf | 58 ++++++++ ovh/resource_cloud_project_instance.go | 97 +------------- ovh/resource_cloud_project_instance_test.go | 115 ++++++++++++++++ ovh/types_cloud_project_instance.go | 124 +++++++++--------- 5 files changed, 243 insertions(+), 162 deletions(-) diff --git a/env.sh b/env.sh index 1de1b52a4..12794be4d 100755 --- a/env.sh +++ b/env.sh @@ -1,12 +1,3 @@ #!/usr/bin/env bash -export OVH_ENDPOINT="ovh-eu" -export OVH_APPLICATION_KEY="000693a9655db1b5" -export OVH_APPLICATION_SECRET="dda3dcae9b4fbd41461c07d100da6586" -export OVH_CONSUMER_KEY="055d118f9c3ca88b4fba9ae1b984fdf8" - -export OVH_DOMAIN_TEST="syk-edu.com" - -export TF_ACC=1 - -echo "[OK] OVH environment variables exported for acceptance tests." \ No newline at end of file +!!! \ 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 621ed7fec..3282e17c8 100644 --- a/ovh/resource_cloud_project_instance.go +++ b/ovh/resource_cloud_project_instance.go @@ -187,7 +187,8 @@ func resourceCloudProjectInstance() *schema.Resource { Type: schema.TypeList, Required: true, ForceNew: true, - Description: "Network information(multiple NICs)", + MaxItems: 1, + Description: "Network information", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "public": { @@ -205,7 +206,6 @@ func resourceCloudProjectInstance() *schema.Resource { "floating_ip": { Type: schema.TypeSet, Optional: true, - MaxItems: 1, Description: "Existing floating IP", ForceNew: true, Elem: &schema.Resource{ @@ -430,6 +430,7 @@ func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceD config := meta.(*Config) serviceName := d.Get("service_name").(string) region := d.Get("region").(string) + params := new(CloudProjectInstanceCreateOpts) params.FromResource(d) @@ -438,24 +439,9 @@ func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceD url.PathEscape(region), ) - payload := map[string]interface{}{ - "auto_backup": params.AutoBackup, - "availabilityZone": params.AvailabilityZone, - "billingPeriod": params.BillingPeriod, - "bootFrom": params.BootFrom, - "bulk": params.Bulk, - "flavor": params.Flavor, - "group": params.Group, - "name": params.Name, - "sshKey": params.SshKey, - "sshKeyCreate": params.SshKeyCreate, - "userData": params.UserData, - "network": networksToPayload(params.Networks), // multi NIC - } - r := &CloudProjectOperationResponse{} - if err := config.OVHClient.Post(endpoint, payload, r); err != nil { - return diag.Errorf("calling %s with params %v:\n\t %q", endpoint, payload, err) + if err := config.OVHClient.Post(endpoint, params, r); err != nil { + return diag.Errorf("calling %s with params %v:\n\t %q", endpoint, params, err) } instanceID, err := waitForCloudProjectOperation(ctx, config.OVHClient, serviceName, r.Id, "instance#create", d.Timeout(schema.TimeoutCreate)) @@ -542,76 +528,3 @@ func resourceCloudProjectInstanceDelete(ctx context.Context, d *schema.ResourceD log.Printf("[DEBUG] Deleted Public Cloud %s Instance %s", serviceName, id) return nil } - -func networksToPayload(networks []*Network) []interface{} { - payload := make([]interface{}, 0, len(networks)) - for _, net := range networks { - netMap := map[string]interface{}{ - "public": net.Public, - } - - if len(net.Private) > 0 { - privateList := make([]interface{}, 0, len(net.Private)) - for _, p := range net.Private { - pMap := map[string]interface{}{} - - if p.FloatingIP != nil { - pMap["floating_ip"] = []map[string]interface{}{ - {"id": p.FloatingIP.ID}, - } - } - - if p.FloatingIPCreate != nil { - pMap["floating_ip_create"] = []map[string]interface{}{ - {"description": p.FloatingIPCreate.Description}, - } - } - - if p.Gateway != nil { - pMap["gateway"] = []map[string]interface{}{ - {"id": p.Gateway.ID}, - } - } - - if p.GatewayCreate != nil { - pMap["gateway_create"] = []map[string]interface{}{ - {"model": p.GatewayCreate.Model, "name": p.GatewayCreate.Name}, - } - } - - if p.IP != "" { - pMap["ip"] = p.IP - } - - if p.Network != nil { - pMap["network"] = []map[string]interface{}{ - {"id": p.Network.ID, "subnet_id": p.Network.SubnetID}, - } - } - - if p.NetworkCreate != nil { - subnetList := []map[string]interface{}{ - { - "cidr": p.NetworkCreate.Subnet.CIDR, - "enable_dhcp": p.NetworkCreate.Subnet.EnableDHCP, - "ip_version": p.NetworkCreate.Subnet.IPVersion, - }, - } - pMap["network_create"] = []map[string]interface{}{ - { - "name": p.NetworkCreate.Name, - "vlan_id": p.NetworkCreate.VlanId, - "subnet": subnetList, - }, - } - } - - privateList = append(privateList, pMap) - } - netMap["private"] = privateList - } - - payload = append(payload, netMap) - } - return payload -} 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 3f4905fd1..9cdbe4378 100644 --- a/ovh/types_cloud_project_instance.go +++ b/ovh/types_cloud_project_instance.go @@ -100,7 +100,7 @@ type CloudProjectInstanceCreateOpts struct { SshKey *SshKey `json:"sshKey,omitempty"` SshKeyCreate *SshKeyCreate `json:"sshKeyCreate,omitempty"` UserData *string `json:"userData,omitempty"` - Networks []*Network `json:"network,omitempty"` + Network *Network `json:"network,omitempty"` } type Address struct { @@ -282,61 +282,34 @@ func GetSshKeyCreate(i interface{}) *SshKeyCreate { func GetNetwork(mapping map[string]interface{}) *Network { networkOutput := &Network{} - // Public NIC if v, ok := mapping["public"]; ok { networkOutput.Public = v.(bool) } - // Private NICs privateRaw, ok := mapping["private"] - if !ok || privateRaw == nil || privateRaw.(*schema.Set).Len() == 0 { + if !ok || privateRaw == nil { return networkOutput } privateSet := privateRaw.(*schema.Set).List() + if len(privateSet) == 0 { + return networkOutput + } + networkOutput.Private = make([]*PrivateNetwork, 0, len(privateSet)) for _, priv := range privateSet { - private := priv.(map[string]interface{}) + privateMap := priv.(map[string]interface{}) pn := &PrivateNetwork{} - if floatingIP, ok := private["floating_ip"]; ok && floatingIP != nil { - for _, float := range floatingIP.(*schema.Set).List() { - params := float.(map[string]interface{}) - pn.FloatingIP = &PrivateNetworkFloatingIP{ID: params["id"].(string)} - } - } - - if floatingIPCreate, ok := private["floating_ip_create"]; ok && floatingIPCreate != nil { - for _, float := range floatingIPCreate.(*schema.Set).List() { - params := float.(map[string]interface{}) - pn.FloatingIPCreate = &PrivateNetworkFloatingIPCreate{Description: params["description"].(string)} - } - } - - if gateway, ok := private["gateway"]; ok && gateway != nil { - for _, g := range gateway.(*schema.Set).List() { - params := g.(map[string]interface{}) - pn.Gateway = &PrivateNetworkGateway{ID: params["id"].(string)} - } - } - - if gatewayCreate, ok := private["gateway_create"]; ok && gatewayCreate != nil { - for _, gc := range gatewayCreate.(*schema.Set).List() { - params := gc.(map[string]interface{}) - pn.GatewayCreate = &PrivateNetworkGatewayCreate{ - Model: params["model"].(string), - Name: params["name"].(string), - } + if v, ok := privateMap["ip"]; ok && v != nil { + if ipVal, ok := v.(string); ok && ipVal != "" { + pn.IP = ipVal } } - if ip, ok := private["ip"]; ok && ip != nil { - pn.IP = ip.(string) - } - - if networkMap, ok := private["network"]; ok && networkMap != nil { - for _, net := range networkMap.(*schema.Set).List() { + 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), @@ -345,14 +318,16 @@ func GetNetwork(mapping map[string]interface{}) *Network { } } - if networkCreate, ok := private["network_create"]; ok && networkCreate != nil { - for _, net := range networkCreate.(*schema.Set).List() { + 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)} + pn.NetworkCreate = &PrivateNetworkInformationCreate{ + Name: params["name"].(string), + } if vlanID, ok := params["vlan_id"]; ok && vlanID != nil { - intVlanID := vlanID.(int) - pn.NetworkCreate.VlanId = &intVlanID + val := vlanID.(int) + pn.NetworkCreate.VlanId = &val } if subnetVal, ok := params["subnet"]; ok && subnetVal != nil { @@ -368,6 +343,43 @@ func GetNetwork(mapping map[string]interface{}) *Network { } } + 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 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 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), + } + } + } + + 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) } @@ -381,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.Networks = expandNetworks(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") @@ -410,18 +429,3 @@ func waitForCloudProjectInstance(ctx context.Context, c *ovhwrap.Client, service return err } - -func expandNetworks(i interface{}) []*Network { - if i == nil { - return nil - } - - networksSet := i.(*schema.Set).List() - networks := make([]*Network, 0, len(networksSet)) - - for _, n := range networksSet { - networks = append(networks, GetNetwork(n.(map[string]interface{}))) - } - - return networks -}