diff --git a/.changelog/6735.txt b/.changelog/6735.txt new file mode 100644 index 0000000000..1ae9038dc9 --- /dev/null +++ b/.changelog/6735.txt @@ -0,0 +1,3 @@ +```release-note:bug +bigtable: updated the error handling logic to remove the resource on resource not found error only +``` diff --git a/google/resource_bigtable_gc_policy.go b/google/resource_bigtable_gc_policy.go index b511236301..f809e52fab 100644 --- a/google/resource_bigtable_gc_policy.go +++ b/google/resource_bigtable_gc_policy.go @@ -256,9 +256,12 @@ func resourceBigtableGCPolicyRead(d *schema.ResourceData, meta interface{}) erro columnFamily := d.Get("column_family").(string) ti, err := c.TableInfo(ctx, name) if err != nil { - log.Printf("[WARN] Removing %s because it's gone", name) - d.SetId("") - return nil + if isNotFoundGrpcError(err) { + log.Printf("[WARN] Removing the GC policy because the parent table %s is gone", name) + d.SetId("") + return nil + } + return err } for _, fi := range ti.FamilyInfos { diff --git a/google/resource_bigtable_instance.go b/google/resource_bigtable_instance.go index 3f142c9bb1..aef69c7c4e 100644 --- a/google/resource_bigtable_instance.go +++ b/google/resource_bigtable_instance.go @@ -246,9 +246,12 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro instance, err := c.InstanceInfo(ctx, instanceName) if err != nil { - log.Printf("[WARN] Removing %s because it's gone", instanceName) - d.SetId("") - return nil + if isNotFoundGrpcError(err) { + log.Printf("[WARN] Removing %s because it's gone", instanceName) + d.SetId("") + return nil + } + return err } if err := d.Set("project", project); err != nil { diff --git a/google/resource_bigtable_table.go b/google/resource_bigtable_table.go index 8ccc415093..8944b1dacc 100644 --- a/google/resource_bigtable_table.go +++ b/google/resource_bigtable_table.go @@ -173,9 +173,12 @@ func resourceBigtableTableRead(d *schema.ResourceData, meta interface{}) error { name := d.Get("name").(string) table, err := c.TableInfo(ctx, name) if err != nil { - log.Printf("[WARN] Removing %s because it's gone", name) - d.SetId("") - return nil + if isNotFoundGrpcError(err) { + log.Printf("[WARN] Removing %s because it's gone", name) + d.SetId("") + return nil + } + return err } if err := d.Set("project", project); err != nil { diff --git a/google/utils.go b/google/utils.go index 1b932c4ca8..0005cec2c5 100644 --- a/google/utils.go +++ b/google/utils.go @@ -16,6 +16,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "google.golang.org/api/googleapi" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type TerraformResourceDataChange interface { @@ -160,6 +162,15 @@ func isConflictError(err error) bool { return false } +// gRPC does not return errors of type *googleapi.Error. Instead the errors returned are *status.Error. +// See the types of codes returned here (https://pkg.go.dev/google.golang.org/grpc/codes#Code). +func isNotFoundGrpcError(err error) bool { + if errorStatus, ok := status.FromError(err); ok && errorStatus.Code() == codes.NotFound { + return true + } + return false +} + // expandLabels pulls the value of "labels" out of a TerraformResourceData as a map[string]string. func expandLabels(d TerraformResourceData) map[string]string { return expandStringMap(d, "labels") diff --git a/google/utils_test.go b/google/utils_test.go index 99dc2fa3e7..fed9c5643d 100644 --- a/google/utils_test.go +++ b/google/utils_test.go @@ -11,6 +11,8 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "google.golang.org/api/googleapi" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) func TestConvertStringArr(t *testing.T) { @@ -694,6 +696,21 @@ func TestConflictError(t *testing.T) { // skipping negative tests as other cases may be added later. } +func TestIsNotFoundGrpcErrort(t *testing.T) { + error_status := status.New(codes.FailedPrecondition, "FailedPrecondition error") + if isNotFoundGrpcError(error_status.Err()) { + t.Error("found FailedPrecondition as a NotFound error") + } + error_status = status.New(codes.OK, "OK") + if isNotFoundGrpcError(error_status.Err()) { + t.Error("found OK as a NotFound error") + } + error_status = status.New(codes.NotFound, "NotFound error") + if !isNotFoundGrpcError(error_status.Err()) { + t.Error("expect a NotFound error") + } +} + func TestSnakeToPascalCase(t *testing.T) { input := "boot_disk" expected := "BootDisk"