From 2ce2aa61b1e718521291d63c88dbc401c326af5f Mon Sep 17 00:00:00 2001 From: David Costa Date: Thu, 22 Feb 2024 17:10:42 +0000 Subject: [PATCH 1/3] nest custom fields support (subnets) --- docs/index.md | 3 + plugin/providers/phpipam/config.go | 17 +++- .../phpipam/data_source_phpipam_subnet.go | 55 +++++++---- plugin/providers/phpipam/provider.go | 19 +++- .../resource_phpipam_first_free_subnet.go | 68 +++++++------- .../phpipam/resource_phpipam_subnet.go | 92 ++++++++++--------- plugin/providers/phpipam/subnet_structure.go | 15 ++- 7 files changed, 164 insertions(+), 105 deletions(-) diff --git a/docs/index.md b/docs/index.md index 81e673e3..96c3986b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -109,6 +109,9 @@ The options for the plugin are as follows: supplied via the `PHPIPAM_USER_NAME` variable. - `insecure` - Set to true to not validate the HTTPS certificate chain. Optional parameter, can be used only with HTTPS connections +- `nest_custom_fields` - Set to true if your PHPIPAM application has the nested + custom fields feature enabled. ptional parameter, currently only affects + subnet resources. ### Resource importing diff --git a/plugin/providers/phpipam/config.go b/plugin/providers/phpipam/config.go index 5b5d761a..79d3eb90 100644 --- a/plugin/providers/phpipam/config.go +++ b/plugin/providers/phpipam/config.go @@ -34,6 +34,9 @@ type Config struct { // Allow connect to HTTPS without SSL issuer validation Insecure bool + + // Whether the API client is configured to nest custom fields + NestCustomFields bool } // ProviderPHPIPAMClient is a structure that contains the client connections @@ -57,16 +60,19 @@ type ProviderPHPIPAMClient struct { // Mutex for free IP address allocation. addressAllocationLock sync.Mutex + + // Whether the API client is configured to nest custom values + NestCustomFields bool } // Client configures and returns a fully initialized PingdomClient. func (c *Config) Client() (interface{}, error) { cfg := phpipam.Config{ - AppID: c.AppID, - Endpoint: c.Endpoint, - Password: c.Password, - Username: c.Username, - Insecure: c.Insecure, + AppID: c.AppID, + Endpoint: c.Endpoint, + Password: c.Password, + Username: c.Username, + Insecure: c.Insecure, } log.Printf("[DEBUG] Initializing PHPIPAM controllers") sess := session.NewSession(cfg) @@ -78,6 +84,7 @@ func (c *Config) Client() (interface{}, error) { l2domainsController: l2domains.NewController(sess), subnetsController: subnets.NewController(sess), vlansController: vlans.NewController(sess), + NestCustomFields: c.NestCustomFields, } // Validate that our conneciton is okay diff --git a/plugin/providers/phpipam/data_source_phpipam_subnet.go b/plugin/providers/phpipam/data_source_phpipam_subnet.go index c7501ab0..d6e549b0 100644 --- a/plugin/providers/phpipam/data_source_phpipam_subnet.go +++ b/plugin/providers/phpipam/data_source_phpipam_subnet.go @@ -1,22 +1,24 @@ package phpipam import ( + "context" "errors" "fmt" "strconv" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/pavel-z1/phpipam-sdk-go/controllers/subnets" ) func dataSourcePHPIPAMSubnet() *schema.Resource { return &schema.Resource{ - Read: dataSourcePHPIPAMSubnetRead, - Schema: dataSourceSubnetSchema(), + ReadContext: dataSourcePHPIPAMSubnetRead, + Schema: dataSourceSubnetSchema(), } } -func dataSourcePHPIPAMSubnetRead(d *schema.ResourceData, meta interface{}) error { +func dataSourcePHPIPAMSubnetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { c := meta.(*ProviderPHPIPAMClient).subnetsController out := make([]subnets.Subnet, 1) var err error @@ -26,22 +28,22 @@ func dataSourcePHPIPAMSubnetRead(d *schema.ResourceData, meta interface{}) error case d.Get("subnet_id").(int) != 0: out[0], err = c.GetSubnetByID(d.Get("subnet_id").(int)) if err != nil { - return err + return diag.FromErr(err) } case d.Get("subnet_address").(string) != "" && d.Get("subnet_mask").(int) != 0 && d.Get("section_id").(int) == 0: out, err = c.GetSubnetsByCIDR(fmt.Sprintf("%s/%d", d.Get("subnet_address"), d.Get("subnet_mask"))) if err != nil { - return err + return diag.FromErr(err) } case d.Get("subnet_address").(string) != "" && d.Get("subnet_mask").(int) != 0 && d.Get("section_id").(int) != 0: out, err = c.GetSubnetsByCIDRAndSection(fmt.Sprintf("%s/%d", d.Get("subnet_address"), d.Get("subnet_mask")), d.Get("section_id").(int)) if err != nil { - return err + return diag.FromErr(err) } case d.Get("section_id").(int) != 0 && (d.Get("description").(string) != "" || d.Get("description_match").(string) != "" || len(d.Get("custom_field_filter").(map[string]interface{})) > 0): out, err = subnetSearchInSection(d, meta) if err != nil { - return err + return diag.FromErr(err) } default: // We need to ensure imported resources are not recreated when terraform apply is ran @@ -50,35 +52,48 @@ func dataSourcePHPIPAMSubnetRead(d *schema.ResourceData, meta interface{}) error if len(id) > 0 { subnet_id, err := strconv.Atoi(id) if err != nil { - return err + return diag.FromErr(err) } out[0], err = c.GetSubnetByID(subnet_id) if err != nil { - return err + return diag.FromErr(err) } } else { - return errors.New("No valid combination of parameters found - need one of subnet_id, subnet_address and subnet_mask, or section_id and (description|description_match|custom_field_filter)") + return diag.FromErr(errors.New("No valid combination of parameters found - need one of subnet_id, subnet_address and subnet_mask, or section_id and (description|description_match|custom_field_filter)")) } } if len(out) != 1 { - return errors.New("Your search returned zero or multiple results. Please correct your search and try again") + return diag.FromErr(errors.New("Your search returned zero or multiple results. Please correct your search and try again")) } - if checkSubnetsCustomFiledsExists(d, c) { - fields, err := c.GetSubnetCustomFields(out[0].ID) - switch { - case err == nil: - trimMap(fields) - if err := d.Set("custom_fields", fields); err != nil { - return err + if !meta.(*ProviderPHPIPAMClient).NestCustomFields { + if checkSubnetsCustomFiledsExists(d, c) { + fields, err := c.GetSubnetCustomFields(out[0].ID) + switch { + case err == nil: + trimMap(fields) + if err := d.Set("custom_fields", fields); err != nil { + return diag.FromErr(err) + } + case err != nil: + return diag.FromErr(err) } - case err != nil: - return err } } flattenSubnet(out[0], d) + if out[0].CustomFields != nil && !meta.(*ProviderPHPIPAMClient).NestCustomFields { + + var diags diag.Diagnostics + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Nest custom fields is enabled on the API", + Detail: "This API has enabled nested custom fields. Please set nest_custom_fields to true in the provider configuration.", + }) + return diags + } + return nil } diff --git a/plugin/providers/phpipam/provider.go b/plugin/providers/phpipam/provider.go index 3e0792b2..1b764aa2 100644 --- a/plugin/providers/phpipam/provider.go +++ b/plugin/providers/phpipam/provider.go @@ -38,6 +38,12 @@ func Provider() *schema.Provider { Default: false, Description: descriptions["insecure"], }, + "nest_custom_fields": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: descriptions["nest_custom_fields"], + }, }, ResourcesMap: map[string]*schema.Resource{ @@ -76,16 +82,19 @@ func init() { "username": "The username of the PHPIPAM account", "insecure": "Whether server should be accessed " + "without verifying the TLS certificate.", + "nest_custom_fields": "Whether the API client is configured " + + "to nest custom values.", } } func providerConfigure(d *schema.ResourceData) (interface{}, error) { config := Config{ - AppID: d.Get("app_id").(string), - Endpoint: d.Get("endpoint").(string), - Password: d.Get("password").(string), - Username: d.Get("username").(string), - Insecure: d.Get("insecure").(bool), + AppID: d.Get("app_id").(string), + Endpoint: d.Get("endpoint").(string), + Password: d.Get("password").(string), + Username: d.Get("username").(string), + Insecure: d.Get("insecure").(bool), + NestCustomFields: d.Get("nest_custom_fields").(bool), } return config.Client() } diff --git a/plugin/providers/phpipam/resource_phpipam_first_free_subnet.go b/plugin/providers/phpipam/resource_phpipam_first_free_subnet.go index 6c603883..78607b7f 100644 --- a/plugin/providers/phpipam/resource_phpipam_first_free_subnet.go +++ b/plugin/providers/phpipam/resource_phpipam_first_free_subnet.go @@ -1,10 +1,12 @@ package phpipam import ( + "context" "errors" "fmt" "strconv" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -15,18 +17,18 @@ import ( // read workflow is identical for both the resource and the data source. func resourcePHPIPAMFirstFreeSubnet() *schema.Resource { return &schema.Resource{ - Create: resourcePHPIPAMFirstFreeSubnetCreate, - Read: dataSourcePHPIPAMSubnetRead, - Update: resourcePHPIPAMFirstFreeSubnetUpdate, - Delete: resourcePHPIPAMFirstFreeSubnetDelete, - Schema: resourceFirstFreeSubnetSchema(), + CreateContext: resourcePHPIPAMFirstFreeSubnetCreate, + ReadContext: dataSourcePHPIPAMSubnetRead, + UpdateContext: resourcePHPIPAMFirstFreeSubnetUpdate, + DeleteContext: resourcePHPIPAMFirstFreeSubnetDelete, + Schema: resourceFirstFreeSubnetSchema(), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, } } -func resourcePHPIPAMFirstFreeSubnetCreate(d *schema.ResourceData, meta interface{}) error { +func resourcePHPIPAMFirstFreeSubnetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { // Get first free subnet from provided subnet_id subnet_id := d.Get("parent_subnet_id").(int) d.Set("subnet_id", nil) @@ -34,60 +36,64 @@ func resourcePHPIPAMFirstFreeSubnetCreate(d *schema.ResourceData, meta interface // Get address controller and start address creation c := meta.(*ProviderPHPIPAMClient).subnetsController - in := expandSubnet(d) + in := expandSubnet(d, meta.(*ProviderPHPIPAMClient).NestCustomFields) out, err := c.CreateFirstFreeSubnet(subnet_id, subnet_mask, in) if err != nil { - return err + return diag.FromErr(err) } d.Set("subnet_address", out) - // If we have custom fields, set them now. We need to get the IP address's ID - // beforehand. - if customFields, ok := d.GetOk("custom_fields"); ok { - addrs, err := c.GetSubnetsByCIDR(fmt.Sprintf("%s/%d", out, in.Mask)) - if err != nil { - return fmt.Errorf("Could not read IP address after creating: %s", err) - } + if !meta.(*ProviderPHPIPAMClient).NestCustomFields { + // If we have custom fields, set them now. We need to get the IP address's ID + // beforehand. + if customFields, ok := d.GetOk("custom_fields"); ok { + addrs, err := c.GetSubnetsByCIDR(fmt.Sprintf("%s/%d", out, in.Mask)) + if err != nil { + return diag.FromErr(fmt.Errorf("Could not read IP address after creating: %s", err)) + } - if len(addrs) != 1 { - return errors.New("IP address either missing or multiple results returned by reading IP after creation") - } + if len(addrs) != 1 { + return diag.FromErr(errors.New("IP address either missing or multiple results returned by reading IP after creation")) + } - d.SetId(strconv.Itoa(addrs[0].ID)) + d.SetId(strconv.Itoa(addrs[0].ID)) - if _, err := c.UpdateSubnetCustomFields(addrs[0].ID, customFields.(map[string]interface{})); err != nil { - return err + if _, err := c.UpdateSubnetCustomFields(addrs[0].ID, customFields.(map[string]interface{})); err != nil { + return diag.FromErr(err) + } } } - return dataSourcePHPIPAMSubnetRead(d, meta) + return dataSourcePHPIPAMSubnetRead(ctx, d, meta) } -func resourcePHPIPAMFirstFreeSubnetUpdate(d *schema.ResourceData, meta interface{}) error { +func resourcePHPIPAMFirstFreeSubnetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { c := meta.(*ProviderPHPIPAMClient).subnetsController - in := expandSubnet(d) + in := expandSubnet(d, meta.(*ProviderPHPIPAMClient).NestCustomFields) // SubnetAddress and mask need to be removed for update requests. in.SubnetAddress = "" in.Mask = 0 if _, err := c.UpdateSubnet(in); err != nil { - return err + return diag.FromErr(err) } - if err := updateCustomFields(d, c); err != nil { - return err + if !meta.(*ProviderPHPIPAMClient).NestCustomFields { + if err := updateCustomFields(d, c); err != nil { + return diag.FromErr(err) + } } - return dataSourcePHPIPAMSubnetRead(d, meta) + return dataSourcePHPIPAMSubnetRead(ctx, d, meta) } -func resourcePHPIPAMFirstFreeSubnetDelete(d *schema.ResourceData, meta interface{}) error { +func resourcePHPIPAMFirstFreeSubnetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { c := meta.(*ProviderPHPIPAMClient).subnetsController - in := expandSubnet(d) + in := expandSubnet(d, meta.(*ProviderPHPIPAMClient).NestCustomFields) if _, err := c.DeleteSubnet(in.ID); err != nil { - return err + return diag.FromErr(err) } d.SetId("") return nil diff --git a/plugin/providers/phpipam/resource_phpipam_subnet.go b/plugin/providers/phpipam/resource_phpipam_subnet.go index 47adc727..b4e87090 100644 --- a/plugin/providers/phpipam/resource_phpipam_subnet.go +++ b/plugin/providers/phpipam/resource_phpipam_subnet.go @@ -1,11 +1,13 @@ package phpipam import ( + "context" "errors" "fmt" "strconv" "strings" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/pavel-z1/phpipam-sdk-go/controllers/subnets" ) @@ -17,33 +19,33 @@ import ( // read workflow is identical for both the resource and the data source. func resourcePHPIPAMSubnet() *schema.Resource { return &schema.Resource{ - Create: resourcePHPIPAMSubnetCreate, - Read: dataSourcePHPIPAMSubnetRead, - Update: resourcePHPIPAMSubnetUpdate, - Delete: resourcePHPIPAMSubnetDelete, - Schema: resourceSubnetSchema(), + CreateContext: resourcePHPIPAMSubnetCreate, + ReadContext: dataSourcePHPIPAMSubnetRead, + UpdateContext: resourcePHPIPAMSubnetUpdate, + DeleteContext: resourcePHPIPAMSubnetDelete, + Schema: resourceSubnetSchema(), Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, } } -func resourcePHPIPAMSubnetCreate(d *schema.ResourceData, meta interface{}) error { +func resourcePHPIPAMSubnetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { c := meta.(*ProviderPHPIPAMClient).subnetsController - in := expandSubnet(d) + in := expandSubnet(d, meta.(*ProviderPHPIPAMClient).NestCustomFields) // Assert the ID field here is empty. If this is not empty the request will fail. in.ID = 0 if _, ok := d.GetOk("subnet_address"); ok { if _, err := c.CreateSubnet(in); err != nil { - return err + return diag.FromErr(err) } } else if parentSubnetId, ok := d.GetOk("parent_subnet_id"); ok { res, err := c.CreateFirstFreeSubnet(parentSubnetId.(int), d.Get("subnet_mask").(int), in) if err != nil { - return err + return diag.FromErr(err) } netAndMask := strings.Split(res, "/") @@ -52,41 +54,43 @@ func resourcePHPIPAMSubnetCreate(d *schema.ResourceData, meta interface{}) error d.Set("subnet_address", netAndMask[0]) d.Set("subnet_mask", maskNum) } else { - return errors.New("Unsupported scenario! One of 'subnet_address' or 'parent_subnet_id' must be set") + return diag.FromErr(errors.New("Unsupported scenario! One of 'subnet_address' or 'parent_subnet_id' must be set")) } - // If we have custom fields, set them now. We need to get the subnet's ID - // beforehand. - if customFields, ok := d.GetOk("custom_fields"); ok { - var subnets []subnets.Subnet - var err error - switch { - case in.SectionID != 0: - subnets, err = c.GetSubnetsByCIDRAndSection(fmt.Sprintf("%s/%d", in.SubnetAddress, in.Mask), in.SectionID) - default: - subnets, err = c.GetSubnetsByCIDR(fmt.Sprintf("%s/%d", in.SubnetAddress, in.Mask)) - } - if err != nil { - return fmt.Errorf("Could not read subnet after creating: %s", err) - } - - if len(subnets) != 1 { - return errors.New("Subnet either missing or multiple results returned by reading subnet after creation") - } - - d.SetId(strconv.Itoa(subnets[0].ID)) - - if _, err := c.UpdateSubnetCustomFields(subnets[0].ID, customFields.(map[string]interface{})); err != nil { - return err + if !meta.(*ProviderPHPIPAMClient).NestCustomFields { + // If we have custom fields, set them now. We need to get the subnet's ID + // beforehand. + if customFields, ok := d.GetOk("custom_fields"); ok { + var subnets []subnets.Subnet + var err error + switch { + case in.SectionID != 0: + subnets, err = c.GetSubnetsByCIDRAndSection(fmt.Sprintf("%s/%d", in.SubnetAddress, in.Mask), in.SectionID) + default: + subnets, err = c.GetSubnetsByCIDR(fmt.Sprintf("%s/%d", in.SubnetAddress, in.Mask)) + } + if err != nil { + return diag.FromErr(fmt.Errorf("Could not read subnet after creating: %s", err)) + } + + if len(subnets) != 1 { + return diag.FromErr(errors.New("Subnet either missing or multiple results returned by reading subnet after creation")) + } + + d.SetId(strconv.Itoa(subnets[0].ID)) + + if _, err := c.UpdateSubnetCustomFields(subnets[0].ID, customFields.(map[string]interface{})); err != nil { + return diag.FromErr(err) + } } } - return dataSourcePHPIPAMSubnetRead(d, meta) + return dataSourcePHPIPAMSubnetRead(ctx, d, meta) } -func resourcePHPIPAMSubnetUpdate(d *schema.ResourceData, meta interface{}) error { +func resourcePHPIPAMSubnetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { c := meta.(*ProviderPHPIPAMClient).subnetsController - in := expandSubnet(d) + in := expandSubnet(d, meta.(*ProviderPHPIPAMClient).NestCustomFields) // Remove the CIDR fields from the request, as these fields being present // implies that the subnet will be either split or renamed, which is not // supported by UpdateSubnet. These are implemented in the API but not in the @@ -94,22 +98,24 @@ func resourcePHPIPAMSubnetUpdate(d *schema.ResourceData, meta interface{}) error in.SubnetAddress = "" in.Mask = 0 if _, err := c.UpdateSubnet(in); err != nil { - return err + return diag.FromErr(err) } - if err := updateCustomFields(d, c); err != nil { - return err + if !meta.(*ProviderPHPIPAMClient).NestCustomFields { + if err := updateCustomFields(d, c); err != nil { + return diag.FromErr(err) + } } - return dataSourcePHPIPAMSubnetRead(d, meta) + return dataSourcePHPIPAMSubnetRead(ctx, d, meta) } -func resourcePHPIPAMSubnetDelete(d *schema.ResourceData, meta interface{}) error { +func resourcePHPIPAMSubnetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { c := meta.(*ProviderPHPIPAMClient).subnetsController - in := expandSubnet(d) + in := expandSubnet(d, meta.(*ProviderPHPIPAMClient).NestCustomFields) if _, err := c.DeleteSubnet(in.ID); err != nil { - return err + return diag.FromErr(err) } d.SetId("") return nil diff --git a/plugin/providers/phpipam/subnet_structure.go b/plugin/providers/phpipam/subnet_structure.go index 59976ce2..212fd5e3 100644 --- a/plugin/providers/phpipam/subnet_structure.go +++ b/plugin/providers/phpipam/subnet_structure.go @@ -239,7 +239,7 @@ func dataSourceSubnetsSchema() map[string]*schema.Schema { // expandSubnet returns the subnets.Subnet structure for a // phpiapm_subnet resource or data source. Depending on if we are dealing with // the resource or data source, extra considerations may need to be taken. -func expandSubnet(d *schema.ResourceData) subnets.Subnet { +func expandSubnet(d *schema.ResourceData, nestCustomFields bool) subnets.Subnet { s := subnets.Subnet{ ID: d.Get("subnet_id").(int), SubnetAddress: d.Get("subnet_address").(string), @@ -267,11 +267,20 @@ func expandSubnet(d *schema.ResourceData) subnets.Subnet { Location: d.Get("location_id").(int), Gateway: d.Get("gateway").(map[string]interface{}), GatewayID: d.Get("gateway_id").(string), + CustomFields: conditionalCustomFields(d, nestCustomFields), } return s } +func conditionalCustomFields(d *schema.ResourceData, nestCustomFields bool) map[string]interface{} { + if nestCustomFields { + return d.Get("custom_fields").(map[string]interface{}) + } else { + return nil + } +} + // flattenSubnet sets fields in a *schema.ResourceData with fields supplied by // the input subnets.Subnet. This is used in read operations. func flattenSubnet(s subnets.Subnet, d *schema.ResourceData) { @@ -304,6 +313,10 @@ func flattenSubnet(s subnets.Subnet, d *schema.ResourceData) { d.Set("edit_date", s.EditDate) d.Set("gateway", s.Gateway) d.Set("gateway_id", s.GatewayID) + + if s.CustomFields != nil { + d.Set("custom_fields", s.CustomFields) + } } // subnetDescriptionMatchSchema returns a *schema.Schema for description From 191252f33562e9d6651887ddc9190b6ce02d659c Mon Sep 17 00:00:00 2001 From: David Costa Date: Mon, 26 Feb 2024 14:38:39 +0000 Subject: [PATCH 2/3] fix expected 404 error message in VLANs and subnet tests --- plugin/providers/phpipam/resource_phpipam_subnet_test.go | 3 ++- plugin/providers/phpipam/resource_phpipam_vlan_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin/providers/phpipam/resource_phpipam_subnet_test.go b/plugin/providers/phpipam/resource_phpipam_subnet_test.go index e256cc9a..fccd2752 100644 --- a/plugin/providers/phpipam/resource_phpipam_subnet_test.go +++ b/plugin/providers/phpipam/resource_phpipam_subnet_test.go @@ -153,10 +153,11 @@ func testAccCheckResourcePHPIPAMSubnetCreated(s *terraform.State) error { func testAccCheckResourcePHPIPAMSubnetDeleted(s *terraform.State) error { subnetController := testAccProvider.Meta().(*ProviderPHPIPAMClient).subnetsController _, err := subnetController.GetSubnetsByCIDRAndSection(testAccResourcePHPIPAMSubnetCIDR, testAccResourceSubnetPHPIPAMSectionID) + error_messages := linearSearchSlice{"Error from API (404): No results (filter applied)", "Error from API (404): No subnets found"} switch { case err == nil: return errors.New("Expected error, got none") - case err != nil && err.Error() != "Error from API (404): No results (filter applied)": + case err != nil && !error_messages.Has(err.Error()): return fmt.Errorf("Expected 404, got %s", err) } diff --git a/plugin/providers/phpipam/resource_phpipam_vlan_test.go b/plugin/providers/phpipam/resource_phpipam_vlan_test.go index bad8c93f..0bde9105 100644 --- a/plugin/providers/phpipam/resource_phpipam_vlan_test.go +++ b/plugin/providers/phpipam/resource_phpipam_vlan_test.go @@ -148,10 +148,11 @@ func testAccCheckResourcePHPIPAMVLANCreated(s *terraform.State) error { func testAccCheckResourcePHPIPAMVLANDeleted(s *terraform.State) error { c := testAccProvider.Meta().(*ProviderPHPIPAMClient).vlansController _, err := c.GetVLANsByNumberAndDomainID(testAccResourcePHPIPAMVLANNumber, testAccResourceVlanPHPIPAML2DomainID) + error_messages := linearSearchSlice{"Error from API (404): No results (filter applied)", "Error from API (404): Vlans not found"} switch { case err == nil: return errors.New("Expected error, got none") - case err != nil && err.Error() != "Error from API (404): No results (filter applied)": + case err != nil && !error_messages.Has(err.Error()): return fmt.Errorf("Expected 404, got %s", err) } From 3750307bc7eb3b23aaa6b9c2f908417d33528f73 Mon Sep 17 00:00:00 2001 From: David Costa Date: Fri, 19 Apr 2024 09:43:33 +0100 Subject: [PATCH 3/3] Add nest_custom_fields info to docs --- docs/index.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/index.md b/docs/index.md index 96c3986b..27feecd8 100644 --- a/docs/index.md +++ b/docs/index.md @@ -39,11 +39,12 @@ along to the configuration for a VM, say, for example, a ```hcl provider "phpipam" { - app_id = "test" - endpoint = "https://phpipam.example.com/api" - password = "PHPIPAM_PASSWORD" - username = "Admin" - insecure = false + app_id = "test" + endpoint = "https://phpipam.example.com/api" + password = "PHPIPAM_PASSWORD" + username = "Admin" + insecure = false + nest_custom_fields = false } data "phpipam_subnet" "subnet" { @@ -109,9 +110,9 @@ The options for the plugin are as follows: supplied via the `PHPIPAM_USER_NAME` variable. - `insecure` - Set to true to not validate the HTTPS certificate chain. Optional parameter, can be used only with HTTPS connections -- `nest_custom_fields` - Set to true if your PHPIPAM application has the nested - custom fields feature enabled. ptional parameter, currently only affects - subnet resources. +- `nest_custom_fields` - Set to true if the API application has this feature + enabled. Currently, this allows the provider to only make one API call when + creating subnets, instead of two. ### Resource importing