Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/cloudflare_access_service_token: add support for Duration #2647

Merged
merged 2 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/2647.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/cloudflare_access_service_token: add support for managing `Duration`
```
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func resourceCloudflareAccessServiceTokenRead(ctx context.Context, d *schema.Res
d.Set("name", token.Name)
d.Set("client_id", token.ClientID)
d.Set("expires_at", token.ExpiresAt.Format(time.RFC3339))
d.Set("duration", token.Duration)
}
}

Expand All @@ -93,7 +94,12 @@ func resourceCloudflareAccessServiceTokenCreate(ctx context.Context, d *schema.R
return diag.FromErr(err)
}

serviceToken, err := client.CreateAccessServiceToken(ctx, identifier, cloudflare.CreateAccessServiceTokenParams{Name: d.Get("name").(string)})
params := cloudflare.CreateAccessServiceTokenParams{Name: d.Get("name").(string)}
if value, ok := d.GetOk("duration"); ok {
params.Duration = value.(string)
}

serviceToken, err := client.CreateAccessServiceToken(ctx, identifier, params)

if err != nil {
return diag.FromErr(fmt.Errorf("error creating access service token: %w", err))
Expand All @@ -104,6 +110,7 @@ func resourceCloudflareAccessServiceTokenCreate(ctx context.Context, d *schema.R
d.Set("client_id", serviceToken.ClientID)
d.Set("client_secret", serviceToken.ClientSecret)
d.Set("expires_at", serviceToken.ExpiresAt.Format(time.RFC3339))
d.Set("duration", serviceToken.Duration)

resourceCloudflareAccessServiceTokenRead(ctx, d, meta)

Expand All @@ -118,10 +125,16 @@ func resourceCloudflareAccessServiceTokenUpdate(ctx context.Context, d *schema.R
return diag.FromErr(err)
}

serviceToken, err := client.UpdateAccessServiceToken(ctx, identifier, cloudflare.UpdateAccessServiceTokenParams{
params := cloudflare.UpdateAccessServiceTokenParams{
UUID: d.Id(),
Name: d.Get("name").(string),
})
}

if d.HasChange("duration") {
params.Duration = d.Get("duration").(string)
}

serviceToken, err := client.UpdateAccessServiceToken(ctx, identifier, params)

if err != nil {
return diag.FromErr(fmt.Errorf("error updating access service token: %w", err))
Expand Down
158 changes: 108 additions & 50 deletions internal/sdkv2provider/resource_cloudflare_access_service_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

func TestAccCloudflareAccessServiceTokenCreate(t *testing.T) {
func TestAccCloudflareAccessServiceToken_Basic(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access
// Service Tokens endpoint does not yet support the API tokens and it
// results in misleading state error messages.
Expand All @@ -22,7 +22,7 @@ func TestAccCloudflareAccessServiceTokenCreate(t *testing.T) {
}

rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_access_service_token.tf-acc-%s", rnd)
name := fmt.Sprintf("cloudflare_access_service_token.%s", rnd)
resourceName := strings.Split(name, ".")[1]

resource.Test(t, resource.TestCase{
Expand All @@ -37,58 +37,18 @@ func TestAccCloudflareAccessServiceTokenCreate(t *testing.T) {
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testCloudflareAccessServiceTokenBasicConfig(resourceName, resourceName, cloudflare.ZoneIdentifier(zoneID)),
Config: testCloudflareAccessServiceTokenBasicConfig(resourceName, resourceName+"-updated", cloudflare.AccountIdentifier(accountID)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(name, "name", resourceName),
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "name", resourceName+"-updated"),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
),
},
},
})
}

func TestAccCloudflareAccessServiceTokenUpdate(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access
// Service Tokens endpoint does not yet support the API tokens and it
// results in misleading state error messages.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
t.Setenv("CLOUDFLARE_API_TOKEN", "")
}

rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_access_service_token.tf-acc-%s", rnd)
resourceName := strings.Split(name, ".")[1]

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testCloudflareAccessServiceTokenBasicConfig(resourceName, resourceName, cloudflare.AccountIdentifier(accountID)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "name", resourceName),
),
},
{
Config: testCloudflareAccessServiceTokenBasicConfig(resourceName, resourceName+"-updated", cloudflare.AccountIdentifier(accountID)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "name", resourceName+"-updated"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
Expand All @@ -101,13 +61,23 @@ func TestAccCloudflareAccessServiceTokenUpdate(t *testing.T) {
{
Config: testCloudflareAccessServiceTokenBasicConfig(resourceName, resourceName, cloudflare.ZoneIdentifier(zoneID)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(name, "name", resourceName),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
{
Config: testCloudflareAccessServiceTokenBasicConfig(resourceName, resourceName+"-updated", cloudflare.ZoneIdentifier(zoneID)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(name, "name", resourceName+"-updated"),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
Expand All @@ -128,7 +98,7 @@ func TestAccCloudflareAccessServiceTokenUpdate(t *testing.T) {
// rnd := generateRandomResourceName()
// var initialState terraform.ResourceState

// name := fmt.Sprintf("cloudflare_access_service_token.tf-acc-%s", rnd)
// name := fmt.Sprintf("cloudflare_access_service_token.%s", rnd)
// resourceName := strings.Split(name, ".")[1]
// expirationTime := 365

Expand Down Expand Up @@ -195,7 +165,7 @@ func testAccCheckCloudflareAccessServiceTokenRenewed(n string, oldResourceState
}
}

func TestAccCloudflareAccessServiceTokenDelete(t *testing.T) {
func TestAccCloudflareAccessServiceToken_Delete(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access
// Service Tokens endpoint does not yet support the API tokens and it
// results in misleading state error messages.
Expand All @@ -204,7 +174,7 @@ func TestAccCloudflareAccessServiceTokenDelete(t *testing.T) {
}

rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_access_service_token.tf-acc-%s", rnd)
name := fmt.Sprintf("cloudflare_access_service_token.%s", rnd)
resourceName := strings.Split(name, ".")[1]

resource.Test(t, resource.TestCase{
Expand All @@ -223,6 +193,7 @@ func TestAccCloudflareAccessServiceTokenDelete(t *testing.T) {
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
Expand All @@ -241,6 +212,83 @@ func TestAccCloudflareAccessServiceTokenDelete(t *testing.T) {
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
})
}

func TestAccCloudflareAccessServiceToken_WithDuration(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access
// Service Tokens endpoint does not yet support the API tokens and it
// results in misleading state error messages.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
t.Setenv("CLOUDFLARE_API_TOKEN", "")
}

rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_access_service_token.%s", rnd)
resourceName := strings.Split(name, ".")[1]

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckCloudflareAccessServiceTokenDestroy,
Steps: []resource.TestStep{
{
Config: testCloudflareAccessServiceTokenBasicConfigWithDuration(resourceName, resourceName, cloudflare.AccountIdentifier(accountID), "forever"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "name", resourceName),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "forever"),
),
},
{
Config: testCloudflareAccessServiceTokenBasicConfigWithDuration(resourceName, resourceName, cloudflare.AccountIdentifier(accountID), "8760h"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.AccountIDSchemaKey, accountID),
resource.TestCheckResourceAttr(name, "name", resourceName),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
})

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
CheckDestroy: testAccCheckCloudflareAccessServiceTokenDestroy,
Steps: []resource.TestStep{
{
Config: testCloudflareAccessServiceTokenBasicConfigWithDuration(resourceName, resourceName, cloudflare.ZoneIdentifier(zoneID), "forever"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(name, "name", resourceName),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "forever"),
),
},
{
Config: testCloudflareAccessServiceTokenBasicConfigWithDuration(resourceName, resourceName, cloudflare.ZoneIdentifier(zoneID), "8760h"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(name, "name", resourceName),
resource.TestCheckResourceAttrSet(name, "client_id"),
resource.TestCheckResourceAttrSet(name, "client_secret"),
resource.TestCheckResourceAttrSet(name, "expires_at"),
resource.TestCheckResourceAttr(name, "duration", "8760h"),
),
},
},
Expand All @@ -256,6 +304,16 @@ resource "cloudflare_access_service_token" "%[1]s" {
}`, resourceName, tokenName, identifier.Type, identifier.Identifier)
}

func testCloudflareAccessServiceTokenBasicConfigWithDuration(resourceName string, tokenName string, identifier *cloudflare.ResourceContainer, duration string) string {
return fmt.Sprintf(`
resource "cloudflare_access_service_token" "%[1]s" {
%[3]s_id = "%[4]s"
name = "%[2]s"
min_days_for_renewal = "0"
duration = "%[5]s"
}`, resourceName, tokenName, identifier.Type, identifier.Identifier, duration)
}

func testAccCheckCloudflareAccessServiceTokenDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*cloudflare.API)

Expand Down
10 changes: 10 additions & 0 deletions internal/sdkv2provider/schema_cloudflare_access_service_tokens.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package sdkv2provider

import (
"fmt"

"github.com/cloudflare/terraform-provider-cloudflare/internal/consts"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceCloudflareAccessServiceTokenSchema() map[string]*schema.Schema {
Expand Down Expand Up @@ -48,5 +51,12 @@ func resourceCloudflareAccessServiceTokenSchema() map[string]*schema.Schema {
Default: 0,
Description: "Refresh the token if terraform is run within the specified amount of days before expiration",
},
"duration": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"8760h", "17520h", "43800h", "87600h", "forever"}, false),
Description: fmt.Sprintf("Length of time the service token is valid for. %s", renderAvailableDocumentationValuesStringSlice([]string{"8760h", "17520h", "43800h", "87600h", "forever"})),
},
}
}