diff --git a/.github/docs/contribution-guide/resource.go b/.github/docs/contribution-guide/resource.go index 89f210dbc..c83ca918d 100644 --- a/.github/docs/contribution-guide/resource.go +++ b/.github/docs/contribution-guide/resource.go @@ -2,7 +2,9 @@ package foo import ( "context" + "errors" "fmt" + "net/http" "strings" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" @@ -13,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" fooUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/foo/utils" @@ -276,12 +279,21 @@ func (r *barResource) Read(ctx context.Context, req resource.ReadRequest, resp * projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) barId := model.BarId.ValueString() + if barId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "bar_id", barId) barResp, err := r.client.GetBar(ctx, projectId, region, barId).Execute() if err != nil { + if oapiErr, ok := errors.AsType[*oapierror.GenericOpenAPIError](err); ok && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading bar", fmt.Sprintf("Calling API: %v", err)) return } @@ -338,6 +350,10 @@ func (r *barResource) Delete(ctx context.Context, req resource.DeleteRequest, re // Delete existing bar _, err := r.client.DeleteBar(ctx, projectId, region, barId).Execute() if err != nil { + if oapiErr, ok := errors.AsType[*oapierror.GenericOpenAPIError](err); ok && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting bar", fmt.Sprintf("Calling API: %v", err)) } diff --git a/stackit/internal/services/alb/applicationloadbalancer/resource.go b/stackit/internal/services/alb/applicationloadbalancer/resource.go index 14cef6558..f5f77c351 100644 --- a/stackit/internal/services/alb/applicationloadbalancer/resource.go +++ b/stackit/internal/services/alb/applicationloadbalancer/resource.go @@ -1344,6 +1344,11 @@ func (r *applicationLoadBalancerResource) Delete(ctx context.Context, req resour // Delete Application Load Balancer _, err := r.client.DefaultAPI.DeleteLoadBalancer(ctx, projectId, region, name).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } errStr := utils.PrettyApiErr(ctx, &resp.Diagnostics, err) core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting Application Load Balancer", fmt.Sprintf("Calling API for delete: %v", errStr)) return diff --git a/stackit/internal/services/authorization/customrole/resource.go b/stackit/internal/services/authorization/customrole/resource.go index 3aa5020f3..663ecd93f 100644 --- a/stackit/internal/services/authorization/customrole/resource.go +++ b/stackit/internal/services/authorization/customrole/resource.go @@ -213,7 +213,14 @@ func (r *customRoleResource) Read(ctx context.Context, req resource.ReadRequest, ctx = r.annotateLogger(ctx, &model) - roleResp, err := r.client.GetRoleExecute(ctx, r.resourceType, model.ResourceId.ValueString(), model.RoleId.ValueString()) + roleId := model.RoleId.ValueString() + if roleId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } + + roleResp, err := r.client.GetRoleExecute(ctx, r.resourceType, model.ResourceId.ValueString(), roleId) if err != nil { var oapiErr *oapierror.GenericOpenAPIError diff --git a/stackit/internal/services/authorization/roleassignments/resource.go b/stackit/internal/services/authorization/roleassignments/resource.go index 50025ab80..fe195f5a2 100644 --- a/stackit/internal/services/authorization/roleassignments/resource.go +++ b/stackit/internal/services/authorization/roleassignments/resource.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "strings" "time" @@ -15,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/services/authorization" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" @@ -316,6 +318,11 @@ func (r *roleAssignmentResource) Delete(ctx context.Context, req resource.Delete // Delete existing project role assignment _, err := r.authorizationClient.RemoveMembers(ctx, model.ResourceId.ValueString()).RemoveMembersPayload(payload).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, fmt.Sprintf("Error deleting %s role assignment", r.apiName), fmt.Sprintf("Calling API: %v", err)) } diff --git a/stackit/internal/services/cdn/customdomain/resource.go b/stackit/internal/services/cdn/customdomain/resource.go index a8ffe7363..97863fda3 100644 --- a/stackit/internal/services/cdn/customdomain/resource.go +++ b/stackit/internal/services/cdn/customdomain/resource.go @@ -374,7 +374,13 @@ func (r *customDomainResource) Delete(ctx context.Context, req resource.DeleteRe _, err := r.client.DefaultAPI.DeleteCustomDomain(ctx, projectId, distributionId, name).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Delete CDN custom domain", fmt.Sprintf("Delete custom domain: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/cdn/distribution/resource.go b/stackit/internal/services/cdn/distribution/resource.go index 27858777a..87e9a6591 100644 --- a/stackit/internal/services/cdn/distribution/resource.go +++ b/stackit/internal/services/cdn/distribution/resource.go @@ -481,6 +481,11 @@ func (r *distributionResource) Read(ctx context.Context, req resource.ReadReques projectId := model.ProjectId.ValueString() distributionId := model.DistributionId.ValueString() + if distributionId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "distribution_id", distributionId) diff --git a/stackit/internal/services/dns/recordset/resource.go b/stackit/internal/services/dns/recordset/resource.go index 99e086835..dcdf3f09c 100644 --- a/stackit/internal/services/dns/recordset/resource.go +++ b/stackit/internal/services/dns/recordset/resource.go @@ -2,7 +2,9 @@ package dns import ( "context" + "errors" "fmt" + "net/http" "strings" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" @@ -17,6 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" dns "github.com/stackitcloud/stackit-sdk-go/services/dns/v1api" "github.com/stackitcloud/stackit-sdk-go/services/dns/v1api/wait" @@ -292,12 +295,22 @@ func (r *recordSetResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() zoneId := model.ZoneId.ValueString() recordSetId := model.RecordSetId.ValueString() + if recordSetId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "zone_id", zoneId) ctx = tflog.SetField(ctx, "record_set_id", recordSetId) recordSetResp, err := r.client.DefaultAPI.GetRecordSet(ctx, projectId, zoneId, recordSetId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading record set", fmt.Sprintf("Calling API: %v", err)) return } @@ -417,7 +430,12 @@ func (r *recordSetResource) Delete(ctx context.Context, req resource.DeleteReque // Delete existing record set _, err := r.client.DefaultAPI.DeleteRecordSet(ctx, projectId, zoneId, recordSetId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting record set", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/dns/zone/resource.go b/stackit/internal/services/dns/zone/resource.go index 9567defc2..496417fc3 100644 --- a/stackit/internal/services/dns/zone/resource.go +++ b/stackit/internal/services/dns/zone/resource.go @@ -2,8 +2,10 @@ package dns import ( "context" + "errors" "fmt" "math" + "net/http" "regexp" "strings" @@ -24,6 +26,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" dns "github.com/stackitcloud/stackit-sdk-go/services/dns/v1api" "github.com/stackitcloud/stackit-sdk-go/services/dns/v1api/wait" @@ -381,11 +384,21 @@ func (r *zoneResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() zoneId := model.ZoneId.ValueString() + if zoneId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "zone_id", zoneId) zoneResp, err := r.client.DefaultAPI.GetZone(ctx, projectId, zoneId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading zone", fmt.Sprintf("Calling API: %v", err)) return } @@ -501,6 +514,11 @@ func (r *zoneResource) Delete(ctx context.Context, req resource.DeleteRequest, r // Delete existing zone _, err := r.client.DefaultAPI.DeleteZone(ctx, projectId, zoneId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting zone", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/edgecloud/instance/resource.go b/stackit/internal/services/edgecloud/instance/resource.go index 0c24b8cdc..c10ad8047 100644 --- a/stackit/internal/services/edgecloud/instance/resource.go +++ b/stackit/internal/services/edgecloud/instance/resource.go @@ -321,6 +321,11 @@ func (i *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() region := i.providerData.GetRegionWithOverride(model.Region) instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "region", region) diff --git a/stackit/internal/services/git/instance/resource.go b/stackit/internal/services/git/instance/resource.go index 6980afce8..da6dc5450 100644 --- a/stackit/internal/services/git/instance/resource.go +++ b/stackit/internal/services/git/instance/resource.go @@ -266,6 +266,11 @@ func (g *gitResource) Read(ctx context.Context, req resource.ReadRequest, resp * // Extract the project ID and instance id of the model projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } // Read the current git instance via id gitInstanceResp, err := g.client.DefaultAPI.GetInstance(ctx, projectId, instanceId).Execute() @@ -325,6 +330,10 @@ func (g *gitResource) Delete(ctx context.Context, req resource.DeleteRequest, re // Call API to delete the existing git instance. err := g.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting git instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/affinitygroup/resource.go b/stackit/internal/services/iaas/affinitygroup/resource.go index 726ac8eeb..c788e97ac 100644 --- a/stackit/internal/services/iaas/affinitygroup/resource.go +++ b/stackit/internal/services/iaas/affinitygroup/resource.go @@ -2,6 +2,7 @@ package affinitygroup import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -245,6 +246,11 @@ func (r *affinityGroupResource) Read(ctx context.Context, req resource.ReadReque projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) affinityGroupId := model.AffinityGroupId.ValueString() + if affinityGroupId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -254,8 +260,8 @@ func (r *affinityGroupResource) Read(ctx context.Context, req resource.ReadReque affinityGroupResp, err := r.client.GetAffinityGroupExecute(ctx, projectId, region, affinityGroupId) if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -305,6 +311,11 @@ func (r *affinityGroupResource) Delete(ctx context.Context, req resource.DeleteR // Delete existing affinity group err := r.client.DeleteAffinityGroupExecute(ctx, projectId, region, affinityGroupId) if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting affinity group", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/image/resource.go b/stackit/internal/services/iaas/image/resource.go index 55a7ef603..ad11af6d3 100644 --- a/stackit/internal/services/iaas/image/resource.go +++ b/stackit/internal/services/iaas/image/resource.go @@ -3,6 +3,7 @@ package image import ( "bufio" "context" + "errors" "fmt" "net/http" "os" @@ -510,6 +511,11 @@ func (r *imageResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) imageId := model.ImageId.ValueString() + if imageId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -519,8 +525,8 @@ func (r *imageResource) Read(ctx context.Context, req resource.ReadRequest, resp imageResp, err := r.client.GetImage(ctx, projectId, region, imageId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -623,6 +629,11 @@ func (r *imageResource) Delete(ctx context.Context, req resource.DeleteRequest, // Delete existing image err := r.client.DeleteImage(ctx, projectId, region, imageId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting image", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/keypair/resource.go b/stackit/internal/services/iaas/keypair/resource.go index eada04e21..8c86866ab 100644 --- a/stackit/internal/services/iaas/keypair/resource.go +++ b/stackit/internal/services/iaas/keypair/resource.go @@ -2,6 +2,7 @@ package keypair import ( "context" + "errors" "fmt" "net/http" "strings" @@ -200,8 +201,8 @@ func (r *keyPairResource) Read(ctx context.Context, req resource.ReadRequest, re keyPairResp, err := r.client.GetKeyPair(ctx, name).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -296,6 +297,11 @@ func (r *keyPairResource) Delete(ctx context.Context, req resource.DeleteRequest // Delete existing key pair err := r.client.DeleteKeyPair(ctx, name).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting key pair", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/network/resource.go b/stackit/internal/services/iaas/network/resource.go index e9014064d..f711f51f1 100644 --- a/stackit/internal/services/iaas/network/resource.go +++ b/stackit/internal/services/iaas/network/resource.go @@ -2,6 +2,7 @@ package network import ( "context" + "errors" "fmt" "net" "net/http" @@ -456,6 +457,11 @@ func (r *networkResource) Read(ctx context.Context, req resource.ReadRequest, re projectId := model.ProjectId.ValueString() networkId := model.NetworkId.ValueString() + if networkId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "network_id", networkId) @@ -463,8 +469,8 @@ func (r *networkResource) Read(ctx context.Context, req resource.ReadRequest, re networkResp, err := r.client.GetNetwork(ctx, projectId, region, networkId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -570,6 +576,11 @@ func (r *networkResource) Delete(ctx context.Context, req resource.DeleteRequest // Delete existing network err := r.client.DeleteNetwork(ctx, projectId, region, networkId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/networkarea/resource.go b/stackit/internal/services/iaas/networkarea/resource.go index 4351fe291..5f8c4b69d 100644 --- a/stackit/internal/services/iaas/networkarea/resource.go +++ b/stackit/internal/services/iaas/networkarea/resource.go @@ -444,6 +444,11 @@ func (r *networkAreaResource) Read(ctx context.Context, req resource.ReadRequest organizationId := model.OrganizationId.ValueString() networkAreaId := model.NetworkAreaId.ValueString() + if networkAreaId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -453,8 +458,7 @@ func (r *networkAreaResource) Read(ctx context.Context, req resource.ReadRequest networkAreaResp, err := r.client.GetNetworkArea(ctx, organizationId, networkAreaId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -479,8 +483,7 @@ func (r *networkAreaResource) Read(ctx context.Context, req resource.ReadRequest networkAreaRegionResp, err := r.client.GetNetworkAreaRegion(ctx, organizationId, networkAreaId, "eu01").Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if !(ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusBadRequest)) { // TODO: iaas api returns http 400 in case network area region is not found + if !(errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusBadRequest)) { // TODO: iaas api returns http 400 in case network area region is not found core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network area region", fmt.Sprintf("Calling API: %v", err)) return } @@ -606,8 +609,7 @@ func (r *networkAreaResource) Update(ctx context.Context, req resource.UpdateReq networkAreaRegionResp, err := r.client.GetNetworkAreaRegion(ctx, organizationId, networkAreaId, "eu01").Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusBadRequest) { // TODO: iaas api returns http 400 in case network area region is not found + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusBadRequest) { // TODO: iaas api returns http 400 in case network area region is not found return } core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network area region", fmt.Sprintf("Calling API: %v", err)) @@ -657,6 +659,11 @@ func (r *networkAreaResource) Delete(ctx context.Context, req resource.DeleteReq _, err := wait.ReadyForNetworkAreaDeletionWaitHandler(ctx, r.client, r.resourceManagerClient, organizationId, networkAreaId).WaitWithContext(ctx) if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network area", fmt.Sprintf("Network area ready for deletion waiting: %v", err)) return } @@ -664,6 +671,11 @@ func (r *networkAreaResource) Delete(ctx context.Context, req resource.DeleteReq // Get all configured regions so we can delete them one by one before deleting the network area regionsListResp, err := r.client.ListNetworkAreaRegions(ctx, organizationId, networkAreaId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network area region", fmt.Sprintf("Calling API to list configured regions: %v", err)) return } @@ -672,6 +684,10 @@ func (r *networkAreaResource) Delete(ctx context.Context, req resource.DeleteReq for region := range *regionsListResp.Regions { err = r.client.DeleteNetworkAreaRegion(ctx, organizationId, networkAreaId, region).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + continue + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network area region", fmt.Sprintf("Calling API: %v", err)) return } @@ -686,6 +702,11 @@ func (r *networkAreaResource) Delete(ctx context.Context, req resource.DeleteReq // Delete existing network area err = r.client.DeleteNetworkArea(ctx, organizationId, networkAreaId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network area", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/networkarearegion/resource.go b/stackit/internal/services/iaas/networkarearegion/resource.go index cc7c23b01..f3e3a9ce5 100644 --- a/stackit/internal/services/iaas/networkarearegion/resource.go +++ b/stackit/internal/services/iaas/networkarearegion/resource.go @@ -328,6 +328,11 @@ func (r *networkAreaRegionResource) Read(ctx context.Context, req resource.ReadR organizationId := model.OrganizationId.ValueString() networkAreaId := model.NetworkAreaId.ValueString() + if networkAreaId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "organization_id", organizationId) ctx = tflog.SetField(ctx, "network_area_id", networkAreaId) @@ -338,11 +343,12 @@ func (r *networkAreaRegionResource) Read(ctx context.Context, req resource.ReadR networkAreaRegionResp, err := r.client.GetNetworkAreaRegion(ctx, organizationId, networkAreaId, region).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading network area region", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) @@ -455,6 +461,10 @@ func (r *networkAreaRegionResource) Delete(ctx context.Context, req resource.Del // Delete network area region configuration err = r.client.DeleteNetworkAreaRegion(ctx, organizationId, networkAreaId, region).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network area region", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/networkarearoute/resource.go b/stackit/internal/services/iaas/networkarearoute/resource.go index 6906232d0..ed2e06016 100644 --- a/stackit/internal/services/iaas/networkarearoute/resource.go +++ b/stackit/internal/services/iaas/networkarearoute/resource.go @@ -2,6 +2,7 @@ package networkarearoute import ( "context" + "errors" "fmt" "net/http" "strings" @@ -405,6 +406,11 @@ func (r *networkAreaRouteResource) Read(ctx context.Context, req resource.ReadRe networkAreaId := model.NetworkAreaId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) networkAreaRouteId := model.NetworkAreaRouteId.ValueString() + if networkAreaRouteId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -415,8 +421,8 @@ func (r *networkAreaRouteResource) Read(ctx context.Context, req resource.ReadRe networkAreaRouteResp, err := r.client.GetNetworkAreaRoute(ctx, organizationId, networkAreaId, region, networkAreaRouteId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -466,6 +472,10 @@ func (r *networkAreaRouteResource) Delete(ctx context.Context, req resource.Dele // Delete existing network err := r.client.DeleteNetworkAreaRoute(ctx, organizationId, networkAreaId, region, networkAreaRouteId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network area route", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/networkinterface/resource.go b/stackit/internal/services/iaas/networkinterface/resource.go index 00abde61e..a021c9027 100644 --- a/stackit/internal/services/iaas/networkinterface/resource.go +++ b/stackit/internal/services/iaas/networkinterface/resource.go @@ -2,6 +2,7 @@ package networkinterface import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -337,6 +338,11 @@ func (r *networkInterfaceResource) Read(ctx context.Context, req resource.ReadRe region := r.providerData.GetRegionWithOverride(model.Region) networkId := model.NetworkId.ValueString() networkInterfaceId := model.NetworkInterfaceId.ValueString() + if networkInterfaceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -347,8 +353,8 @@ func (r *networkInterfaceResource) Read(ctx context.Context, req resource.ReadRe networkInterfaceResp, err := r.client.GetNic(ctx, projectId, region, networkId, networkInterfaceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -455,6 +461,10 @@ func (r *networkInterfaceResource) Delete(ctx context.Context, req resource.Dele // Delete existing network interface err := r.client.DeleteNic(ctx, projectId, region, networkId, networkInterfaceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting network interface", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/networkinterfaceattach/resource.go b/stackit/internal/services/iaas/networkinterfaceattach/resource.go index 419d20055..de2102ab3 100644 --- a/stackit/internal/services/iaas/networkinterfaceattach/resource.go +++ b/stackit/internal/services/iaas/networkinterfaceattach/resource.go @@ -2,6 +2,7 @@ package networkinterfaceattach import ( "context" + "errors" "fmt" "net/http" "strings" @@ -226,8 +227,8 @@ func (r *networkInterfaceAttachResource) Read(ctx context.Context, req resource. nics, err := r.client.ListServerNICs(ctx, projectId, region, serverId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -296,6 +297,10 @@ func (r *networkInterfaceAttachResource) Delete(ctx context.Context, req resourc // Remove network_interface from server err := r.client.RemoveNicFromServer(ctx, projectId, region, serverId, network_interfaceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error removing network interface from server", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/publicip/resource.go b/stackit/internal/services/iaas/publicip/resource.go index 72e0d20f0..d39d241f9 100644 --- a/stackit/internal/services/iaas/publicip/resource.go +++ b/stackit/internal/services/iaas/publicip/resource.go @@ -2,6 +2,7 @@ package publicip import ( "context" + "errors" "fmt" "net/http" "strings" @@ -240,6 +241,11 @@ func (r *publicIpResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) publicIpId := model.PublicIpId.ValueString() + if publicIpId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -249,8 +255,8 @@ func (r *publicIpResource) Read(ctx context.Context, req resource.ReadRequest, r publicIpResp, err := r.client.GetPublicIP(ctx, projectId, region, publicIpId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -353,6 +359,11 @@ func (r *publicIpResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing publicIp err := r.client.DeletePublicIP(ctx, projectId, region, publicIpId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting public IP", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/publicipassociate/resource.go b/stackit/internal/services/iaas/publicipassociate/resource.go index 702b46693..2f3e5dcc2 100644 --- a/stackit/internal/services/iaas/publicipassociate/resource.go +++ b/stackit/internal/services/iaas/publicipassociate/resource.go @@ -2,6 +2,7 @@ package publicipassociate import ( "context" + "errors" "fmt" "net/http" "strings" @@ -255,8 +256,8 @@ func (r *publicIpAssociateResource) Read(ctx context.Context, req resource.ReadR publicIpResp, err := r.client.GetPublicIP(ctx, projectId, region, publicIpId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -314,6 +315,11 @@ func (r *publicIpAssociateResource) Delete(ctx context.Context, req resource.Del _, err := r.client.UpdatePublicIP(ctx, projectId, region, publicIpId).UpdatePublicIPPayload(*payload).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting public IP association", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/routingtable/route/resource.go b/stackit/internal/services/iaas/routingtable/route/resource.go index aa95b2d4c..ce8680cb2 100644 --- a/stackit/internal/services/iaas/routingtable/route/resource.go +++ b/stackit/internal/services/iaas/routingtable/route/resource.go @@ -2,9 +2,12 @@ package route import ( "context" + "errors" "fmt" + "net/http" "strings" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/services/iaas" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/routingtable/shared" @@ -302,6 +305,11 @@ func (r *routeResource) Read(ctx context.Context, req resource.ReadRequest, resp routingTableId := model.RoutingTableId.ValueString() networkAreaId := model.NetworkAreaId.ValueString() routeId := model.RouteId.ValueString() + if routeId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "organization_id", organizationId) @@ -312,6 +320,11 @@ func (r *routeResource) Read(ctx context.Context, req resource.ReadRequest, resp routeResp, err := r.client.GetRouteOfRoutingTable(ctx, organizationId, networkAreaId, region, routingTableId, routeId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading routing table route", fmt.Sprintf("Calling API: %v", err)) return } @@ -422,7 +435,12 @@ func (r *routeResource) Delete(ctx context.Context, req resource.DeleteRequest, // Delete existing routing table route err := r.client.DeleteRouteFromRoutingTable(ctx, organizationId, networkAreaId, region, routingTableId, routeId).Execute() if err != nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error routing table route", fmt.Sprintf("Calling API: %v", err)) + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } + core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting routing table route", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/iaas/routingtable/table/resource.go b/stackit/internal/services/iaas/routingtable/table/resource.go index 5e267d8cb..284f68c7d 100644 --- a/stackit/internal/services/iaas/routingtable/table/resource.go +++ b/stackit/internal/services/iaas/routingtable/table/resource.go @@ -2,6 +2,7 @@ package table import ( "context" + "errors" "fmt" "net/http" "strings" @@ -21,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -284,6 +286,11 @@ func (r *routingTableResource) Read(ctx context.Context, req resource.ReadReques organizationId := model.OrganizationId.ValueString() routingTableId := model.RoutingTableId.ValueString() + if routingTableId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } networkAreaId := model.NetworkAreaId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) @@ -409,6 +416,11 @@ func (r *routingTableResource) Delete(ctx context.Context, req resource.DeleteRe // Delete existing routing table err := r.client.DeleteRoutingTableFromArea(ctx, organizationId, networkAreaId, region, routingTableId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting routing table", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/securitygroup/resource.go b/stackit/internal/services/iaas/securitygroup/resource.go index 8138e63cf..08924a3f2 100644 --- a/stackit/internal/services/iaas/securitygroup/resource.go +++ b/stackit/internal/services/iaas/securitygroup/resource.go @@ -2,6 +2,7 @@ package securitygroup import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -259,6 +260,11 @@ func (r *securityGroupResource) Read(ctx context.Context, req resource.ReadReque projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) securityGroupId := model.SecurityGroupId.ValueString() + if securityGroupId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -268,8 +274,8 @@ func (r *securityGroupResource) Read(ctx context.Context, req resource.ReadReque securityGroupResp, err := r.client.GetSecurityGroup(ctx, projectId, region, securityGroupId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -372,6 +378,11 @@ func (r *securityGroupResource) Delete(ctx context.Context, req resource.DeleteR // Delete existing security group err := r.client.DeleteSecurityGroup(ctx, projectId, region, securityGroupId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting security group", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/securitygrouprule/resource.go b/stackit/internal/services/iaas/securitygrouprule/resource.go index a94c85556..1f73d3ed7 100644 --- a/stackit/internal/services/iaas/securitygrouprule/resource.go +++ b/stackit/internal/services/iaas/securitygrouprule/resource.go @@ -2,6 +2,7 @@ package securitygrouprule import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -518,6 +519,11 @@ func (r *securityGroupRuleResource) Read(ctx context.Context, req resource.ReadR region := r.providerData.GetRegionWithOverride(model.Region) securityGroupId := model.SecurityGroupId.ValueString() securityGroupRuleId := model.SecurityGroupRuleId.ValueString() + if securityGroupRuleId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -528,8 +534,8 @@ func (r *securityGroupRuleResource) Read(ctx context.Context, req resource.ReadR securityGroupRuleResp, err := r.client.GetSecurityGroupRule(ctx, projectId, region, securityGroupId, securityGroupRuleId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -585,6 +591,11 @@ func (r *securityGroupRuleResource) Delete(ctx context.Context, req resource.Del // Delete existing security group rule err := r.client.DeleteSecurityGroupRule(ctx, projectId, region, securityGroupId, securityGroupRuleId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting security group rule", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/server/resource.go b/stackit/internal/services/iaas/server/resource.go index a56ef0747..ebf74767e 100644 --- a/stackit/internal/services/iaas/server/resource.go +++ b/stackit/internal/services/iaas/server/resource.go @@ -3,6 +3,7 @@ package server import ( "context" "encoding/base64" + "errors" "fmt" "net/http" "regexp" @@ -677,6 +678,11 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) serverId := model.ServerId.ValueString() + if serverId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -688,8 +694,8 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res serverReq = serverReq.Details(true) serverResp, err := serverReq.Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -863,6 +869,11 @@ func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest, // Delete existing server err := r.client.DeleteServer(ctx, projectId, region, serverId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting server", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/serviceaccountattach/resource.go b/stackit/internal/services/iaas/serviceaccountattach/resource.go index 2118454d9..4831eca09 100644 --- a/stackit/internal/services/iaas/serviceaccountattach/resource.go +++ b/stackit/internal/services/iaas/serviceaccountattach/resource.go @@ -2,6 +2,7 @@ package serviceaccountattach import ( "context" + "errors" "fmt" "net/http" "strings" @@ -223,8 +224,8 @@ func (r *serviceAccountAttachResource) Read(ctx context.Context, req resource.Re serviceAccounts, err := r.client.ListServerServiceAccounts(ctx, projectId, region, serverId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -291,6 +292,11 @@ func (r *serviceAccountAttachResource) Delete(ctx context.Context, req resource. // Remove service_account from server _, err := r.client.RemoveServiceAccountFromServer(ctx, projectId, region, serverId, service_accountId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error removing service account from server", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/volume/resource.go b/stackit/internal/services/iaas/volume/resource.go index ca9ec8bd1..7d9ecd5ed 100644 --- a/stackit/internal/services/iaas/volume/resource.go +++ b/stackit/internal/services/iaas/volume/resource.go @@ -2,6 +2,7 @@ package volume import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -513,6 +514,11 @@ func (r *volumeResource) Read(ctx context.Context, req resource.ReadRequest, res projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) volumeId := model.VolumeId.ValueString() + if volumeId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = core.InitProviderContext(ctx) @@ -522,8 +528,8 @@ func (r *volumeResource) Read(ctx context.Context, req resource.ReadRequest, res volumeResp, err := r.client.GetVolume(ctx, projectId, region, volumeId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -644,6 +650,11 @@ func (r *volumeResource) Delete(ctx context.Context, req resource.DeleteRequest, // Delete existing volume err := r.client.DeleteVolume(ctx, projectId, region, volumeId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting volume", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/iaas/volumeattach/resource.go b/stackit/internal/services/iaas/volumeattach/resource.go index f84890c95..190f48574 100644 --- a/stackit/internal/services/iaas/volumeattach/resource.go +++ b/stackit/internal/services/iaas/volumeattach/resource.go @@ -2,6 +2,7 @@ package volumeattach import ( "context" + "errors" "fmt" "net/http" "strings" @@ -248,8 +249,8 @@ func (r *volumeAttachResource) Read(ctx context.Context, req resource.ReadReques _, err := r.client.GetAttachedVolume(ctx, projectId, region, serverId, volumeId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -301,6 +302,10 @@ func (r *volumeAttachResource) Delete(ctx context.Context, req resource.DeleteRe // Remove volume from server err := r.client.RemoveVolumeFromServer(ctx, projectId, region, serverId, volumeId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error removing volume from server", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/kms/key/resource.go b/stackit/internal/services/kms/key/resource.go index 1e4c4ce65..f089e465d 100644 --- a/stackit/internal/services/kms/key/resource.go +++ b/stackit/internal/services/kms/key/resource.go @@ -315,6 +315,11 @@ func (r *keyResource) Read(ctx context.Context, req resource.ReadRequest, resp * keyRingId := model.KeyRingId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) keyId := model.KeyId.ValueString() + if keyId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "keyring_id", keyRingId) ctx = tflog.SetField(ctx, "project_id", projectId) @@ -324,8 +329,7 @@ func (r *keyResource) Read(ctx context.Context, req resource.ReadRequest, resp * keyResponse, err := r.client.DefaultAPI.GetKey(ctx, projectId, region, keyRingId, keyId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -370,7 +374,12 @@ func (r *keyResource) Delete(ctx context.Context, req resource.DeleteRequest, re err := r.client.DefaultAPI.DeleteKey(ctx, projectId, region, keyRingId, keyId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting key", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/kms/keyring/resource.go b/stackit/internal/services/kms/keyring/resource.go index d4867d9ef..432d50642 100644 --- a/stackit/internal/services/kms/keyring/resource.go +++ b/stackit/internal/services/kms/keyring/resource.go @@ -245,6 +245,11 @@ func (r *keyRingResource) Read(ctx context.Context, req resource.ReadRequest, re projectId := model.ProjectId.ValueString() keyRingId := model.KeyRingId.ValueString() + if keyRingId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "keyring_id", keyRingId) diff --git a/stackit/internal/services/kms/wrapping-key/resource.go b/stackit/internal/services/kms/wrapping-key/resource.go index 714e45b4c..f2d5bb301 100644 --- a/stackit/internal/services/kms/wrapping-key/resource.go +++ b/stackit/internal/services/kms/wrapping-key/resource.go @@ -322,6 +322,11 @@ func (r *wrappingKeyResource) Read(ctx context.Context, request resource.ReadReq keyRingId := model.KeyRingId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) wrappingKeyId := model.WrappingKeyId.ValueString() + if wrappingKeyId == "" { + // Resource not yet created; ID is unknown. + response.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "keyring_id", keyRingId) ctx = tflog.SetField(ctx, "project_id", projectId) @@ -331,8 +336,7 @@ func (r *wrappingKeyResource) Read(ctx context.Context, request resource.ReadReq wrappingKeyResponse, err := r.client.DefaultAPI.GetWrappingKey(ctx, projectId, region, keyRingId, wrappingKeyId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { response.State.RemoveResource(ctx) return } @@ -377,7 +381,12 @@ func (r *wrappingKeyResource) Delete(ctx context.Context, request resource.Delet err := r.client.DefaultAPI.DeleteWrappingKey(ctx, projectId, region, keyRingId, wrappingKeyId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &response.Diagnostics, "Error deleting wrapping key", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/loadbalancer/loadbalancer/resource.go b/stackit/internal/services/loadbalancer/loadbalancer/resource.go index e5d2ddd04..4952df138 100644 --- a/stackit/internal/services/loadbalancer/loadbalancer/resource.go +++ b/stackit/internal/services/loadbalancer/loadbalancer/resource.go @@ -2,6 +2,7 @@ package loadbalancer import ( "context" + "errors" "fmt" "net/http" "strings" @@ -830,8 +831,8 @@ func (r *loadBalancerResource) Read(ctx context.Context, req resource.ReadReques lbResp, err := r.client.DefaultAPI.GetLoadBalancer(ctx, projectId, region, name).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -935,6 +936,11 @@ func (r *loadBalancerResource) Delete(ctx context.Context, req resource.DeleteRe // Delete load balancer _, err := r.client.DefaultAPI.DeleteLoadBalancer(ctx, projectId, region, name).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting load balancer", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/loadbalancer/observability-credential/resource.go b/stackit/internal/services/loadbalancer/observability-credential/resource.go index 30b2ea599..e49f032fd 100644 --- a/stackit/internal/services/loadbalancer/observability-credential/resource.go +++ b/stackit/internal/services/loadbalancer/observability-credential/resource.go @@ -2,6 +2,7 @@ package loadbalancer import ( "context" + "errors" "fmt" "net/http" "strings" @@ -253,8 +254,8 @@ func (r *observabilityCredentialResource) Read(ctx context.Context, req resource // Get credentials credResp, err := r.client.DefaultAPI.GetCredentials(ctx, projectId, region, credentialsRef).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -306,6 +307,11 @@ func (r *observabilityCredentialResource) Delete(ctx context.Context, req resour // Delete credentials _, err := r.client.DefaultAPI.DeleteCredentials(ctx, projectId, region, credentialsRef).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting observability credential", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/logme/credential/resource.go b/stackit/internal/services/logme/credential/resource.go index c627d4a11..7fd0b5c19 100644 --- a/stackit/internal/services/logme/credential/resource.go +++ b/stackit/internal/services/logme/credential/resource.go @@ -2,6 +2,7 @@ package logme import ( "context" + "errors" "fmt" "net/http" "strings" @@ -223,14 +224,19 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() credentialId := model.CredentialId.ValueString() + if credentialId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "credential_id", credentialId) recordSetResp, err := r.client.DefaultAPI.GetCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -283,7 +289,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.DefaultAPI.DeleteCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/logme/instance/resource.go b/stackit/internal/services/logme/instance/resource.go index d31d71fb1..8d5c4cf38 100644 --- a/stackit/internal/services/logme/instance/resource.go +++ b/stackit/internal/services/logme/instance/resource.go @@ -2,6 +2,7 @@ package logme import ( "context" + "errors" "fmt" "math" "net/http" @@ -473,13 +474,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.DefaultAPI.GetInstance(ctx, projectId, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { resp.State.RemoveResource(ctx) return } @@ -600,6 +606,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/logs/accesstoken/resource.go b/stackit/internal/services/logs/accesstoken/resource.go index 864695b0f..ed06beee5 100644 --- a/stackit/internal/services/logs/accesstoken/resource.go +++ b/stackit/internal/services/logs/accesstoken/resource.go @@ -297,6 +297,11 @@ func (r *logsAccessTokenResource) Read(ctx context.Context, req resource.ReadReq region := r.providerData.GetRegionWithOverride(model.Region) instanceID := model.InstanceID.ValueString() accessTokenID := model.AccessTokenID.ValueString() + if accessTokenID == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectID) ctx = tflog.SetField(ctx, "region", region) diff --git a/stackit/internal/services/logs/instance/resource.go b/stackit/internal/services/logs/instance/resource.go index 5bf6bef90..581ec269f 100644 --- a/stackit/internal/services/logs/instance/resource.go +++ b/stackit/internal/services/logs/instance/resource.go @@ -296,6 +296,11 @@ func (r *logsInstanceResource) Read(ctx context.Context, req resource.ReadReques projectID := model.ProjectID.ValueString() region := model.Region.ValueString() instanceID := model.InstanceID.ValueString() + if instanceID == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectID) ctx = tflog.SetField(ctx, "region", region) diff --git a/stackit/internal/services/mariadb/credential/resource.go b/stackit/internal/services/mariadb/credential/resource.go index b954bf236..2cafdaf4f 100644 --- a/stackit/internal/services/mariadb/credential/resource.go +++ b/stackit/internal/services/mariadb/credential/resource.go @@ -2,6 +2,7 @@ package mariadb import ( "context" + "errors" "fmt" "net/http" "strings" @@ -230,14 +231,19 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() credentialId := model.CredentialId.ValueString() + if credentialId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "credential_id", credentialId) recordSetResp, err := r.client.DefaultAPI.GetCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -290,7 +296,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.DefaultAPI.DeleteCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/mariadb/instance/resource.go b/stackit/internal/services/mariadb/instance/resource.go index 56670680a..51f763938 100644 --- a/stackit/internal/services/mariadb/instance/resource.go +++ b/stackit/internal/services/mariadb/instance/resource.go @@ -2,6 +2,7 @@ package mariadb import ( "context" + "errors" "fmt" "net/http" "strings" @@ -370,13 +371,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.DefaultAPI.GetInstance(ctx, projectId, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { resp.State.RemoveResource(ctx) return } @@ -497,6 +503,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/modelserving/token/resource.go b/stackit/internal/services/modelserving/token/resource.go index 5e9ffe566..bfc51dbd5 100644 --- a/stackit/internal/services/modelserving/token/resource.go +++ b/stackit/internal/services/modelserving/token/resource.go @@ -355,6 +355,11 @@ func (r *tokenResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() tokenId := model.TokenId.ValueString() + if tokenId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) diff --git a/stackit/internal/services/mongodbflex/instance/resource.go b/stackit/internal/services/mongodbflex/instance/resource.go index 65d1bffd2..f52a4dfa3 100644 --- a/stackit/internal/services/mongodbflex/instance/resource.go +++ b/stackit/internal/services/mongodbflex/instance/resource.go @@ -2,6 +2,7 @@ package mongodbflex import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -498,6 +499,11 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() region := r.providerData.GetRegionWithOverride(model.Region) instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -530,8 +536,8 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r instanceResp, err := r.client.DefaultAPI.GetInstance(ctx, projectId, instanceId, region).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -689,6 +695,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId, region).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/mongodbflex/user/resource.go b/stackit/internal/services/mongodbflex/user/resource.go index 92e15f977..6b4af3962 100644 --- a/stackit/internal/services/mongodbflex/user/resource.go +++ b/stackit/internal/services/mongodbflex/user/resource.go @@ -2,6 +2,7 @@ package mongodbflex import ( "context" + "errors" "fmt" "net/http" "strings" @@ -301,6 +302,11 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp region := r.providerData.GetRegionWithOverride(model.Region) instanceId := model.InstanceId.ValueString() userId := model.UserId.ValueString() + if userId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -308,8 +314,8 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp recordSetResp, err := r.client.DefaultAPI.GetUser(ctx, projectId, instanceId, userId, region).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -435,6 +441,10 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r // Delete user err := r.client.DefaultAPI.DeleteUser(ctx, projectId, instanceId, userId, region).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/objectstorage/bucket/resource.go b/stackit/internal/services/objectstorage/bucket/resource.go index 143366b16..7aaed490f 100644 --- a/stackit/internal/services/objectstorage/bucket/resource.go +++ b/stackit/internal/services/objectstorage/bucket/resource.go @@ -270,8 +270,8 @@ func (r *bucketResource) Read(ctx context.Context, req resource.ReadRequest, res bucketResp, err := r.client.DefaultAPI.GetBucket(ctx, projectId, region, bucketName).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -327,6 +327,10 @@ func (r *bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, if err != nil { var oapiErr *oapierror.GenericOpenAPIError if errors.As(err, &oapiErr) { + if oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } if oapiErr.StatusCode == http.StatusUnprocessableEntity { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting bucket", "Bucket isn't empty and cannot be deleted") return diff --git a/stackit/internal/services/objectstorage/credential/resource.go b/stackit/internal/services/objectstorage/credential/resource.go index 6b86c16b6..5026e2413 100644 --- a/stackit/internal/services/objectstorage/credential/resource.go +++ b/stackit/internal/services/objectstorage/credential/resource.go @@ -2,6 +2,7 @@ package objectstorage import ( "context" + "errors" "fmt" "net/http" "strings" @@ -343,6 +344,11 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() credentialsGroupId := model.CredentialsGroupId.ValueString() credentialId := model.CredentialId.ValueString() + if credentialId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) @@ -431,7 +437,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing credential _, err := r.client.DefaultAPI.DeleteAccessKey(ctx, projectId, region, credentialId).CredentialsGroup(credentialsGroupId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) @@ -544,8 +555,8 @@ func readCredentials(ctx context.Context, model *Model, region string, client *o credentialsGroupResp, err := client.DefaultAPI.ListAccessKeys(ctx, projectId, region).CredentialsGroup(credentialsGroupId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { return false, nil } return false, fmt.Errorf("getting credentials groups: %w", err) diff --git a/stackit/internal/services/objectstorage/credentialsgroup/resource.go b/stackit/internal/services/objectstorage/credentialsgroup/resource.go index db5036f22..e0c34f284 100644 --- a/stackit/internal/services/objectstorage/credentialsgroup/resource.go +++ b/stackit/internal/services/objectstorage/credentialsgroup/resource.go @@ -2,6 +2,7 @@ package objectstorage import ( "context" + "errors" "fmt" "net/http" "strings" @@ -248,6 +249,11 @@ func (r *credentialsGroupResource) Read(ctx context.Context, req resource.ReadRe projectId := model.ProjectId.ValueString() credentialsGroupId := model.CredentialsGroupId.ValueString() + if credentialsGroupId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) @@ -306,7 +312,13 @@ func (r *credentialsGroupResource) Delete(ctx context.Context, req resource.Dele // Delete existing credentials group _, err := r.client.DefaultAPI.DeleteCredentialsGroup(ctx, projectId, region, credentialsGroupId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credentials group", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) @@ -392,8 +404,8 @@ func readCredentialsGroups(ctx context.Context, model *Model, region string, cli credentialsGroupsResp, err := client.ListCredentialsGroups(ctx, model.ProjectId.ValueString(), region).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { return found, nil } return found, fmt.Errorf("getting credentials groups: %w", err) diff --git a/stackit/internal/services/observability/alertgroup/resource.go b/stackit/internal/services/observability/alertgroup/resource.go index 93adf44c4..f4f18b27f 100644 --- a/stackit/internal/services/observability/alertgroup/resource.go +++ b/stackit/internal/services/observability/alertgroup/resource.go @@ -367,8 +367,7 @@ func (a *alertGroupResource) Read(ctx context.Context, req resource.ReadRequest, readAlertGroupResp, err := a.client.GetAlertgroup(ctx, alertGroupName, instanceId, projectId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -418,6 +417,11 @@ func (a *alertGroupResource) Delete(ctx context.Context, req resource.DeleteRequ _, err := a.client.DeleteAlertgroup(ctx, alertGroupName, instanceId, projectId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting alert group", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/observability/credential/resource.go b/stackit/internal/services/observability/credential/resource.go index 4ee796ece..3a2f33662 100644 --- a/stackit/internal/services/observability/credential/resource.go +++ b/stackit/internal/services/observability/credential/resource.go @@ -2,6 +2,7 @@ package observability import ( "context" + "errors" "fmt" "net/http" @@ -16,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/services/observability" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -220,19 +222,28 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() userName := model.Username.ValueString() + if userName == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } _, err := r.client.GetCredentials(ctx, instanceId, projectId, userName).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } utils.LogError( ctx, &resp.Diagnostics, err, "Reading credential", - fmt.Sprintf("Credential with username %q or instance with ID %q does not exist in project %q.", userName, instanceId, projectId), + fmt.Sprintf("Error reading credential with username %q for instance %q in project %q.", userName, instanceId, projectId), map[int]string{ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId), }, ) - resp.State.RemoveResource(ctx) return } @@ -268,6 +279,10 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ userName := model.Username.ValueString() _, err := r.client.DeleteCredentials(ctx, instanceId, projectId, userName).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/observability/instance/resource.go b/stackit/internal/services/observability/instance/resource.go index e1f20c5d2..ee69d7220 100644 --- a/stackit/internal/services/observability/instance/resource.go +++ b/stackit/internal/services/observability/instance/resource.go @@ -2,6 +2,7 @@ package observability import ( "context" + "errors" "fmt" "net/http" "strconv" @@ -1149,13 +1150,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.GetInstance(ctx, instanceId, projectId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -1507,6 +1513,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance _, err := r.client.DeleteInstance(ctx, instanceId, projectId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/observability/log-alertgroup/resource.go b/stackit/internal/services/observability/log-alertgroup/resource.go index 310c2d2b8..d60fa9fc3 100644 --- a/stackit/internal/services/observability/log-alertgroup/resource.go +++ b/stackit/internal/services/observability/log-alertgroup/resource.go @@ -315,8 +315,7 @@ func (l *logAlertGroupResource) Read(ctx context.Context, req resource.ReadReque readAlertGroupResp, err := l.client.GetLogsAlertgroup(ctx, alertGroupName, instanceId, projectId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -366,6 +365,11 @@ func (l *logAlertGroupResource) Delete(ctx context.Context, req resource.DeleteR _, err := l.client.DeleteLogsAlertgroup(ctx, alertGroupName, instanceId, projectId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting log alert group", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/observability/scrapeconfig/resource.go b/stackit/internal/services/observability/scrapeconfig/resource.go index 25d6cb3b9..05a7cf9e5 100644 --- a/stackit/internal/services/observability/scrapeconfig/resource.go +++ b/stackit/internal/services/observability/scrapeconfig/resource.go @@ -2,6 +2,7 @@ package observability import ( "context" + "errors" "fmt" "net/http" "strings" @@ -404,8 +405,8 @@ func (r *scrapeConfigResource) Read(ctx context.Context, req resource.ReadReques scResp, err := r.client.GetScrapeConfig(ctx, instanceId, scName, projectId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -528,6 +529,11 @@ func (r *scrapeConfigResource) Delete(ctx context.Context, req resource.DeleteRe // Delete existing ScrapeConfig _, err := r.client.DeleteScrapeConfig(ctx, instanceId, scName, projectId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting scrape config", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/opensearch/credential/resource.go b/stackit/internal/services/opensearch/credential/resource.go index 8634d9139..ad3c62f4a 100644 --- a/stackit/internal/services/opensearch/credential/resource.go +++ b/stackit/internal/services/opensearch/credential/resource.go @@ -2,6 +2,7 @@ package opensearch import ( "context" + "errors" "fmt" "net/http" "strings" @@ -226,14 +227,19 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() credentialId := model.CredentialId.ValueString() + if credentialId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "credential_id", credentialId) recordSetResp, err := r.client.DefaultAPI.GetCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -286,7 +292,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.DefaultAPI.DeleteCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/opensearch/instance/resource.go b/stackit/internal/services/opensearch/instance/resource.go index 537eed4e1..0c139a73d 100644 --- a/stackit/internal/services/opensearch/instance/resource.go +++ b/stackit/internal/services/opensearch/instance/resource.go @@ -2,6 +2,7 @@ package opensearch import ( "context" + "errors" "fmt" "net/http" "slices" @@ -408,13 +409,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.DefaultAPI.GetInstance(ctx, projectId, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { resp.State.RemoveResource(ctx) return } @@ -534,6 +540,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/postgresflex/database/resource.go b/stackit/internal/services/postgresflex/database/resource.go index 3d958f855..2a36fc01d 100644 --- a/stackit/internal/services/postgresflex/database/resource.go +++ b/stackit/internal/services/postgresflex/database/resource.go @@ -265,6 +265,11 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() databaseId := model.DatabaseId.ValueString() + if databaseId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -273,8 +278,8 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r databaseResp, err := getDatabase(ctx, r.client, projectId, region, instanceId, databaseId) if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if (ok && oapiErr.StatusCode == http.StatusNotFound) || errors.Is(err, errDatabaseNotFound) { + var oapiErr *oapierror.GenericOpenAPIError + if (errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound) || errors.Is(err, errDatabaseNotFound) { resp.State.RemoveResource(ctx) return } @@ -330,7 +335,12 @@ func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing record set err := r.client.DeleteDatabase(ctx, projectId, region, instanceId, databaseId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting database", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/postgresflex/instance/resource.go b/stackit/internal/services/postgresflex/instance/resource.go index 347fbb412..2fc18a653 100644 --- a/stackit/internal/services/postgresflex/instance/resource.go +++ b/stackit/internal/services/postgresflex/instance/resource.go @@ -2,6 +2,7 @@ package postgresflex import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -383,6 +384,11 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -407,8 +413,8 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r instanceResp, err := r.client.GetInstance(ctx, projectId, region, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -544,6 +550,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DeleteInstance(ctx, projectId, region, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/postgresflex/user/resource.go b/stackit/internal/services/postgresflex/user/resource.go index 61d3bad73..425211ea0 100644 --- a/stackit/internal/services/postgresflex/user/resource.go +++ b/stackit/internal/services/postgresflex/user/resource.go @@ -2,6 +2,7 @@ package postgresflex import ( "context" + "errors" "fmt" "net/http" "strings" @@ -284,6 +285,11 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() userId := model.UserId.ValueString() + if userId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -292,8 +298,8 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp recordSetResp, err := r.client.GetUser(ctx, projectId, region, instanceId, userId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -419,7 +425,12 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r // Delete existing record set err := r.client.DeleteUser(ctx, projectId, region, instanceId, userId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/rabbitmq/credential/resource.go b/stackit/internal/services/rabbitmq/credential/resource.go index c4d2c78d8..f75591eb7 100644 --- a/stackit/internal/services/rabbitmq/credential/resource.go +++ b/stackit/internal/services/rabbitmq/credential/resource.go @@ -2,6 +2,7 @@ package rabbitmq import ( "context" + "errors" "fmt" "net/http" "strings" @@ -247,14 +248,19 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() credentialId := model.CredentialId.ValueString() + if credentialId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "credential_id", credentialId) recordSetResp, err := r.client.DefaultAPI.GetCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -307,7 +313,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.DefaultAPI.DeleteCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/rabbitmq/instance/resource.go b/stackit/internal/services/rabbitmq/instance/resource.go index 26ecdef58..458f699a9 100644 --- a/stackit/internal/services/rabbitmq/instance/resource.go +++ b/stackit/internal/services/rabbitmq/instance/resource.go @@ -2,6 +2,7 @@ package rabbitmq import ( "context" + "errors" "fmt" "net/http" "strings" @@ -412,13 +413,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.DefaultAPI.GetInstance(ctx, projectId, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { resp.State.RemoveResource(ctx) return } @@ -539,6 +545,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/redis/credential/resource.go b/stackit/internal/services/redis/credential/resource.go index 41cc62b51..3712c5b1c 100644 --- a/stackit/internal/services/redis/credential/resource.go +++ b/stackit/internal/services/redis/credential/resource.go @@ -2,6 +2,7 @@ package redis import ( "context" + "errors" "fmt" "net/http" "strings" @@ -236,14 +237,19 @@ func (r *credentialResource) Read(ctx context.Context, req resource.ReadRequest, projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() credentialId := model.CredentialId.ValueString() + if credentialId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "credential_id", credentialId) recordSetResp, err := r.client.DefaultAPI.GetCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -296,7 +302,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.DefaultAPI.DeleteCredentials(ctx, projectId, instanceId, credentialId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/redis/instance/resource.go b/stackit/internal/services/redis/instance/resource.go index 293bbba91..710ea9c27 100644 --- a/stackit/internal/services/redis/instance/resource.go +++ b/stackit/internal/services/redis/instance/resource.go @@ -2,6 +2,7 @@ package redis import ( "context" + "errors" "fmt" "net/http" "slices" @@ -481,13 +482,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.DefaultAPI.GetInstance(ctx, projectId, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusGone) { resp.State.RemoveResource(ctx) return } @@ -608,6 +614,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DefaultAPI.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/resourcemanager/folder/resource.go b/stackit/internal/services/resourcemanager/folder/resource.go index 7e328124e..f1109e451 100644 --- a/stackit/internal/services/resourcemanager/folder/resource.go +++ b/stackit/internal/services/resourcemanager/folder/resource.go @@ -2,6 +2,7 @@ package folder import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -259,14 +260,19 @@ func (r *folderResource) Read(ctx context.Context, req resource.ReadRequest, res ctx = core.InitProviderContext(ctx) containerId := model.ContainerId.ValueString() + if containerId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } folderName := model.Name.ValueString() ctx = tflog.SetField(ctx, "folder_name", folderName) ctx = tflog.SetField(ctx, "container_id", containerId) folderResp, err := r.client.DefaultAPI.GetFolderDetails(ctx, containerId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusForbidden { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusForbidden { resp.State.RemoveResource(ctx) return } @@ -360,6 +366,11 @@ func (r *folderResource) Delete(ctx context.Context, req resource.DeleteRequest, // Delete existing folder err := r.client.DefaultAPI.DeleteFolder(ctx, containerId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError( ctx, &resp.Diagnostics, diff --git a/stackit/internal/services/resourcemanager/project/resource.go b/stackit/internal/services/resourcemanager/project/resource.go index 9f4be845e..a7c0ead2e 100644 --- a/stackit/internal/services/resourcemanager/project/resource.go +++ b/stackit/internal/services/resourcemanager/project/resource.go @@ -2,6 +2,7 @@ package project import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -260,12 +261,17 @@ func (r *projectResource) Read(ctx context.Context, req resource.ReadRequest, re ctx = core.InitProviderContext(ctx) containerId := model.ContainerId.ValueString() + if containerId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "container_id", containerId) projectResp, err := r.client.DefaultAPI.GetProject(ctx, containerId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusForbidden { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusForbidden { resp.State.RemoveResource(ctx) return } @@ -359,6 +365,11 @@ func (r *projectResource) Delete(ctx context.Context, req resource.DeleteRequest // Delete existing project err := r.client.DefaultAPI.DeleteProject(ctx, containerId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting project", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/scf/organization/resource.go b/stackit/internal/services/scf/organization/resource.go index 15c4dc3a1..ad044d128 100644 --- a/stackit/internal/services/scf/organization/resource.go +++ b/stackit/internal/services/scf/organization/resource.go @@ -333,6 +333,11 @@ func (s *scfOrganizationResource) Read(ctx context.Context, request resource.Rea // Extract the project ID and instance id of the model projectId := model.ProjectId.ValueString() orgId := model.OrgId.ValueString() + if orgId == "" { + // Resource not yet created; ID is unknown. + response.State.RemoveResource(ctx) + return + } // Extract the region region := s.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) diff --git a/stackit/internal/services/scf/organizationmanager/resource.go b/stackit/internal/services/scf/organizationmanager/resource.go index ad8e9ae14..925b9ebc0 100644 --- a/stackit/internal/services/scf/organizationmanager/resource.go +++ b/stackit/internal/services/scf/organizationmanager/resource.go @@ -287,8 +287,7 @@ func (s *scfOrganizationManagerResource) Read(ctx context.Context, request resou scfOrgManager, err := s.client.GetOrgManagerExecute(ctx, projectId, region, orgId) if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusNotFound { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { core.LogAndAddWarning(ctx, &response.Diagnostics, "SCF Organization manager not found", "SCF Organization manager not found, remove from state") response.State.RemoveResource(ctx) return @@ -338,8 +337,7 @@ func (s *scfOrganizationManagerResource) Delete(ctx context.Context, request res _, err := s.client.DeleteOrgManagerExecute(ctx, projectId, region, orgId) if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) - if ok && oapiErr.StatusCode == http.StatusGone { + if errors.As(err, &oapiErr) && (oapiErr.StatusCode == http.StatusGone || oapiErr.StatusCode == http.StatusNotFound) { tflog.Info(ctx, "Scf organization manager was already deleted") return } diff --git a/stackit/internal/services/secretsmanager/instance/resource.go b/stackit/internal/services/secretsmanager/instance/resource.go index dbf1b07d4..ac9372874 100644 --- a/stackit/internal/services/secretsmanager/instance/resource.go +++ b/stackit/internal/services/secretsmanager/instance/resource.go @@ -2,6 +2,7 @@ package secretsmanager import ( "context" + "errors" "fmt" "net/http" "strings" @@ -263,13 +264,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -397,6 +403,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DeleteInstance(ctx, projectId, instanceId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/secretsmanager/user/resource.go b/stackit/internal/services/secretsmanager/user/resource.go index 976396f6c..9b7de8ce3 100644 --- a/stackit/internal/services/secretsmanager/user/resource.go +++ b/stackit/internal/services/secretsmanager/user/resource.go @@ -2,6 +2,7 @@ package secretsmanager import ( "context" + "errors" "fmt" "net/http" "strings" @@ -225,14 +226,19 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() userId := model.UserId.ValueString() + if userId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "user_id", userId) userResp, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -339,7 +345,12 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r // Delete existing user err := r.client.DeleteUser(ctx, projectId, instanceId, userId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) + return } ctx = core.LogResponse(ctx) diff --git a/stackit/internal/services/serverbackup/schedule/resource.go b/stackit/internal/services/serverbackup/schedule/resource.go index 0d497a94a..0547dfa7a 100644 --- a/stackit/internal/services/serverbackup/schedule/resource.go +++ b/stackit/internal/services/serverbackup/schedule/resource.go @@ -2,6 +2,7 @@ package schedule import ( "context" + "errors" "fmt" "net/http" "strconv" @@ -306,6 +307,11 @@ func (r *scheduleResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() serverId := model.ServerId.ValueString() backupScheduleId := model.BackupScheduleId.ValueInt64() + if backupScheduleId == 0 { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) @@ -315,8 +321,8 @@ func (r *scheduleResource) Read(ctx context.Context, req resource.ReadRequest, r scheduleResp, err := r.client.GetBackupSchedule(ctx, projectId, serverId, region, strconv.FormatInt(backupScheduleId, 10)).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } diff --git a/stackit/internal/services/serverupdate/schedule/resource.go b/stackit/internal/services/serverupdate/schedule/resource.go index 1666b7a9a..041f7beea 100644 --- a/stackit/internal/services/serverupdate/schedule/resource.go +++ b/stackit/internal/services/serverupdate/schedule/resource.go @@ -2,6 +2,7 @@ package schedule import ( "context" + "errors" "fmt" "net/http" "strconv" @@ -282,6 +283,11 @@ func (r *scheduleResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() serverId := model.ServerId.ValueString() updateScheduleId := model.UpdateScheduleId.ValueInt64() + if updateScheduleId == 0 { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "server_id", serverId) @@ -290,8 +296,8 @@ func (r *scheduleResource) Read(ctx context.Context, req resource.ReadRequest, r scheduleResp, err := r.client.GetUpdateSchedule(ctx, projectId, serverId, strconv.FormatInt(updateScheduleId, 10), region).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } diff --git a/stackit/internal/services/serviceaccount/account/resource.go b/stackit/internal/services/serviceaccount/account/resource.go index 08bd28ffb..97b5da5af 100644 --- a/stackit/internal/services/serviceaccount/account/resource.go +++ b/stackit/internal/services/serviceaccount/account/resource.go @@ -2,7 +2,9 @@ package account import ( "context" + "errors" "fmt" + "net/http" "regexp" "strings" "time" @@ -20,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/services/serviceaccount" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" @@ -200,6 +203,11 @@ func (r *serviceAccountResource) Read(ctx context.Context, req resource.ReadRequ // Fetch the list of service accounts from the API. listSaResp, err := r.client.ListServiceAccounts(ctx, projectId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading service account", fmt.Sprintf("Error calling API: %v", err)) return } @@ -257,6 +265,11 @@ func (r *serviceAccountResource) Delete(ctx context.Context, req resource.Delete // Call API to delete the existing service account. err := r.client.DeleteServiceAccount(ctx, projectId, serviceAccountEmail).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting service account", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/serviceaccount/key/resource.go b/stackit/internal/services/serviceaccount/key/resource.go index de6983df8..dafc1ad14 100644 --- a/stackit/internal/services/serviceaccount/key/resource.go +++ b/stackit/internal/services/serviceaccount/key/resource.go @@ -223,13 +223,17 @@ func (r *serviceAccountKeyResource) Read(ctx context.Context, req resource.ReadR projectId := model.ProjectId.ValueString() serviceAccountEmail := model.ServiceAccountEmail.ValueString() keyId := model.KeyId.ValueString() + if keyId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } _, err := r.client.GetServiceAccountKey(ctx, projectId, serviceAccountEmail, keyId).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError - ok := errors.As(err, &oapiErr) // due to security purposes, attempting to get access key for a non-existent Service Account will return 403. - if ok && oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden || oapiErr.StatusCode == http.StatusBadRequest { + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden || oapiErr.StatusCode == http.StatusBadRequest { resp.State.RemoveResource(ctx) return } @@ -281,6 +285,10 @@ func (r *serviceAccountKeyResource) Delete(ctx context.Context, req resource.Del // Call API to delete the existing service account key. err := r.client.DeleteServiceAccountKey(ctx, projectId, serviceAccountEmail, keyId).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting service account key", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/sfs/export-policy/resource.go b/stackit/internal/services/sfs/export-policy/resource.go index b101cf7fe..d37fc891e 100644 --- a/stackit/internal/services/sfs/export-policy/resource.go +++ b/stackit/internal/services/sfs/export-policy/resource.go @@ -329,6 +329,11 @@ func (r *exportPolicyResource) Read(ctx context.Context, req resource.ReadReques } projectId := model.ProjectId.ValueString() exportPolicyId := model.ExportPolicyId.ValueString() + if exportPolicyId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "policy_id", exportPolicyId) diff --git a/stackit/internal/services/sfs/resourcepool/resource.go b/stackit/internal/services/sfs/resourcepool/resource.go index 6a98f834e..fc9699260 100644 --- a/stackit/internal/services/sfs/resourcepool/resource.go +++ b/stackit/internal/services/sfs/resourcepool/resource.go @@ -300,6 +300,11 @@ func (r *resourcePoolResource) Read(ctx context.Context, req resource.ReadReques } projectId := model.ProjectId.ValueString() resourcePoolId := model.ResourcePoolId.ValueString() + if resourcePoolId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "resource_pool_id", resourcePoolId) diff --git a/stackit/internal/services/sfs/share/resource.go b/stackit/internal/services/sfs/share/resource.go index 7f3b077b1..65b49153e 100644 --- a/stackit/internal/services/sfs/share/resource.go +++ b/stackit/internal/services/sfs/share/resource.go @@ -299,6 +299,11 @@ func (r *shareResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() resourcePoolId := model.ResourcePoolId.ValueString() shareId := model.ShareId.ValueString() + if shareId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "resource_pool_id", resourcePoolId) diff --git a/stackit/internal/services/ske/cluster/resource.go b/stackit/internal/services/ske/cluster/resource.go index ae96243c1..9653f03cd 100644 --- a/stackit/internal/services/ske/cluster/resource.go +++ b/stackit/internal/services/ske/cluster/resource.go @@ -2,6 +2,7 @@ package ske import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -2191,8 +2192,8 @@ func (r *clusterResource) Read(ctx context.Context, req resource.ReadRequest, re clResp, err := r.skeClient.GetCluster(ctx, projectId, region, name).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -2273,6 +2274,11 @@ func (r *clusterResource) Delete(ctx context.Context, req resource.DeleteRequest c := r.skeClient _, err := c.DeleteCluster(ctx, projectId, region, name).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting cluster", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/ske/kubeconfig/resource.go b/stackit/internal/services/ske/kubeconfig/resource.go index d7c0907e0..8f67c3c62 100644 --- a/stackit/internal/services/ske/kubeconfig/resource.go +++ b/stackit/internal/services/ske/kubeconfig/resource.go @@ -2,6 +2,7 @@ package ske import ( "context" + "errors" "fmt" "net/http" "strconv" @@ -28,6 +29,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/services/ske" ) @@ -314,17 +316,21 @@ func (r *kubeconfigResource) Read(ctx context.Context, req resource.ReadRequest, cluster, err := r.client.GetClusterExecute(ctx, projectId, region, clusterName) if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } utils.LogError( ctx, &resp.Diagnostics, err, "Reading kubeconfig", - fmt.Sprintf("Kubeconfig with ID %q or cluster with name %q does not exist in project %q.", kubeconfigUUID, clusterName, projectId), + fmt.Sprintf("Error reading kubeconfig with ID %q for cluster %q in project %q.", kubeconfigUUID, clusterName, projectId), map[int]string{ http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId), }, ) - resp.State.RemoveResource(ctx) return } diff --git a/stackit/internal/services/sqlserverflex/instance/resource.go b/stackit/internal/services/sqlserverflex/instance/resource.go index bac1f4a58..aa31c9f6b 100644 --- a/stackit/internal/services/sqlserverflex/instance/resource.go +++ b/stackit/internal/services/sqlserverflex/instance/resource.go @@ -2,6 +2,7 @@ package sqlserverflex import ( "context" + "errors" "fmt" "net/http" "regexp" @@ -464,6 +465,11 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() + if instanceId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) @@ -498,8 +504,8 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId, region).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -640,6 +646,11 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques // Delete existing instance err := r.client.DeleteInstance(ctx, projectId, instanceId, region).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err)) return } diff --git a/stackit/internal/services/sqlserverflex/user/resource.go b/stackit/internal/services/sqlserverflex/user/resource.go index 3dd83180f..d35eb4313 100644 --- a/stackit/internal/services/sqlserverflex/user/resource.go +++ b/stackit/internal/services/sqlserverflex/user/resource.go @@ -2,6 +2,7 @@ package sqlserverflex import ( "context" + "errors" "fmt" "net/http" "strings" @@ -290,6 +291,11 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp projectId := model.ProjectId.ValueString() instanceId := model.InstanceId.ValueString() userId := model.UserId.ValueString() + if userId == "" { + // Resource not yet created; ID is unknown. + resp.State.RemoveResource(ctx) + return + } region := r.providerData.GetRegionWithOverride(model.Region) ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) @@ -298,8 +304,8 @@ func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp recordSetResp, err := r.client.GetUser(ctx, projectId, instanceId, userId, region).Execute() if err != nil { - oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped - if ok && oapiErr.StatusCode == http.StatusNotFound { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { resp.State.RemoveResource(ctx) return } @@ -355,6 +361,10 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r // Delete existing record set err := r.client.DeleteUser(ctx, projectId, instanceId, userId, region).Execute() if err != nil { + var oapiErr *oapierror.GenericOpenAPIError + if errors.As(err, &oapiErr) && oapiErr.StatusCode == http.StatusNotFound { + return + } core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err)) return }