Skip to content

Commit

Permalink
Don't force resource replacement when expiration is unset during SDK-…
Browse files Browse the repository at this point in the history
…PF migration (elastic#875)

* Don't force resource replacement when expiration is unset during SDK-PF migratino

* CHANGELOG.md
  • Loading branch information
tobio authored and kjwardy committed Nov 19, 2024
1 parent 77fc587 commit 9a2ecc3
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 4 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
## [Unreleased]

- Allow `elasticstack_kibana_alerting_rule` to be used without Elasticsearch being configured. ([#869](https://github.com/elastic/terraform-provider-elasticstack/pull/869))
- Add resource `elasticstack_elasticsearch_data_stream_lifecycle` ([838](https://github.com/elastic/terraform-provider-elasticstack/issues/838))
- Add resource `elasticstack_elasticsearch_data_stream_lifecycle` ([#838](https://github.com/elastic/terraform-provider-elasticstack/issues/838))
- Ensure API keys are not replaced when upgrading from 0.11.9 or earlier. ([#875](https://github.com/elastic/terraform-provider-elasticstack/pull/875))

## [0.11.10] - 2024-10-23

Expand Down
2 changes: 1 addition & 1 deletion internal/clients/elasticsearch/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ func UpdateApiKey(apiClient *clients.ApiClient, apikey models.ApiKey) fwdiag.Dia
return utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to create apikey"); diags.HasError() {
if diags := utils.CheckError(res, "Unable to update apikey"); diags.HasError() {
return utils.FrameworkDiagsFromSDK(diags)
}

Expand Down
77 changes: 77 additions & 0 deletions internal/elasticsearch/security/api_key/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,60 @@ func SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(minApiKeySupported
}
}

func TestAccResourceSecurityApiKeyFromSDK(t *testing.T) {
// generate a random name
apiKeyName := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
var initialApiKey string

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: checkResourceSecurityApiKeyDestroy,
Steps: []resource.TestStep{
{
// Create the api_key with the last provider version where the api_key resource was built on the SDK
ExternalProviders: map[string]resource.ExternalProvider{
"elasticstack": {
Source: "elastic/elasticstack",
VersionConstraint: "0.11.9",
},
},
ProtoV6ProviderFactories: acctest.Providers,
SkipFunc: versionutils.CheckIfVersionIsUnsupported(api_key.MinVersion),
Config: testAccResourceSecurityApiKeyWithoutExpiration(apiKeyName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "name", apiKeyName),
resource.TestCheckResourceAttrSet("elasticstack_elasticsearch_security_api_key.test", "role_descriptors"),
resource.TestCheckResourceAttrSet("elasticstack_elasticsearch_security_api_key.test", "encoded"),
resource.TestCheckResourceAttrSet("elasticstack_elasticsearch_security_api_key.test", "id"),
resource.TestCheckResourceAttrWith("elasticstack_elasticsearch_security_api_key.test", "api_key", func(value string) error {
initialApiKey = value

if value == "" {
return fmt.Errorf("expected api_key to be non-empty")
}

return nil
}),
),
},
{
ProtoV6ProviderFactories: acctest.Providers,
SkipFunc: versionutils.CheckIfVersionIsUnsupported(api_key.MinVersion),
Config: testAccResourceSecurityApiKeyWithoutExpiration(apiKeyName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrWith("elasticstack_elasticsearch_security_api_key.test", "api_key", func(value string) error {
if value != initialApiKey {
return fmt.Errorf("expected api_key to be unchanged")
}

return nil
}),
),
},
},
})
}

func testAccResourceSecurityApiKeyCreate(apiKeyName string) string {
return fmt.Sprintf(`
provider "elasticstack" {
Expand Down Expand Up @@ -291,6 +345,29 @@ resource "elasticstack_elasticsearch_security_api_key" "test" {
`, apiKeyName)
}

func testAccResourceSecurityApiKeyWithoutExpiration(apiKeyName string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
}
resource "elasticstack_elasticsearch_security_api_key" "test" {
name = "%s"
role_descriptors = jsonencode({
role-a = {
cluster = ["all"]
indices = [{
names = ["index-a*"]
privileges = ["read"]
allow_restricted_indices = false
}]
}
})
}
`, apiKeyName)
}

func testAccResourceSecurityApiKeyRemoteIndices(apiKeyName string) string {
return fmt.Sprintf(`
provider "elasticstack" {
Expand Down
1 change: 1 addition & 0 deletions internal/elasticsearch/security/api_key/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
// Ensure provider defined types fully satisfy framework interfaces
var _ resource.Resource = &Resource{}
var _ resource.ResourceWithConfigure = &Resource{}
var _ resource.ResourceWithUpgradeState = &Resource{}
var (
MinVersion = version.Must(version.NewVersion("8.0.0")) // Enabled in 8.0
MinVersionWithUpdate = version.Must(version.NewVersion("8.4.0"))
Expand Down
7 changes: 5 additions & 2 deletions internal/elasticsearch/security/api_key/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import (
providerschema "github.com/elastic/terraform-provider-elasticstack/internal/schema"
)

const currentSchemaVersion int64 = 1

func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = r.getSchema()
resp.Schema = r.getSchema(currentSchemaVersion)
}

func (r *Resource) getSchema() schema.Schema {
func (r *Resource) getSchema(version int64) schema.Schema {
return schema.Schema{
Version: version,
Description: "Creates an API key for access without requiring basic authentication. See, https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html",
Blocks: map[string]schema.Block{
"elasticsearch_connection": providerschema.GetEsFWConnectionBlock("elasticsearch_connection", false),
Expand Down
30 changes: 30 additions & 0 deletions internal/elasticsearch/security/api_key/state_upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package api_key

import (
"context"

"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

func (r *Resource) UpgradeState(context.Context) map[int64]resource.StateUpgrader {
return map[int64]resource.StateUpgrader{
0: {
PriorSchema: utils.Pointer(r.getSchema(0)),
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
var model tfModel
resp.Diagnostics.Append(req.State.Get(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}

if utils.IsKnown(model.Expiration) && model.Expiration.ValueString() == "" {
model.Expiration = basetypes.NewStringNull()
}

resp.State.Set(ctx, model)
},
},
}
}

0 comments on commit 9a2ecc3

Please sign in to comment.