Skip to content
Closed
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: 2 additions & 1 deletion docs/resources/fleet_integration_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ resource "elasticstack_fleet_integration_policy" "sample" {

### Required

- `agent_policy_id` (String) ID of the agent policy.
- `integration_name` (String) The name of the integration package.
- `integration_version` (String) The version of the integration package.
- `name` (String) The name of the integration policy.
- `namespace` (String) The namespace of the integration policy.

### Optional

- `agent_policy_id` (String, Deprecated) ID of the agent policy.
- `agent_policy_ids` (List of String) List of agent policy IDs.
- `description` (String) The description of the integration policy.
- `enabled` (Boolean) Enable the integration policy.
- `force` (Boolean) Force operations, such as creation and deletion, to occur.
Expand Down
805 changes: 738 additions & 67 deletions generated/kbapi/kibana.gen.go

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion internal/fleet/integration_policy/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ func (r *integrationPolicyResource) Create(ctx context.Context, req resource.Cre
return
}

body, diags := planModel.toAPIModel(ctx, false)
feat, diags := r.buildFeatures(ctx)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

body, diags := planModel.toAPIModel(ctx, false, feat)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down
59 changes: 56 additions & 3 deletions internal/fleet/integration_policy/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package integration_policy

import (
"context"
"fmt"
"sort"

"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
Expand All @@ -12,12 +13,17 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
)

type features struct {
SupportsPolicyIds bool
}

type integrationPolicyModel struct {
ID types.String `tfsdk:"id"`
PolicyID types.String `tfsdk:"policy_id"`
Name types.String `tfsdk:"name"`
Namespace types.String `tfsdk:"namespace"`
AgentPolicyID types.String `tfsdk:"agent_policy_id"`
AgentPolicyIDs types.List `tfsdk:"agent_policy_ids"`
Description types.String `tfsdk:"description"`
Enabled types.Bool `tfsdk:"enabled"`
Force types.Bool `tfsdk:"force"`
Expand Down Expand Up @@ -45,7 +51,27 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data *
model.PolicyID = types.StringValue(data.Id)
model.Name = types.StringValue(data.Name)
model.Namespace = types.StringPointerValue(data.Namespace)
model.AgentPolicyID = types.StringPointerValue(data.PolicyId)

// Only populate the agent policy field that was originally configured
// to avoid Terraform detecting inconsistent state
originallyUsedAgentPolicyID := utils.IsKnown(model.AgentPolicyID)
originallyUsedAgentPolicyIDs := utils.IsKnown(model.AgentPolicyIDs)

if originallyUsedAgentPolicyID {
// Only set agent_policy_id if it was originally used
model.AgentPolicyID = types.StringPointerValue(data.PolicyId)
}
if originallyUsedAgentPolicyIDs {
// Only set agent_policy_ids if it was originally used
if data.PolicyIds != nil {
agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds)
diags.Append(d...)
model.AgentPolicyIDs = agentPolicyIDs
} else {
model.AgentPolicyIDs = types.ListNull(types.StringType)
}
}

model.Description = types.StringPointerValue(data.Description)
model.Enabled = types.BoolValue(data.Enabled)
model.IntegrationName = types.StringValue(data.Package.Name)
Expand Down Expand Up @@ -81,9 +107,22 @@ func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, i
}
}

func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool) (kbapi.PackagePolicyRequest, diag.Diagnostics) {
func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool, feat features) (kbapi.PackagePolicyRequest, diag.Diagnostics) {
var diags diag.Diagnostics

// Check if agent_policy_ids is configured and version supports it
if utils.IsKnown(model.AgentPolicyIDs) {
if !feat.SupportsPolicyIds {
return kbapi.PackagePolicyRequest{}, diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
path.Root("agent_policy_ids"),
"Unsupported Elasticsearch version",
fmt.Sprintf("Agent policy IDs are only supported in Elastic Stack %s and above", MinVersionPolicyIds),
),
}
}
}

body := kbapi.PackagePolicyRequest{
Description: model.Description.ValueStringPointer(),
Force: model.Force.ValueBoolPointer(),
Expand All @@ -94,7 +133,21 @@ func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate boo
Version: model.IntegrationVersion.ValueString(),
},
PolicyId: model.AgentPolicyID.ValueStringPointer(),
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)),
PolicyIds: func() *[]string {
if !model.AgentPolicyIDs.IsNull() && !model.AgentPolicyIDs.IsUnknown() {
var policyIDs []string
d := model.AgentPolicyIDs.ElementsAs(ctx, &policyIDs, false)
diags.Append(d...)
return &policyIDs
}
// Only return empty array for 8.15+ when agent_policy_ids is not defined
if feat.SupportsPolicyIds {
emptyArray := []string{}
return &emptyArray
}
return nil
}(),
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)),
}

if isUpdate {
Expand Down
18 changes: 18 additions & 0 deletions internal/fleet/integration_policy/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"

"github.com/elastic/terraform-provider-elasticstack/internal/clients"
"github.com/elastic/terraform-provider-elasticstack/internal/diagutil"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
)
Expand All @@ -16,6 +19,10 @@ var (
_ resource.ResourceWithUpgradeState = &integrationPolicyResource{}
)

var (
MinVersionPolicyIds = version.Must(version.NewVersion("8.15.0"))
)

// NewResource is a helper function to simplify the provider implementation.
func NewResource() resource.Resource {
return &integrationPolicyResource{}
Expand Down Expand Up @@ -44,3 +51,14 @@ func (r *integrationPolicyResource) UpgradeState(context.Context) map[int64]reso
0: {PriorSchema: getSchemaV0(), StateUpgrader: upgradeV0},
}
}

func (r *integrationPolicyResource) buildFeatures(ctx context.Context) (features, diag.Diagnostics) {
supportsPolicyIds, diags := r.client.EnforceMinVersion(ctx, MinVersionPolicyIds)
if diags.HasError() {
return features{}, diagutil.FrameworkDiagsFromSDK(diags)
}

return features{
SupportsPolicyIds: supportsPolicyIds,
}, nil
}
141 changes: 141 additions & 0 deletions internal/fleet/integration_policy/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,54 @@ import (
)

var minVersionIntegrationPolicy = version.Must(version.NewVersion("8.10.0"))
var minVersionIntegrationPolicyIds = version.Must(version.NewVersion("8.15.0"))

func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) {
policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: checkResourceIntegrationPolicyDestroy,
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicyIds),
Config: testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(policyName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "description", "IntegrationPolicyTest Policy"),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_name", "tcp"),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_version", "1.16.0"),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "agent_policy_ids.#", "2"),
),
},
},
})
}

func TestAccResourceIntegrationPolicyBothAgentPolicyFields(t *testing.T) {
policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: checkResourceIntegrationPolicyDestroy,
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicyIds),
Config: testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(policyName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "description", "IntegrationPolicyTest Policy"),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_name", "tcp"),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_version", "1.16.0"),
resource.TestCheckResourceAttrSet("elasticstack_fleet_integration_policy.test_policy", "agent_policy_id"),
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "agent_policy_ids.#", "1"),
),
},
},
})
}

func TestJsonTypes(t *testing.T) {
mapBytes, err := json.Marshal(map[string]string{})
Expand Down Expand Up @@ -451,3 +499,96 @@ resource "elasticstack_fleet_integration_policy" "test_policy" {
}
`, common, id, key, id)
}

func testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(id string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
kibana {}
}
resource "elasticstack_fleet_integration" "test_policy" {
name = "tcp"
version = "1.16.0"
force = true
}
resource "elasticstack_fleet_agent_policy" "test_policy_1" {
name = "%s Agent Policy 1"
namespace = "default"
description = "IntegrationPolicyTest Agent Policy 1"
monitor_logs = true
monitor_metrics = true
skip_destroy = false
}
resource "elasticstack_fleet_agent_policy" "test_policy_2" {
name = "%s Agent Policy 2"
namespace = "default"
description = "IntegrationPolicyTest Agent Policy 2"
monitor_logs = true
monitor_metrics = true
skip_destroy = false
}
resource "elasticstack_fleet_integration_policy" "test_policy" {
name = "%s"
namespace = "default"
description = "IntegrationPolicyTest Policy"
agent_policy_ids = [
elasticstack_fleet_agent_policy.test_policy_1.policy_id,
elasticstack_fleet_agent_policy.test_policy_2.policy_id
]
integration_name = elasticstack_fleet_integration.test_policy.name
integration_version = elasticstack_fleet_integration.test_policy.version
input {
input_id = "tcp-tcp"
streams_json = jsonencode({
"tcp.generic": {
"enabled": true
"vars": {
"listen_address": "localhost"
"listen_port": 8080
"data_stream.dataset": "tcp.generic"
"tags": []
"syslog_options": "field: message"
"ssl": ""
"custom": ""
}
}
})
}
}
`, id, id, id)
}

func testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(id string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
kibana {}
}
resource "elasticstack_fleet_integration" "test_policy" {
name = "tcp"
version = "1.16.0"
force = true
}
resource "elasticstack_fleet_agent_policy" "test_policy" {
name = "%s Agent Policy"
namespace = "default"
description = "IntegrationPolicyTest Agent Policy"
monitor_logs = true
monitor_metrics = true
skip_destroy = false
}
resource "elasticstack_fleet_integration_policy" "test_policy" {
name = "%s"
namespace = "default"
description = "IntegrationPolicyTest Policy"
agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id
agent_policy_ids = [elasticstack_fleet_agent_policy.test_policy.policy_id]
integration_name = elasticstack_fleet_integration.test_policy.name
integration_version = elasticstack_fleet_integration.test_policy.version
input {
input_id = "tcp-tcp"
enabled = true
}
}
`, id, id)
}
21 changes: 19 additions & 2 deletions internal/fleet/integration_policy/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import (

"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/types"
)

//go:embed resource-description.md
Expand Down Expand Up @@ -50,8 +55,20 @@ func getSchemaV1() schema.Schema {
Required: true,
},
"agent_policy_id": schema.StringAttribute{
Description: "ID of the agent policy.",
Required: true,
Description: "ID of the agent policy.",
Optional: true,
Validators: []validator.String{
stringvalidator.ConflictsWith(path.Root("agent_policy_ids").Expression()),
},
},
"agent_policy_ids": schema.ListAttribute{
Description: "List of agent policy IDs.",
ElementType: types.StringType,
Optional: true,
Validators: []validator.List{
listvalidator.ConflictsWith(path.Root("agent_policy_ids").Expression()),
listvalidator.SizeAtLeast(1),
},
},
"description": schema.StringAttribute{
Description: "The description of the integration policy.",
Expand Down
8 changes: 7 additions & 1 deletion internal/fleet/integration_policy/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ func (r *integrationPolicyResource) Update(ctx context.Context, req resource.Upd
return
}

body, diags := planModel.toAPIModel(ctx, true)
feat, diags := r.buildFeatures(ctx)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

body, diags := planModel.toAPIModel(ctx, true, feat)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
Expand Down
Loading
Loading