Skip to content

Commit 6e165b3

Browse files
Zythertobio
andauthored
Issue667 - Add global_data_tags to fleet agent policies (#1044)
* devcontainer * schema and model to support global data tags * minimum version * tests * test cleanup * lint * schema description * docs * if datatags is nil * len not nil * reflect instead * ehh * deref * test mod * temp dev * temp dev2 * temp dev 3 * to jsonencode * docs * pointers * maybe json * 8.17.3 generate with new transform to combine global_data_tags schemas * amazing, types arent screaming and tests pass * changelog * seems like this breaks other things * using elastic/kibana 4f38cf96d24a66c98ac6dbaede2a9b92fa460b75 to generate kibana.gen.go * gen kbapi from github_ref=3757e641278a5186919e35a0f980ac3cda7e8ccd * fmt * schema * new docs * test mod * novalidate * its alive * passing tests * docs * devcontainer * schema and model to support global data tags * minimum version * tests * test cleanup * lint * schema description * docs * if datatags is nil * len not nil * reflect instead * ehh * deref * test mod * temp dev * temp dev2 * temp dev 3 * to jsonencode * docs * pointers * maybe json * 8.17.3 generate with new transform to combine global_data_tags schemas * amazing, types arent screaming and tests pass * changelog * seems like this breaks other things * using elastic/kibana 4f38cf96d24a66c98ac6dbaede2a9b92fa460b75 to generate kibana.gen.go * gen kbapi from github_ref=3757e641278a5186919e35a0f980ac3cda7e8ccd * fmt * schema * new docs * test mod * novalidate * its alive * passing tests * docs * requested changes * prep sync * changelog * separate test, passes * remove comment in test * fmt * test name change to create * remove RequiresReplace * readd * attempt computed, and UseStateForUnknown * maybe add a default * perhaps default without useState * send an empty global_data_tags on update * only for min ver and higher * check elements directly * datatags -- update test should test the removal of tag2 * test count * drying code * Fixup changelog --------- Co-authored-by: Toby Brain <[email protected]>
1 parent 07aa264 commit 6e165b3

File tree

12 files changed

+533
-178
lines changed

12 files changed

+533
-178
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## [Unreleased]
22

3+
- Add `global_data_tags` to fleet agent policies. ([#1044](https://github.com/elastic/terraform-provider-elasticstack/pull/1044))
4+
35
## [0.11.14] - 2025-03-17
46

57
- Fix a provider crash when interacting with elasticstack_kibana_data_view resources created with 0.11.0. ([#979](https://github.com/elastic/terraform-provider-elasticstack/pull/979))

docs/resources/fleet_agent_policy.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
2424
sys_monitoring = true
2525
monitor_logs = true
2626
monitor_metrics = true
27+
28+
global_data_tags = {
29+
first_tag = {
30+
string_value = "tag_value"
31+
},
32+
second_tag = {
33+
number_value = 1.2
34+
}
35+
}
2736
}
2837
```
2938

@@ -41,6 +50,7 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
4150
- `description` (String) The description of the agent policy.
4251
- `download_source_id` (String) The identifier for the Elastic Agent binary download server.
4352
- `fleet_server_host_id` (String) The identifier for the Fleet server host.
53+
- `global_data_tags` (Attributes Map) User-defined data tags to apply to all inputs. Values can be strings (string_value) or numbers (number_value) but not both. Example -- key1 = {string_value = value1}, key2 = {number_value = 42} (see [below for nested schema](#nestedatt--global_data_tags))
4454
- `monitor_logs` (Boolean) Enable collection of agent logs.
4555
- `monitor_metrics` (Boolean) Enable collection of agent metrics.
4656
- `monitoring_output_id` (String) The identifier for monitoring output.
@@ -52,6 +62,14 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
5262

5363
- `id` (String) The ID of this resource.
5464

65+
<a id="nestedatt--global_data_tags"></a>
66+
### Nested Schema for `global_data_tags`
67+
68+
Optional:
69+
70+
- `number_value` (Number) Number value for the field. If this is set, string_value must not be defined.
71+
- `string_value` (String) String value for the field. If this is set, number_value must not be defined.
72+
5573
## Import
5674

5775
Import is supported using the following syntax:

examples/resources/elasticstack_fleet_agent_policy/resource.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,13 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
99
sys_monitoring = true
1010
monitor_logs = true
1111
monitor_metrics = true
12+
13+
global_data_tags = {
14+
first_tag = {
15+
string_value = "tag_value"
16+
},
17+
second_tag = {
18+
number_value = 1.2
19+
}
20+
}
1221
}

generated/kbapi/kibana.gen.go

Lines changed: 73 additions & 98 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated/kbapi/transform_schema.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,12 @@ func transformFleetPaths(schema *Schema) {
838838
agentPolicyPath.Put.Set(fmt.Sprintf("requestBody.content.application/json.schema.properties.%s.x-omitempty", key), true)
839839
}
840840

841+
schema.Components.CreateRef(schema, "agent_policy_global_data_tags_item", "schemas.agent_policy.properties.global_data_tags.items")
842+
843+
// Define the value types for the GlobalDataTags
844+
agentPoliciesPath.Post.Set("requestBody.content.application/json.schema.properties.global_data_tags.items.$ref", "#/components/schemas/agent_policy_global_data_tags_item")
845+
agentPolicyPath.Put.Set("requestBody.content.application/json.schema.properties.global_data_tags.items.$ref", "#/components/schemas/agent_policy_global_data_tags_item")
846+
841847
// Enrollment api keys
842848
// https://github.com/elastic/kibana/blob/main/x-pack/plugins/fleet/common/types/models/enrollment_api_key.ts
843849
// https://github.com/elastic/kibana/blob/main/x-pack/plugins/fleet/common/types/rest_spec/enrollment_api_key.ts

internal/fleet/agent_policy/create.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,16 @@ func (r *agentPolicyResource) Create(ctx context.Context, req resource.CreateReq
2222
return
2323
}
2424

25-
body := planModel.toAPICreateModel()
25+
sVersion, e := r.client.ServerVersion(ctx)
26+
if e != nil {
27+
return
28+
}
29+
30+
body, diags := planModel.toAPICreateModel(ctx, sVersion)
31+
resp.Diagnostics.Append(diags...)
32+
if resp.Diagnostics.HasError() {
33+
return
34+
}
2635

2736
sysMonitoring := planModel.SysMonitoring.ValueBool()
2837
policy, diags := fleet.CreateAgentPolicy(ctx, client, body, sysMonitoring)
@@ -31,7 +40,11 @@ func (r *agentPolicyResource) Create(ctx context.Context, req resource.CreateReq
3140
return
3241
}
3342

34-
planModel.populateFromAPI(policy)
43+
diags = planModel.populateFromAPI(ctx, policy)
44+
resp.Diagnostics.Append(diags...)
45+
if resp.Diagnostics.HasError() {
46+
return
47+
}
3548

3649
resp.State.Set(ctx, planModel)
3750
resp.Diagnostics.Append(diags...)

internal/fleet/agent_policy/models.go

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
package agent_policy
22

33
import (
4+
"context"
5+
"fmt"
46
"slices"
57

68
"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
79
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
10+
11+
"github.com/hashicorp/go-version"
12+
"github.com/hashicorp/terraform-plugin-framework/attr"
13+
"github.com/hashicorp/terraform-plugin-framework/diag"
14+
"github.com/hashicorp/terraform-plugin-framework/path"
815
"github.com/hashicorp/terraform-plugin-framework/types"
916
)
1017

18+
type globalDataTagsItemModel struct {
19+
StringValue types.String `tfsdk:"string_value"`
20+
NumberValue types.Float32 `tfsdk:"number_value"`
21+
}
22+
1123
type agentPolicyModel struct {
1224
ID types.String `tfsdk:"id"`
1325
PolicyID types.String `tfsdk:"policy_id"`
@@ -22,11 +34,12 @@ type agentPolicyModel struct {
2234
MonitorMetrics types.Bool `tfsdk:"monitor_metrics"`
2335
SysMonitoring types.Bool `tfsdk:"sys_monitoring"`
2436
SkipDestroy types.Bool `tfsdk:"skip_destroy"`
37+
GlobalDataTags types.Map `tfsdk:"global_data_tags"` //> globalDataTagsModel
2538
}
2639

27-
func (model *agentPolicyModel) populateFromAPI(data *kbapi.AgentPolicy) {
40+
func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.AgentPolicy) diag.Diagnostics {
2841
if data == nil {
29-
return
42+
return nil
3043
}
3144

3245
model.ID = types.StringValue(data.Id)
@@ -47,17 +60,95 @@ func (model *agentPolicyModel) populateFromAPI(data *kbapi.AgentPolicy) {
4760
if !utils.IsKnown(model.MonitorLogs) {
4861
model.MonitorLogs = types.BoolValue(false)
4962
}
50-
if !utils.IsKnown(model.MonitorLogs) {
51-
model.MonitorLogs = types.BoolValue(false)
63+
if !utils.IsKnown(model.MonitorMetrics) {
64+
model.MonitorMetrics = types.BoolValue(false)
5265
}
5366

5467
model.MonitoringOutputId = types.StringPointerValue(data.MonitoringOutputId)
5568
model.Name = types.StringValue(data.Name)
5669
model.Namespace = types.StringValue(data.Namespace)
70+
if utils.Deref(data.GlobalDataTags) != nil {
71+
diags := diag.Diagnostics{}
72+
var map0 = make(map[string]globalDataTagsItemModel)
73+
for _, v := range utils.Deref(data.GlobalDataTags) {
74+
maybeFloat, error := v.Value.AsAgentPolicyGlobalDataTagsItemValue1()
75+
if error != nil {
76+
maybeString, error := v.Value.AsAgentPolicyGlobalDataTagsItemValue0()
77+
if error != nil {
78+
diags.AddError("Failed to unmarshal global data tags", error.Error())
79+
}
80+
map0[v.Name] = globalDataTagsItemModel{
81+
StringValue: types.StringValue(maybeString),
82+
}
83+
} else {
84+
map0[v.Name] = globalDataTagsItemModel{
85+
NumberValue: types.Float32Value(float32(maybeFloat)),
86+
}
87+
}
88+
}
89+
90+
model.GlobalDataTags = utils.MapValueFrom(ctx, map0, getGlobalDataTagsAttrTypes().(attr.TypeWithElementType).ElementType(), path.Root("global_data_tags"), &diags)
91+
if diags.HasError() {
92+
return diags
93+
}
94+
95+
}
96+
97+
return nil
5798
}
5899

59-
func (model agentPolicyModel) toAPICreateModel() kbapi.PostFleetAgentPoliciesJSONRequestBody {
100+
// convertGlobalDataTags converts the global data tags from terraform model to API model
101+
// and performs version validation
102+
func (model *agentPolicyModel) convertGlobalDataTags(ctx context.Context, serverVersion *version.Version) (*[]kbapi.AgentPolicyGlobalDataTagsItem, diag.Diagnostics) {
103+
var diags diag.Diagnostics
104+
105+
if len(model.GlobalDataTags.Elements()) == 0 {
106+
if serverVersion.GreaterThanOrEqual(MinVersionGlobalDataTags) {
107+
emptyList := make([]kbapi.AgentPolicyGlobalDataTagsItem, 0)
108+
return &emptyList, diags
109+
}
110+
return nil, diags
111+
}
112+
113+
if serverVersion.LessThan(MinVersionGlobalDataTags) {
114+
diags.AddError("global_data_tags ES version error", fmt.Sprintf("Global data tags are only supported in Elastic Stack %s and above", MinVersionGlobalDataTags))
115+
return nil, diags
116+
}
117+
118+
items := utils.MapTypeToMap(ctx, model.GlobalDataTags, path.Root("global_data_tags"), &diags,
119+
func(item globalDataTagsItemModel, meta utils.MapMeta) kbapi.AgentPolicyGlobalDataTagsItem {
120+
var value kbapi.AgentPolicyGlobalDataTagsItem_Value
121+
var err error
122+
if item.StringValue.ValueStringPointer() != nil {
123+
err = value.FromAgentPolicyGlobalDataTagsItemValue0(*item.StringValue.ValueStringPointer())
124+
} else {
125+
err = value.FromAgentPolicyGlobalDataTagsItemValue1(*item.NumberValue.ValueFloat32Pointer())
126+
}
127+
if err != nil {
128+
diags.AddError("global_data_tags validation_error_converting_values", err.Error())
129+
return kbapi.AgentPolicyGlobalDataTagsItem{}
130+
}
131+
return kbapi.AgentPolicyGlobalDataTagsItem{
132+
Name: meta.Key,
133+
Value: value,
134+
}
135+
})
136+
137+
if diags.HasError() {
138+
return nil, diags
139+
}
140+
141+
itemsList := make([]kbapi.AgentPolicyGlobalDataTagsItem, 0, len(items))
142+
for _, v := range items {
143+
itemsList = append(itemsList, v)
144+
}
145+
146+
return &itemsList, diags
147+
}
148+
149+
func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, serverVersion *version.Version) (kbapi.PostFleetAgentPoliciesJSONRequestBody, diag.Diagnostics) {
60150
monitoring := make([]kbapi.PostFleetAgentPoliciesJSONBodyMonitoringEnabled, 0, 2)
151+
61152
if model.MonitorLogs.ValueBool() {
62153
monitoring = append(monitoring, kbapi.PostFleetAgentPoliciesJSONBodyMonitoringEnabledLogs)
63154
}
@@ -77,10 +168,16 @@ func (model agentPolicyModel) toAPICreateModel() kbapi.PostFleetAgentPoliciesJSO
77168
Namespace: model.Namespace.ValueString(),
78169
}
79170

80-
return body
171+
tags, diags := model.convertGlobalDataTags(ctx, serverVersion)
172+
if diags.HasError() {
173+
return kbapi.PostFleetAgentPoliciesJSONRequestBody{}, diags
174+
}
175+
body.GlobalDataTags = tags
176+
177+
return body, nil
81178
}
82179

83-
func (model agentPolicyModel) toAPIUpdateModel() kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody {
180+
func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, serverVersion *version.Version) (kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody, diag.Diagnostics) {
84181
monitoring := make([]kbapi.PutFleetAgentPoliciesAgentpolicyidJSONBodyMonitoringEnabled, 0, 2)
85182
if model.MonitorLogs.ValueBool() {
86183
monitoring = append(monitoring, kbapi.Logs)
@@ -100,5 +197,11 @@ func (model agentPolicyModel) toAPIUpdateModel() kbapi.PutFleetAgentPoliciesAgen
100197
Namespace: model.Namespace.ValueString(),
101198
}
102199

103-
return body
200+
tags, diags := model.convertGlobalDataTags(ctx, serverVersion)
201+
if diags.HasError() {
202+
return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{}, diags
203+
}
204+
body.GlobalDataTags = tags
205+
206+
return body, nil
104207
}

internal/fleet/agent_policy/read.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ func (r *agentPolicyResource) Read(ctx context.Context, req resource.ReadRequest
3434
return
3535
}
3636

37-
stateModel.populateFromAPI(policy)
37+
diags = stateModel.populateFromAPI(ctx, policy)
38+
resp.Diagnostics.Append(diags...)
39+
if resp.Diagnostics.HasError() {
40+
return
41+
}
3842

3943
resp.State.Set(ctx, stateModel)
4044
resp.Diagnostics.Append(diags...)

internal/fleet/agent_policy/resource.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
8+
"github.com/hashicorp/go-version"
89
"github.com/hashicorp/terraform-plugin-framework/path"
910
"github.com/hashicorp/terraform-plugin-framework/resource"
1011
)
@@ -15,6 +16,8 @@ var (
1516
_ resource.ResourceWithImportState = &agentPolicyResource{}
1617
)
1718

19+
var MinVersionGlobalDataTags = version.Must(version.NewVersion("8.15.0"))
20+
1821
// NewResource is a helper function to simplify the provider implementation.
1922
func NewResource() resource.Resource {
2023
return &agentPolicyResource{}

0 commit comments

Comments
 (0)