diff --git a/stackit/internal/services/alb/applicationloadbalancer/resource.go b/stackit/internal/services/alb/applicationloadbalancer/resource.go index 85deb2a93..632c2388f 100644 --- a/stackit/internal/services/alb/applicationloadbalancer/resource.go +++ b/stackit/internal/services/alb/applicationloadbalancer/resource.go @@ -1343,6 +1343,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 fde1618e3..60447c369 100644 --- a/stackit/internal/services/authorization/customrole/resource.go +++ b/stackit/internal/services/authorization/customrole/resource.go @@ -212,7 +212,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 76afe20b4..72c7617b4 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" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -315,6 +317,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 ed8de254e..636e11152 100644 --- a/stackit/internal/services/cdn/customdomain/resource.go +++ b/stackit/internal/services/cdn/customdomain/resource.go @@ -371,7 +371,13 @@ func (r *customDomainResource) Delete(ctx context.Context, req resource.DeleteRe _, err := r.client.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 17b0ce0ca..9dd9abede 100644 --- a/stackit/internal/services/cdn/distribution/resource.go +++ b/stackit/internal/services/cdn/distribution/resource.go @@ -480,6 +480,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 ad665a3a7..4efd8ce73 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-validators/int32validator" @@ -16,6 +18,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" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" @@ -267,12 +270,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 } @@ -374,7 +387,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 fcd14c4d5..c2b79b664 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" @@ -22,6 +24,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" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" @@ -355,11 +358,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 } @@ -457,6 +470,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 10420606e..ce0d5f4f1 100644 --- a/stackit/internal/services/edgecloud/instance/resource.go +++ b/stackit/internal/services/edgecloud/instance/resource.go @@ -325,6 +325,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 ef312fe35..d4bc074fc 100644 --- a/stackit/internal/services/git/instance/resource.go +++ b/stackit/internal/services/git/instance/resource.go @@ -265,6 +265,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() 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 89c6d620c..946470f62 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" @@ -509,6 +510,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) @@ -518,8 +524,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 } @@ -622,6 +628,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 4c709b33d..f362be7f0 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" @@ -199,8 +200,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 } @@ -295,6 +296,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 06476e5c6..3f926ee1e 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" @@ -455,6 +456,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) @@ -462,8 +468,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 } @@ -569,6 +575,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 6a3384c8b..71cfb610a 100644 --- a/stackit/internal/services/iaas/networkarea/resource.go +++ b/stackit/internal/services/iaas/networkarea/resource.go @@ -442,6 +442,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) @@ -451,8 +456,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 } @@ -477,8 +481,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 } @@ -604,8 +607,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)) @@ -655,6 +657,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 } @@ -662,6 +669,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 } @@ -670,6 +682,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 } @@ -684,6 +700,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 b8055aee4..37beadba6 100644 --- a/stackit/internal/services/iaas/networkarearegion/resource.go +++ b/stackit/internal/services/iaas/networkarearegion/resource.go @@ -335,8 +335,7 @@ 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 } @@ -452,6 +451,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 41d0b9d7e..03f6af1eb 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" @@ -404,6 +405,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) @@ -414,8 +420,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 } @@ -465,6 +471,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 a050174e4..d9cb473bc 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" @@ -336,6 +337,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) @@ -346,8 +352,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 } @@ -454,6 +460,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 aa06b50a5..a2c57a641 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" @@ -225,8 +226,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 } @@ -295,6 +296,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 0d69eab1d..b93a8a9fa 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" @@ -239,6 +240,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) @@ -248,8 +254,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 } @@ -352,6 +358,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 6d13bc1fd..90784c895 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" @@ -254,8 +255,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 } @@ -313,6 +314,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 3f5026d48..a3a05ee81 100644 --- a/stackit/internal/services/iaas/routingtable/route/resource.go +++ b/stackit/internal/services/iaas/routingtable/route/resource.go @@ -300,6 +300,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) diff --git a/stackit/internal/services/iaas/routingtable/table/resource.go b/stackit/internal/services/iaas/routingtable/table/resource.go index f891a3839..4e623ef05 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" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" @@ -283,6 +285,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) @@ -408,6 +415,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 07554fe3a..da09f5b7b 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" @@ -258,6 +259,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) @@ -267,8 +273,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 } @@ -371,6 +377,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 866580e73..de2061cdd 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" @@ -517,6 +518,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) @@ -527,8 +533,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 } @@ -584,6 +590,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 9f368f610..76f89ee6f 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" @@ -670,6 +671,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) @@ -681,8 +687,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 } @@ -856,6 +862,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 bf6d31e91..7059a3568 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" @@ -222,8 +223,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 } @@ -290,6 +291,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 6e01157c5..b226cc46c 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" @@ -512,6 +513,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) @@ -521,8 +527,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 } @@ -643,6 +649,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 484a0fec1..68806b884 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" @@ -247,8 +248,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 } @@ -300,6 +301,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 41707dc7d..5cb1a92fb 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.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.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 6b2f8e9aa..df1d91884 100644 --- a/stackit/internal/services/kms/keyring/resource.go +++ b/stackit/internal/services/kms/keyring/resource.go @@ -244,6 +244,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 a1501c8ce..d5323be41 100644 --- a/stackit/internal/services/kms/wrapping-key/resource.go +++ b/stackit/internal/services/kms/wrapping-key/resource.go @@ -321,6 +321,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) @@ -330,8 +335,7 @@ func (r *wrappingKeyResource) Read(ctx context.Context, request resource.ReadReq wrappingKeyResponse, err := r.client.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 } @@ -376,7 +380,12 @@ func (r *wrappingKeyResource) Delete(ctx context.Context, request resource.Delet err := r.client.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 7e6ad45c6..5d0701c15 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.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 } @@ -950,6 +951,11 @@ func (r *loadBalancerResource) Delete(ctx context.Context, req resource.DeleteRe // Delete load balancer _, err := r.client.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 e4613c062..2048a22f8 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" @@ -252,8 +253,8 @@ func (r *observabilityCredentialResource) Read(ctx context.Context, req resource // Get credentials credResp, err := r.client.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 } @@ -305,6 +306,11 @@ func (r *observabilityCredentialResource) Delete(ctx context.Context, req resour // Delete credentials _, err := r.client.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 a5b1372a3..0bcc856c2 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" @@ -225,14 +226,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.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 } @@ -285,7 +291,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.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 f4571f98f..2eb01d3f5 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" "net/http" "slices" @@ -470,13 +471,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 || 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 } @@ -597,6 +603,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/logs/accesstoken/resource.go b/stackit/internal/services/logs/accesstoken/resource.go index a2f2e827f..14b5995f3 100644 --- a/stackit/internal/services/logs/accesstoken/resource.go +++ b/stackit/internal/services/logs/accesstoken/resource.go @@ -296,6 +296,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 3204fde03..993219f05 100644 --- a/stackit/internal/services/logs/instance/resource.go +++ b/stackit/internal/services/logs/instance/resource.go @@ -295,6 +295,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 531ea9e0a..bd3f2d92a 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" @@ -233,14 +234,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.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 } @@ -293,7 +299,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.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 42f6e46cd..aa4731704 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" @@ -369,13 +370,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 || 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 } @@ -496,6 +502,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/modelserving/token/resource.go b/stackit/internal/services/modelserving/token/resource.go index 23531bf99..4d5f42e54 100644 --- a/stackit/internal/services/modelserving/token/resource.go +++ b/stackit/internal/services/modelserving/token/resource.go @@ -354,6 +354,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 b74f8e912..132968def 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" @@ -492,6 +493,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) @@ -524,8 +530,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 } @@ -683,6 +689,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/mongodbflex/user/resource.go b/stackit/internal/services/mongodbflex/user/resource.go index 7c11d2d2d..ae924b019 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" @@ -300,6 +301,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) @@ -307,8 +313,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 } @@ -434,6 +440,10 @@ func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, r // Delete user 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 } diff --git a/stackit/internal/services/objectstorage/bucket/resource.go b/stackit/internal/services/objectstorage/bucket/resource.go index 1711c55cd..8337221c0 100644 --- a/stackit/internal/services/objectstorage/bucket/resource.go +++ b/stackit/internal/services/objectstorage/bucket/resource.go @@ -268,8 +268,8 @@ func (r *bucketResource) Read(ctx context.Context, req resource.ReadRequest, res bucketResp, err := r.client.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 } @@ -325,6 +325,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 c12e120e1..8db2a2a8f 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" @@ -346,6 +347,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) @@ -434,7 +440,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing credential _, err := r.client.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) @@ -551,8 +562,8 @@ func readCredentials(ctx context.Context, model *Model, region string, client *o credentialsGroupResp, err := client.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 5643081a7..d8074e7dc 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" @@ -247,6 +248,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) @@ -305,7 +311,13 @@ func (r *credentialsGroupResource) Delete(ctx context.Context, req resource.Dele // Delete existing credentials group _, err := r.client.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) @@ -399,8 +411,8 @@ func readCredentialsGroups(ctx context.Context, model *Model, region string, cli credentialsGroupsResp, err := client.ListCredentialsGroupsExecute(ctx, model.ProjectId.ValueString(), region) 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 c0b24fb15..458480f43 100644 --- a/stackit/internal/services/observability/alertgroup/resource.go +++ b/stackit/internal/services/observability/alertgroup/resource.go @@ -366,8 +366,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 } @@ -417,6 +416,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 90f349d5e..cf9101ebb 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" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" @@ -219,19 +221,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 } @@ -267,6 +278,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 cb609196f..4a8207fa5 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" @@ -1147,13 +1148,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 } @@ -1505,6 +1511,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 27d90bd66..b1d2f6fbf 100644 --- a/stackit/internal/services/observability/log-alertgroup/resource.go +++ b/stackit/internal/services/observability/log-alertgroup/resource.go @@ -314,8 +314,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 } @@ -365,6 +364,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 4b74bcd48..e36416a25 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" @@ -403,8 +404,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 } @@ -527,6 +528,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 1510cd89a..668ab1859 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" @@ -229,14 +230,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.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 } @@ -289,7 +295,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.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 372088f3a..6d4b27efd 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" @@ -410,13 +411,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 || 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 } @@ -536,6 +542,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/postgresflex/database/resource.go b/stackit/internal/services/postgresflex/database/resource.go index 619aed53b..ddd0b1fac 100644 --- a/stackit/internal/services/postgresflex/database/resource.go +++ b/stackit/internal/services/postgresflex/database/resource.go @@ -264,6 +264,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) @@ -272,8 +277,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 } @@ -329,7 +334,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 399a61189..a6fc05978 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" @@ -378,6 +379,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) @@ -402,8 +408,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 } @@ -539,6 +545,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 4d5e9ffc1..e9843cfac 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" @@ -283,6 +284,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) @@ -291,8 +297,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 } @@ -418,7 +424,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 50b5fecaa..abd56045d 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" @@ -246,14 +247,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.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 } @@ -306,7 +312,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.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 19f1c2e9c..60b5e3d5c 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" @@ -411,13 +412,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 || 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 } @@ -538,6 +544,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/redis/credential/resource.go b/stackit/internal/services/redis/credential/resource.go index bec8f926d..707fa3e97 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" @@ -235,14 +236,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.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 } @@ -295,7 +301,12 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ // Delete existing record set err := r.client.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 627261ff7..17dfea0ca 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" @@ -480,13 +481,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 || 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 } @@ -607,6 +613,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/resourcemanager/folder/resource.go b/stackit/internal/services/resourcemanager/folder/resource.go index 088ab62a0..bbb8abf52 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" @@ -258,14 +259,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 } @@ -359,6 +365,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 43acfd533..1fd6c6881 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" @@ -259,12 +260,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 } @@ -358,6 +364,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 12ec898c4..fc34915b8 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" @@ -262,13 +263,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 } @@ -396,6 +402,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 d49b62dfe..f7fdf8e4c 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" @@ -224,14 +225,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 } @@ -338,7 +344,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 62f0c2725..7e92a6a5e 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" @@ -305,6 +306,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) @@ -314,8 +320,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 5ef737d90..f87eaaf05 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" @@ -281,6 +282,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) @@ -289,8 +295,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 ebb234f27..200b1365d 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" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" @@ -199,6 +202,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 } @@ -256,6 +264,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 ea254c94c..3ea5ac7bf 100644 --- a/stackit/internal/services/serviceaccount/key/resource.go +++ b/stackit/internal/services/serviceaccount/key/resource.go @@ -222,13 +222,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 } @@ -280,6 +284,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 e9095a2ef..dc5d63b10 100644 --- a/stackit/internal/services/sfs/export-policy/resource.go +++ b/stackit/internal/services/sfs/export-policy/resource.go @@ -328,6 +328,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 3686ce346..33c1d239d 100644 --- a/stackit/internal/services/sfs/resourcepool/resource.go +++ b/stackit/internal/services/sfs/resourcepool/resource.go @@ -299,6 +299,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 70e2e72c2..5ad8c8f81 100644 --- a/stackit/internal/services/sfs/share/resource.go +++ b/stackit/internal/services/sfs/share/resource.go @@ -298,6 +298,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 69b916fbc..7248627ee 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" @@ -2190,8 +2191,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 } @@ -2272,6 +2273,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 ec804d89c..d4aac6dda 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" @@ -27,6 +28,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" ) @@ -313,17 +315,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 d45cdc19c..52b3e87ff 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" @@ -461,6 +462,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) @@ -495,8 +501,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 } @@ -637,6 +643,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 796dcfd2d..a55787566 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" @@ -289,6 +290,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) @@ -297,8 +303,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 } @@ -354,6 +360,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 }