Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
44 changes: 44 additions & 0 deletions internal/elasticsearch/security/api_key/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,17 @@ func TestAccResourceSecurityApiKeyCrossCluster(t *testing.T) {
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "access.replication.0.names.0", "archive-*"),
),
},
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(api_key.MinVersionWithCrossCluster),
Config: testAccResourceSecurityApiKeyCrossClusterUpdate(apiKeyName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "name", apiKeyName),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "type", "cross_cluster"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "access.search.0.names.0", "log-*"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "access.search.0.names.1", "metrics-*"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "access.replication.0.names.0", "archives-*"),
),
},
},
})
}
Expand Down Expand Up @@ -507,3 +518,36 @@ resource "elasticstack_elasticsearch_security_api_key" "test" {
}
`, apiKeyName)
}

func testAccResourceSecurityApiKeyCrossClusterUpdate(apiKeyName string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
}

resource "elasticstack_elasticsearch_security_api_key" "test" {
name = "%s"
type = "cross_cluster"

access = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have this acceptance test update something in access to get some IT style acceptance test coverage of SetUnknownIfAccessHasChanges?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, it does exactly that.

search = [
{
names = ["log-*", "metrics-*"]
}
]
replication = [
{
names = ["archives-*"]
}
]
}

expiration = "30d"

metadata = jsonencode({
description = "Cross-cluster test key"
environment = "test"
})
}
`, apiKeyName)
}
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 @@ -15,6 +15,7 @@ import (
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
11 changes: 7 additions & 4 deletions internal/elasticsearch/security/api_key/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import (
)

const (
currentSchemaVersion int64 = 2
defaultAPIKeyType = "rest"
currentSchemaVersion int64 = 2
restAPIKeyType = "rest"
crossClusterAPIKeyType = "cross_cluster"
defaultAPIKeyType = restAPIKeyType
)

func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
Expand Down Expand Up @@ -65,7 +67,7 @@ func (r *Resource) getSchema(version int64) schema.Schema {
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf(defaultAPIKeyType, "cross_cluster"),
stringvalidator.OneOf(defaultAPIKeyType, crossClusterAPIKeyType),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
Expand All @@ -84,6 +86,7 @@ func (r *Resource) getSchema(version int64) schema.Schema {
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
r.requiresReplaceIfUpdateNotSupported(),
SetUnknownIfAccessHasChanges(),
},
},
"expiration": schema.StringAttribute{
Expand Down Expand Up @@ -114,7 +117,7 @@ func (r *Resource) getSchema(version int64) schema.Schema {
Description: "Access configuration for cross-cluster API keys. Only applicable when type is 'cross_cluster'.",
Optional: true,
Validators: []validator.Object{
RequiresType("cross_cluster"),
RequiresType(crossClusterAPIKeyType),
},
Attributes: map[string]schema.Attribute{
"search": schema.ListNestedAttribute{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package api_key

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// SetUnknownIfAccessHasChanges returns a plan modifier that sets the current attribute to unknown
// if the access attribute has changed between state and config for cross-cluster API keys.
func SetUnknownIfAccessHasChanges() planmodifier.String {
return setUnknownIfAccessHasChanges{}
}

type setUnknownIfAccessHasChanges struct{}

func (s setUnknownIfAccessHasChanges) Description(ctx context.Context) string {
return "Sets the attribute value to unknown if the access attribute has changed for cross-cluster API keys"
}

func (s setUnknownIfAccessHasChanges) MarkdownDescription(ctx context.Context) string {
return s.Description(ctx)
}

func (s setUnknownIfAccessHasChanges) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) {
// Only apply this modifier if we have both state and config
if req.State.Raw.IsNull() || req.Config.Raw.IsNull() {
return
}

// Get the type attribute to check if this is a cross-cluster API key
var keyType types.String
resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("type"), &keyType)...)
if resp.Diagnostics.HasError() {
return
}

// Only apply to cross-cluster API keys
if keyType.ValueString() != crossClusterAPIKeyType {
return
}

// Get the access attribute from state and config to check if it has changed
var stateAccess, configAccess types.Object
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("access"), &stateAccess)...)
resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("access"), &configAccess)...)
if resp.Diagnostics.HasError() {
return
}

// If the access attribute has changed between state and config, set the current attribute to Unknown
if !stateAccess.Equal(configAccess) {
resp.PlanValue = types.StringUnknown()
}
}
Loading
Loading