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

Add support for global data tags in agent policy creation and update #730

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Add support for data_stream `lifecycle` template settings ([#724](https://github.com/elastic/terraform-provider-elasticstack/pull/724))
- Fix a provider panic when `elasticstack_kibana_action_connector` reads a non-existant connector ([#729](https://github.com/elastic/terraform-provider-elasticstack/pull/729))
- Add support for `remote_indicies` to `elasticstack_elasticsearch_security_role` & `elasticstack_kibana_security_role` (#723)[https://github.com/elastic/terraform-provider-elasticstack/pull/723]
- Added support for global data tags in agent policy creation and update. Upgraded the generated Fleet API to version 8.15.0. (#730)[https://github.com/elastic/terraform-provider-elasticstack/pull/730]

## [0.11.6] - 2024-08-20

Expand Down
1 change: 1 addition & 0 deletions docs/resources/fleet_agent_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
- `description` (String) The description of the agent policy.
- `download_source_id` (String) The identifier for the Elastic Agent binary download server.
- `fleet_server_host_id` (String) The identifier for the Fleet server host.
- `global_data_tags` (Map of String) User-defined data tags that are added to all inputs.
- `monitor_logs` (Boolean) Enable collection of agent logs.
- `monitor_metrics` (Boolean) Enable collection of agent metrics.
- `monitoring_output_id` (String) The identifier for monitoring output.
Expand Down
4 changes: 2 additions & 2 deletions generated/alerting/api_alerting_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion generated/connectors/connectors.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

611 changes: 522 additions & 89 deletions generated/fleet/fleet.gen.go
mholttech marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions generated/fleet/getschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func transformOutputTypeRequired(schema *Schema) {
"schemas.output_update_request_elasticsearch.required",
"schemas.output_update_request_kafka.required",
"schemas.output_update_request_logstash.required",
"schemas.output_create_request_remote_elasticsearch.required",
}

for _, v := range path {
Expand Down
111 changes: 111 additions & 0 deletions internal/fleet/agent_policy_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import (
"context"
"fmt"

fleetapi "github.com/elastic/terraform-provider-elasticstack/generated/fleet"
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
"github.com/elastic/terraform-provider-elasticstack/internal/clients/fleet"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -15,6 +18,8 @@
monitorMetrics = "metrics"
)

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

func ResourceAgentPolicy() *schema.Resource {
agentPolicySchema := map[string]*schema.Schema{
"policy_id": {
Expand Down Expand Up @@ -79,6 +84,14 @@
Type: schema.TypeBool,
Optional: true,
},
"global_data_tags": {
Description: "User-defined data tags that are added to all inputs.",
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
}

return &schema.Resource{
Expand All @@ -103,6 +116,16 @@
return diags
}

apiClient, diags := clients.NewApiClientFromSDKResource(d, meta)
if diags.HasError() {
return diags
}

serverVersion, diags := apiClient.ServerVersion(ctx)
if diags.HasError() {
return diags
}

if id := d.Get("policy_id").(string); id != "" {
d.SetId(id)
}
Expand Down Expand Up @@ -140,6 +163,29 @@
}
req.MonitoringEnabled = &monitoringValues

if tags, ok := d.GetOk("global_data_tags"); ok {
tagMap := tags.(map[string]interface{})

if len(tagMap) > 0 && serverVersion.LessThan(minVersionGlobalDataTags) {
return diag.FromErr(fmt.Errorf("'global_data_tags' is supported only for Elasticsearch v%s and above", minVersionGlobalDataTags.String()))
}

gdt := []map[string]fleetapi.AgentPolicyCreateRequest_GlobalDataTags_AdditionalProperties{}
for key, value := range tagMap {
name := fleetapi.AgentPolicyCreateRequest_GlobalDataTags_AdditionalProperties{}
name.FromAgentPolicyCreateRequestGlobalDataTags0(key)

Check failure on line 176 in internal/fleet/agent_policy_resource.go

View workflow job for this annotation

GitHub Actions / Lint

Error return value of `name.FromAgentPolicyCreateRequestGlobalDataTags0` is not checked (errcheck)

val := fleetapi.AgentPolicyCreateRequest_GlobalDataTags_AdditionalProperties{}
val.FromAgentPolicyCreateRequestGlobalDataTags0(value.(string))

Check failure on line 179 in internal/fleet/agent_policy_resource.go

View workflow job for this annotation

GitHub Actions / Lint

Error return value of `val.FromAgentPolicyCreateRequestGlobalDataTags0` is not checked (errcheck)

gdt = append(gdt, map[string]fleetapi.AgentPolicyCreateRequest_GlobalDataTags_AdditionalProperties{
"name": name,
"value": val,
})
}
req.GlobalDataTags = &gdt
}

policy, diags := fleet.CreateAgentPolicy(ctx, fleetClient, req)
if diags.HasError() {
return diags
Expand All @@ -159,6 +205,16 @@
return diags
}

apiClient, diags := clients.NewApiClientFromSDKResource(d, meta)
if diags.HasError() {
return diags
}

serverVersion, diags := apiClient.ServerVersion(ctx)
if diags.HasError() {
return diags
}

req := fleetapi.AgentPolicyUpdateRequest{
Name: d.Get("name").(string),
Namespace: d.Get("namespace").(string),
Expand Down Expand Up @@ -189,6 +245,36 @@
}
req.MonitoringEnabled = &monitoringValues

if tags, ok := d.GetOk("global_data_tags"); ok {
tagMap := tags.(map[string]interface{})

if len(tagMap) > 0 && serverVersion.LessThan(minVersionGlobalDataTags) {
return diag.FromErr(fmt.Errorf("'global_data_tags' is supported only for Elasticsearch v%s and above", minVersionGlobalDataTags.String()))
}

globalDataTags := []map[string]fleetapi.AgentPolicyUpdateRequest_GlobalDataTags_AdditionalProperties{}
for key, val := range tagMap {
var name, value fleetapi.AgentPolicyUpdateRequest_GlobalDataTags_AdditionalProperties
err := name.FromAgentPolicyUpdateRequestGlobalDataTags0(key)
if err != nil {
return diag.FromErr(err)
}

err = value.FromAgentPolicyUpdateRequestGlobalDataTags0(val.(string))
if err != nil {
return diag.FromErr(err)
}

globalDataTags = append(globalDataTags, map[string]fleetapi.AgentPolicyUpdateRequest_GlobalDataTags_AdditionalProperties{
"name": name,
"value": value,
})
}
req.GlobalDataTags = &globalDataTags
} else {
req.GlobalDataTags = &[]map[string]fleetapi.AgentPolicyUpdateRequest_GlobalDataTags_AdditionalProperties{} // Ensure it's an empty array
}

_, diags = fleet.UpdateAgentPolicy(ctx, fleetClient, d.Id(), req)
if diags.HasError() {
return diags
Expand Down Expand Up @@ -264,6 +350,31 @@
}
}

if agentPolicy.GlobalDataTags != nil {

globalDataTags := make(map[string]string, len(*agentPolicy.GlobalDataTags))
for _, tag := range *agentPolicy.GlobalDataTags {
name, err := tag["name"].AsAgentPolicyGlobalDataTags0()
if err != nil {
return diag.FromErr(err)
}

value, err := tag["value"].AsAgentPolicyGlobalDataTags0()
if err != nil {
return diag.FromErr(err)
}
globalDataTags[name] = value
}

if err := d.Set("global_data_tags", globalDataTags); err != nil {
return diag.FromErr(err)
}
} else {
if err := d.Set("global_data_tags", nil); err != nil {
return diag.FromErr(err)
}
}

return nil
}

Expand Down
123 changes: 123 additions & 0 deletions internal/fleet/agent_policy_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import (
)

var minVersionAgentPolicy = version.Must(version.NewVersion("8.6.0"))
var minVersionGlobalDataTags = version.Must(version.NewVersion("8.15.0"))

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

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
Expand Down Expand Up @@ -50,6 +52,51 @@ func TestAccResourceAgentPolicy(t *testing.T) {
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "skip_destroy", "false"),
),
},
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionGlobalDataTags),
Config: testAccResourceAgentPolicyCreateWithGlobalDataTags(policyNameGlobalDataTags, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "name", fmt.Sprintf("Policy %s", policyNameGlobalDataTags)),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "namespace", "default"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "description", "Test Agent Policy"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_logs", "true"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_metrics", "false"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "skip_destroy", "false"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag1", "value1"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag2", "value2"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag3", "value3"),
),
},
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionGlobalDataTags),
Config: testAccResourceAgentPolicyUpdateWithGlobalDataTags(policyNameGlobalDataTags, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "name", fmt.Sprintf("Updated Policy %s", policyNameGlobalDataTags)),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "namespace", "default"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "description", "This policy was updated"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_logs", "false"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_metrics", "true"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "skip_destroy", "false"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag1", "value1a"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag2", "value2b"),
resource.TestCheckNoResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag3"),
),
},
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionGlobalDataTags),
Config: testAccResourceAgentPolicyUpdateWithNoGlobalDataTags(policyNameGlobalDataTags, false),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "name", fmt.Sprintf("Updated Policy %s", policyNameGlobalDataTags)),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "namespace", "default"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "description", "This policy was updated without global data tags"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_logs", "false"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_metrics", "true"),
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "skip_destroy", "false"),
resource.TestCheckNoResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag1"),
resource.TestCheckNoResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag2"),
resource.TestCheckNoResourceAttr("elasticstack_fleet_agent_policy.test_policy", "global_data_tags.tag3"),
),
},
},
})
}
Expand Down Expand Up @@ -123,6 +170,82 @@ data "elasticstack_fleet_enrollment_tokens" "test_policy" {
`, fmt.Sprintf("Updated Policy %s", id), skipDestroy)
}

func testAccResourceAgentPolicyCreateWithGlobalDataTags(id string, skipDestroy bool) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
kibana {}
}

resource "elasticstack_fleet_agent_policy" "test_policy" {
name = "%s"
namespace = "default"
description = "Test Agent Policy"
monitor_logs = true
monitor_metrics = false
skip_destroy = %t
global_data_tags = {
tag1 = "value1"
tag2 = "value2"
tag3 = "value3"
}
}

data "elasticstack_fleet_enrollment_tokens" "test_policy" {
policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id
}

`, fmt.Sprintf("Policy %s", id), skipDestroy)
}

func testAccResourceAgentPolicyUpdateWithGlobalDataTags(id string, skipDestroy bool) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
kibana {}
}

resource "elasticstack_fleet_agent_policy" "test_policy" {
name = "%s"
namespace = "default"
description = "This policy was updated"
monitor_logs = false
monitor_metrics = true
skip_destroy = %t
global_data_tags = {
tag1 = "value1a"
tag2 = "value2b"
}
}

data "elasticstack_fleet_enrollment_tokens" "test_policy" {
policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id
}
`, fmt.Sprintf("Updated Policy %s", id), skipDestroy)
}

func testAccResourceAgentPolicyUpdateWithNoGlobalDataTags(id string, skipDestroy bool) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
kibana {}
}

resource "elasticstack_fleet_agent_policy" "test_policy" {
name = "%s"
namespace = "default"
description = "This policy was updated without global data tags"
monitor_logs = false
monitor_metrics = true
skip_destroy = %t
}

data "elasticstack_fleet_enrollment_tokens" "test_policy" {
policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id
}
`, fmt.Sprintf("Updated Policy %s", id), skipDestroy)
}

func checkResourceAgentPolicyDestroy(s *terraform.State) error {
client, err := clients.NewAcceptanceTestingClient()
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions internal/fleet/integration_policy_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

fleetapi "github.com/elastic/terraform-provider-elasticstack/generated/fleet"
"github.com/elastic/terraform-provider-elasticstack/internal/clients/fleet"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
)

func ResourceIntegrationPolicy() *schema.Resource {
Expand Down Expand Up @@ -135,7 +136,7 @@ func resourceIntegrationPolicyCreate(ctx context.Context, d *schema.ResourceData
}

req := fleetapi.CreatePackagePolicyJSONRequestBody{
PolicyId: d.Get("agent_policy_id").(string),
PolicyId: utils.Pointer(d.Get("agent_policy_id").(string)),
Name: d.Get("name").(string),
}
req.Package.Name = d.Get("integration_name").(string)
Expand Down Expand Up @@ -215,7 +216,7 @@ func resourceIntegrationPolicyUpdate(ctx context.Context, d *schema.ResourceData
}

req := fleetapi.UpdatePackagePolicyJSONRequestBody{
PolicyId: d.Get("agent_policy_id").(string),
PolicyId: utils.Pointer(d.Get("agent_policy_id").(string)),
Name: d.Get("name").(string),
}
req.Package.Name = d.Get("integration_name").(string)
Expand Down
Loading
Loading