From 44818cf14ba20beb28541e90d9458ca73b39c6a9 Mon Sep 17 00:00:00 2001 From: Stamatis Katsaounis Date: Wed, 17 Apr 2024 17:52:00 +0300 Subject: [PATCH] fix: properly import physical interfaces (#169) --- docs/resources/network_interface_physical.md | 2 +- .../maas_network_interface_physical/import.sh | 2 +- ...esource_maas_network_interface_physical.go | 22 +-- ...ce_maas_network_interface_physical_test.go | 170 ++++++++++++++++++ 4 files changed, 178 insertions(+), 18 deletions(-) create mode 100644 maas/resource_maas_network_interface_physical_test.go diff --git a/docs/resources/network_interface_physical.md b/docs/resources/network_interface_physical.md index 4c7b591f..6302546e 100644 --- a/docs/resources/network_interface_physical.md +++ b/docs/resources/network_interface_physical.md @@ -75,5 +75,5 @@ Import is supported using the following syntax: ```shell # A physical network interface can be imported using the machine identifier (system ID, hostname, or FQDN) and its own identifier (MAC address, name, or ID). e.g. -$ terraform import maas_network_interface_physical.virsh_vm1 vm1:eth0 +$ terraform import maas_network_interface_physical.virsh_vm1 vm1/eth0 ``` diff --git a/examples/resources/maas_network_interface_physical/import.sh b/examples/resources/maas_network_interface_physical/import.sh index 3a33d747..596ccff8 100644 --- a/examples/resources/maas_network_interface_physical/import.sh +++ b/examples/resources/maas_network_interface_physical/import.sh @@ -1,2 +1,2 @@ # A physical network interface can be imported using the machine identifier (system ID, hostname, or FQDN) and its own identifier (MAC address, name, or ID). e.g. -$ terraform import maas_network_interface_physical.virsh_vm1 vm1:eth0 +$ terraform import maas_network_interface_physical.virsh_vm1 vm1/eth0 diff --git a/maas/resource_maas_network_interface_physical.go b/maas/resource_maas_network_interface_physical.go index 889d64ca..4d5b96a0 100644 --- a/maas/resource_maas_network_interface_physical.go +++ b/maas/resource_maas_network_interface_physical.go @@ -21,9 +21,9 @@ func resourceMaasNetworkInterfacePhysical() *schema.Resource { DeleteContext: resourceNetworkInterfacePhysicalDelete, Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - idParts := strings.Split(d.Id(), ":") + idParts := strings.Split(d.Id(), "/") if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { - return nil, fmt.Errorf("unexpected format of ID (%q), expected MACHINE:NETWORK_INTERFACE", d.Id()) + return nil, fmt.Errorf("unexpected format of ID (%q), expected MACHINE/NETWORK_INTERFACE", d.Id()) } client := meta.(*client.Client) machine, err := getMachine(client, idParts[0]) @@ -34,18 +34,8 @@ func resourceMaasNetworkInterfacePhysical() *schema.Resource { if err != nil { return nil, err } - tfState := map[string]interface{}{ - "id": fmt.Sprintf("%v", n.ID), - "mac_address": n.MACAddress, - "machine": machine.SystemID, - "mtu": n.EffectiveMTU, - "name": n.Name, - "tags": n.Tags, - "vlan": n.VLAN.ID, - } - if err := setTerraformState(d, tfState); err != nil { - return nil, err - } + d.Set("machine", idParts[0]) + d.SetId(strconv.Itoa(n.ID)) return []*schema.ResourceData{d}, nil }, }, @@ -110,7 +100,7 @@ func resourceNetworkInterfacePhysicalCreate(ctx context.Context, d *schema.Resou return diag.FromErr(err) } } - d.SetId(fmt.Sprintf("%v", networkInterface.ID)) + d.SetId(strconv.Itoa(networkInterface.ID)) return resourceNetworkInterfacePhysicalRead(ctx, d, meta) } @@ -210,7 +200,7 @@ func findNetworkInterfacePhysical(client *client.Client, machineSystemID string, if n.Type != "physical" { continue } - if n.MACAddress == identifier || n.Name == identifier || fmt.Sprintf("%v", n.ID) == identifier { + if n.MACAddress == identifier || n.Name == identifier || strconv.Itoa(n.ID) == identifier { return &n, nil } } diff --git a/maas/resource_maas_network_interface_physical_test.go b/maas/resource_maas_network_interface_physical_test.go new file mode 100644 index 00000000..e656d05b --- /dev/null +++ b/maas/resource_maas_network_interface_physical_test.go @@ -0,0 +1,170 @@ +package maas_test + +import ( + "fmt" + "os" + "strconv" + "strings" + "terraform-provider-maas/maas/testutils" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/maas/gomaasclient/client" + "github.com/maas/gomaasclient/entity" +) + +func testAccMaasNetworkInterfacePhysical(name string, machine string, fabric string) string { + return fmt.Sprintf(` +data "maas_fabric" "default" { + name = "%s" +} + +data "maas_machine" "machine" { + hostname = "%s" +} + +data "maas_vlan" "default" { + fabric = data.maas_fabric.default.id + vlan = 0 +} + +resource "maas_network_interface_physical" "test" { + machine = data.maas_machine.machine.id + name = "%s" + mac_address = "01:12:34:56:78:9A" + mtu = 9000 + tags = ["tag1", "tag2"] + vlan = data.maas_vlan.default.id + } +`, fabric, machine, name) +} + +func TestAccResourceMaasNetworkInterfacePhysical_basic(t *testing.T) { + + var networkInterfacePhysical entity.NetworkInterface + name := acctest.RandomWithPrefix("tf-network-interface-physical") + machine := os.Getenv("TF_ACC_NETWORK_INTERFACE_MACHINE") + fabric := os.Getenv("TF_ACC_FABRIC") + + checks := []resource.TestCheckFunc{ + testAccMaasNetworkInterfacePhysicalCheckExists("maas_network_interface_physical.test", &networkInterfacePhysical), + resource.TestCheckResourceAttr("maas_network_interface_physical.test", "name", name), + resource.TestCheckResourceAttr("maas_network_interface_physical.test", "mac_address", "01:12:34:56:78:9A"), + resource.TestCheckResourceAttr("maas_network_interface_physical.test", "mtu", "9000"), + resource.TestCheckResourceAttr("maas_network_interface_physical.test", "tags.#", "2"), + resource.TestCheckResourceAttr("maas_network_interface_physical.test", "tags.0", "tag1"), + resource.TestCheckResourceAttr("maas_network_interface_physical.test", "tags.1", "tag2"), + resource.TestCheckResourceAttrPair("maas_network_interface_physical.test", "vlan", "data.maas_vlan.default", "id"), + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testutils.PreCheck(t, []string{"TF_ACC_NETWORK_INTERFACE_MACHINE", "TF_ACC_FABRIC"}) }, + Providers: testutils.TestAccProviders, + CheckDestroy: testAccCheckMaasNetworkInterfacePhysicalDestroy, + ErrorCheck: func(err error) error { return err }, + Steps: []resource.TestStep{ + { + Config: testAccMaasNetworkInterfacePhysical(name, machine, fabric), + Check: resource.ComposeTestCheckFunc(checks...), + }, + // Test import + { + ResourceName: "maas_network_interface_physical.test", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources["maas_network_interface_physical.test"] + if !ok { + return "", fmt.Errorf("resource not found: %s", "maas_network_interface_physical.test") + } + + if rs.Primary.ID == "" { + return "", fmt.Errorf("resource id not set") + } + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["machine"], rs.Primary.Attributes["id"]), nil + }, + }, + // Test import by MAC Address + { + ResourceName: "maas_network_interface_physical.test", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources["maas_network_interface_physical.test"] + if !ok { + return "", fmt.Errorf("resource not found: %s", "maas_network_interface_physical.test") + } + + if rs.Primary.ID == "" { + return "", fmt.Errorf("resource id not set") + } + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["machine"], rs.Primary.Attributes["mac_address"]), nil + }, + }, + }, + }) +} + +func testAccMaasNetworkInterfacePhysicalCheckExists(rn string, networkInterfacePhysical *entity.NetworkInterface) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rn] + if !ok { + return fmt.Errorf("resource not found: %s\n %#v", rn, s.RootModule().Resources) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("resource id not set") + } + + conn := testutils.TestAccProvider.Meta().(*client.Client) + id, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return err + } + gotNetworkInterfacePhysical, err := conn.NetworkInterface.Get(rs.Primary.Attributes["machine"], id) + if err != nil { + return fmt.Errorf("error getting network interface physical: %s", err) + } + + *networkInterfacePhysical = *gotNetworkInterfacePhysical + + return nil + } +} + +func testAccCheckMaasNetworkInterfacePhysicalDestroy(s *terraform.State) error { + // retrieve the connection established in Provider configuration + conn := testutils.TestAccProvider.Meta().(*client.Client) + + // loop through the resources in state, verifying each maas_network_interface_physical + // is destroyed + for _, rs := range s.RootModule().Resources { + if rs.Type != "maas_network_interface_physical" { + continue + } + + // Retrieve our maas_network_interface_physical by referencing it's state ID for API lookup + id, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return err + } + response, err := conn.NetworkInterface.Get(rs.Primary.Attributes["machine"], id) + if err == nil { + if response != nil && response.ID == id { + return fmt.Errorf("MAAS Network interface physical (%s) still exists.", rs.Primary.ID) + } + + return nil + } + + // If the error is equivalent to 404 not found, the maas_network_interface_physical is destroyed. + // Otherwise return the error + if !strings.Contains(err.Error(), "404 Not Found") { + return err + } + } + + return nil +}