From 675fa326395afb145032b9e4d9df9c41decebdac Mon Sep 17 00:00:00 2001 From: Volodymyr Manilo <35466116+vmanilo@users.noreply.github.com> Date: Fri, 20 Oct 2023 21:24:46 +0200 Subject: [PATCH] Fix: service account and service key (#415) * fix service_account and service_key common lifecycle * fix tests * remove feature branch from CI * update test flow * revert minor changes * added behaviour to re-create service-key when service_account_id changes * added test --- twingate/internal/model/resource.go | 3 +- .../internal/provider/resource/resource.go | 4 +- .../internal/provider/resource/service-key.go | 4 + twingate/internal/test/acctests/helper.go | 3 +- .../acctests/resource/service-key_test.go | 85 +++++++++++++++++++ 5 files changed, 95 insertions(+), 4 deletions(-) diff --git a/twingate/internal/model/resource.go b/twingate/internal/model/resource.go index 89b2be27..c073bb60 100644 --- a/twingate/internal/model/resource.go +++ b/twingate/internal/model/resource.go @@ -2,6 +2,7 @@ package model import ( "fmt" + "strconv" "strings" "github.com/Twingate/terraform-provider-twingate/twingate/internal/attr" @@ -77,7 +78,7 @@ type PortRange struct { func (p PortRange) String() string { if p.Start == p.End { - return fmt.Sprintf("%d", p.Start) + return strconv.Itoa(p.Start) } return fmt.Sprintf("%d-%d", p.Start, p.End) diff --git a/twingate/internal/provider/resource/resource.go b/twingate/internal/provider/resource/resource.go index 616dc7d7..d851ef39 100644 --- a/twingate/internal/provider/resource/resource.go +++ b/twingate/internal/provider/resource/resource.go @@ -496,7 +496,7 @@ func convertResource(data *schema.ResourceData) (*model.Resource, error) { res.IsVisible = &val } - isBrowserShortcutEnabled, ok := data.GetOkExists(attr.IsBrowserShortcutEnabled) //nolint:staticcheck + isBrowserShortcutEnabled, ok := data.GetOkExists(attr.IsBrowserShortcutEnabled) //nolint if val := isBrowserShortcutEnabled.(bool); ok { res.IsBrowserShortcutEnabled = &val } @@ -530,7 +530,7 @@ func convertAccess(data *schema.ResourceData) ([]string, []string) { } func convertAuthoritativeFlagLegacy(data *schema.ResourceData) bool { - flag, hasFlag := data.GetOkExists(attr.IsAuthoritative) //nolint:staticcheck + flag, hasFlag := data.GetOkExists(attr.IsAuthoritative) //nolint if hasFlag { return flag.(bool) diff --git a/twingate/internal/provider/resource/service-key.go b/twingate/internal/provider/resource/service-key.go index 22521a0a..e4343daa 100644 --- a/twingate/internal/provider/resource/service-key.go +++ b/twingate/internal/provider/resource/service-key.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -58,6 +59,9 @@ func (r *serviceKey) Schema(_ context.Context, _ resource.SchemaRequest, resp *r attr.ServiceAccountID: schema.StringAttribute{ Required: true, Description: "The id of the Service Account", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, // optional attr.Name: schema.StringAttribute{ diff --git a/twingate/internal/test/acctests/helper.go b/twingate/internal/test/acctests/helper.go index 59afc139..b3eef07c 100644 --- a/twingate/internal/test/acctests/helper.go +++ b/twingate/internal/test/acctests/helper.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "os" + "strconv" "strings" "testing" "time" @@ -80,7 +81,7 @@ var ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ //n } func SetPageLimit(limit int) { - if err := os.Setenv(client.EnvPageLimit, fmt.Sprintf("%d", limit)); err != nil { + if err := os.Setenv(client.EnvPageLimit, strconv.Itoa(limit)); err != nil { log.Fatal("failed to set page limit", err) } } diff --git a/twingate/internal/test/acctests/resource/service-key_test.go b/twingate/internal/test/acctests/resource/service-key_test.go index e1ba852e..c81e3c84 100644 --- a/twingate/internal/test/acctests/resource/service-key_test.go +++ b/twingate/internal/test/acctests/resource/service-key_test.go @@ -326,3 +326,88 @@ func TestAccTwingateServiceKeyReCreateAfterChangingExpirationTime(t *testing.T) }) }) } + +func TestAccTwingateServiceKeyAndServiceAccountLifecycle(t *testing.T) { + t.Run("Test Twingate Resource : Acc Service Key and Service Account Lifecycle", func(t *testing.T) { + serviceAccountName := test.RandomName() + serviceAccountNameV2 := test.RandomName() + terraformServiceAccountName := test.TerraformRandName("test_acc") + terraformServiceAccountNameV2 := test.TerraformRandName("test_acc_v2") + terraformServiceAccountKeyName := test.TerraformRandName("test_key") + serviceAccount := acctests.TerraformServiceAccount(terraformServiceAccountName) + serviceAccountV2 := acctests.TerraformServiceAccount(terraformServiceAccountNameV2) + serviceKey := acctests.TerraformServiceKey(terraformServiceAccountKeyName) + + serviceKeyResourceID := new(string) + serviceAccountResourceID := new(string) + + sdk.Test(t, sdk.TestCase{ + ProtoV6ProviderFactories: acctests.ProviderFactories, + PreCheck: func() { acctests.PreCheck(t) }, + CheckDestroy: acctests.CheckTwingateServiceAccountDestroy, + Steps: []sdk.TestStep{ + { + Config: createServiceKeyV1(terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, terraformServiceAccountName), + Check: acctests.ComposeTestCheckFunc( + acctests.CheckTwingateResourceExists(serviceAccount), + sdk.TestCheckResourceAttr(serviceAccount, attr.Name, serviceAccountName), + acctests.CheckTwingateResourceExists(serviceKey), + sdk.TestCheckResourceAttrWith(serviceKey, attr.Token, nonEmptyValue), + acctests.GetTwingateResourceID(serviceKey, &serviceKeyResourceID), + acctests.GetTwingateResourceID(serviceKey, &serviceAccountResourceID), + ), + }, + { + Config: createServiceKeyV1(terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, terraformServiceAccountNameV2), + Check: acctests.ComposeTestCheckFunc( + acctests.CheckTwingateResourceExists(serviceAccountV2), + sdk.TestCheckResourceAttr(serviceAccountV2, attr.Name, serviceAccountNameV2), + acctests.CheckTwingateResourceExists(serviceKey), + sdk.TestCheckResourceAttrWith(serviceKey, attr.Token, nonEmptyValue), + + // test resources were re-created + sdk.TestCheckResourceAttrWith(serviceKey, attr.ID, func(value string) error { + if *serviceKeyResourceID == "" { + return errors.New("failed to fetch service_key resource id") + } + + if value == *serviceKeyResourceID { + return errors.New("service_key resource was not re-created") + } + + return nil + }), + + sdk.TestCheckResourceAttrWith(serviceAccountV2, attr.ID, func(value string) error { + if *serviceAccountResourceID == "" { + return errors.New("failed to fetch service_account resource id") + } + + if value == *serviceAccountResourceID { + return errors.New("service_account resource was not re-created") + } + + return nil + }), + ), + }, + }, + }) + }) +} + +func createServiceKeyV1(terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, serviceAccount string) string { + return fmt.Sprintf(` + resource "twingate_service_account" "%s" { + name = "%s" + } + + resource "twingate_service_account" "%s" { + name = "%s" + } + + resource "twingate_service_account_key" "%s" { + service_account_id = twingate_service_account.%s.id + } + `, terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, serviceAccount) +}