Skip to content

Commit

Permalink
Merge pull request #341 from ns1-terraform/PENG-6067/fix-apikey-state
Browse files Browse the repository at this point in the history
Fix to prevent api key from being removed from the state file when the key is updated
  • Loading branch information
pburrows-ns1 authored Feb 19, 2025
2 parents b7986ca + 8488269 commit 8cc2b0a
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.5.2 (February 19, 2025)
BUGFIX
* Keep API key in state file

## 2.5.1 (February 5, 2025)
ENHANCEMENTS
* Allow underscore for non-HTTPS redirects
Expand Down
2 changes: 1 addition & 1 deletion ns1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

var (
clientVersion = "2.5.1"
clientVersion = "2.5.2"
providerUserAgent = "tf-ns1" + "/" + clientVersion
defaultRetryMax = 3
)
Expand Down
7 changes: 6 additions & 1 deletion ns1/resource_apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,16 @@ func apikeyResource() *schema.Resource {
func apikeyToResourceData(d *schema.ResourceData, k *account.APIKey) error {
d.SetId(k.ID)
d.Set("name", k.Name)
d.Set("key", k.Key)
d.Set("teams", k.TeamIDs)
d.Set("ip_whitelist", k.IPWhitelist)
d.Set("ip_whitelist_strict", k.IPWhitelistStrict)
permissionsToResourceData(d, k.Permissions)

// keep the existing key in the state file when there's no key in the response
if k.Key != "" {
d.Set("key", k.Key)
}

return nil
}

Expand Down
56 changes: 42 additions & 14 deletions ns1/resource_apikey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ func TestAccAPIKey_basic(t *testing.T) {
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
testAccCheckAPIKeyIPWhitelists(&apiKey, []string{"1.1.1.1", "2.2.2.2"}),
testAccCheckAPIKeyNotEmpty(&apiKey),
resource.TestCheckResourceAttr("ns1_apikey.it", "ip_whitelist_strict", "true"),
),
},
{
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"key"}, // importing existing key won't have the key
},
},
})
Expand All @@ -57,6 +59,7 @@ func TestAccAPIKey_updated(t *testing.T) {
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
testAccCheckAPIKeyIPWhitelists(&apiKey, []string{"1.1.1.1", "2.2.2.2"}),
testAccCheckAPIKeyNotEmpty(&apiKey),
resource.TestCheckResourceAttr("ns1_apikey.it", "ip_whitelist_strict", "true"),
),
},
Expand All @@ -66,13 +69,15 @@ func TestAccAPIKey_updated(t *testing.T) {
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, updatedName),
testAccCheckAPIKeyIPWhitelists(&apiKey, []string{}),
testAccCheckAPIKeyNotEmpty(&apiKey),
resource.TestCheckResourceAttr("ns1_apikey.it", "ip_whitelist_strict", "false"),
),
},
{
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"key"}, // importing existing key won't have the key
},
},
})
Expand Down Expand Up @@ -122,14 +127,14 @@ func TestAccAPIKey_teamKey(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
// ensure Key attribute is populated on create of apikey joined to team
resource.TestCheckResourceAttrSet("ns1_apikey.it", "key"),
testAccCheckAPIKeyNotEmpty(&apiKey),
),
},
{
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"key"}, // importing existing key won't have the key
},
},
})
Expand All @@ -150,20 +155,23 @@ func TestAccAPIKey_permissions(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
testAccCheckAPIKeyNotEmpty(&apiKey),
),
},
{
Config: testAccAPIKeyPermissionsOnTeam(rString),
Check: resource.ComposeTestCheckFunc(
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
testAccCheckAPIKeyNotEmpty(&apiKey),
),
},
{
Config: testAccAPIKeyPermissionsNoTeam(rString),
Check: resource.ComposeTestCheckFunc(
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
testAccCheckAPIKeyNotEmpty(&apiKey),
// The key should still have this permission, it would have inherited it from the team.
resource.TestCheckResourceAttr("ns1_apikey.it", "account_manage_account_settings", "true"),
resource.TestCheckResourceAttr("ns1_apikey.it", "account_manage_ip_whitelist", "true"),
Expand All @@ -175,15 +183,17 @@ func TestAccAPIKey_permissions(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckAPIKeyExists("ns1_apikey.it", &apiKey),
testAccCheckAPIKeyName(&apiKey, name),
testAccCheckAPIKeyNotEmpty(&apiKey),
// But if an apply is ran again, the permission will be removed.
resource.TestCheckResourceAttr("ns1_apikey.it", "account_manage_account_settings", "false"),
resource.TestCheckResourceAttr("ns1_apikey.it", "account_manage_ip_whitelist", "false"),
),
},
{
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ResourceName: "ns1_apikey.it",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"key"}, // importing existing key won't have the key
},
},
})
Expand Down Expand Up @@ -277,6 +287,24 @@ func testAccManualDeleteAPIKey(apiKey *account.APIKey) func() {
}
}

func testAccCheckAPIKeyNotEmpty(k *account.APIKey) resource.TestCheckFunc {
return func(s *terraform.State) error {
var key string

for _, rs := range s.RootModule().Resources {
if rs.Primary.ID == k.ID {
key = rs.Primary.Attributes["key"]
}
}

if key == "" {
return fmt.Errorf("key should not be empty string")
}

return nil
}
}

func testAccAPIKeyBasic(apiKeyName string) string {
return fmt.Sprintf(`resource "ns1_apikey" "it" {
name = "%s"
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/apikey.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ In addition to all arguments above, the following attributes are exported:

## Import

-> Imported keys will not have their key stored in the state file.

`terraform import ns1_apikey`

So for the example above:
Expand Down

0 comments on commit 8cc2b0a

Please sign in to comment.