Skip to content

Commit 68dfcdd

Browse files
authored
Support the supports_agentless field in Fleet agent policies. (#1197)
* Add supports_agentless parameter * make docs-generate * Reject attempts to set supports_agentless when not supported * Changelog * Changelog
1 parent 6dea22c commit 68dfcdd

File tree

12 files changed

+175
-25
lines changed

12 files changed

+175
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
- Add support for `timeslice_metric_indicator` in `elasticstack_kibana_slo` ([#1195](https://github.com/elastic/terraform-provider-elasticstack/pull/1195))
44
- Add `elasticstack_elasticsearch_ingest_processor_reroute` data source ([#678](https://github.com/elastic/terraform-provider-elasticstack/issues/678))
5+
- Add support for `supports_agentless` to `elasticstack_fleet_agent_policy` ([#1197](https://github.com/elastic/terraform-provider-elasticstack/pull/1197))
6+
- Ignore `master_timeout` when targeting Serverless projects ([#1207](https://github.com/elastic/terraform-provider-elasticstack/pull/1207))
57

68
## [0.11.16] - 2025-07-09
79

docs/resources/fleet_agent_policy.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
5656
- `monitoring_output_id` (String) The identifier for monitoring output.
5757
- `policy_id` (String) Unique identifier of the agent policy.
5858
- `skip_destroy` (Boolean) Set to true if you do not wish the agent policy to be deleted at destroy time, and instead just remove the agent policy from the Terraform state.
59+
- `supports_agentless` (Boolean) Set to true to enable agentless data collection.
5960
- `sys_monitoring` (Boolean) Enable collection of system logs and metrics.
6061

6162
### Read-Only

internal/clients/api_client.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ type CompositeId struct {
3333
ResourceId string
3434
}
3535

36+
const ServerlessFlavor = "serverless"
37+
3638
func CompositeIdFromStr(id string) (*CompositeId, diag.Diagnostics) {
3739
var diags diag.Diagnostics
3840
idParts := strings.Split(id, "/")
@@ -352,6 +354,24 @@ func (a *ApiClient) serverInfo(ctx context.Context) (*models.ClusterInfo, diag.D
352354
return &info, diags
353355
}
354356

357+
func (a *ApiClient) EnforceMinVersion(ctx context.Context, minVersion *version.Version) (bool, diag.Diagnostics) {
358+
flavor, diags := a.ServerFlavor(ctx)
359+
if diags.HasError() {
360+
return false, diags
361+
}
362+
363+
if flavor == ServerlessFlavor {
364+
return true, nil
365+
}
366+
367+
serverVersion, diags := a.ServerVersion(ctx)
368+
if diags.HasError() {
369+
return false, diags
370+
}
371+
372+
return serverVersion.GreaterThanOrEqual(minVersion), nil
373+
}
374+
355375
func (a *ApiClient) ServerVersion(ctx context.Context) (*version.Version, diag.Diagnostics) {
356376
if a.elasticsearch != nil {
357377
return a.versionFromElasticsearch(ctx)

internal/elasticsearch/index/index/models.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ func (model tfModel) toPutIndexParams(serverFlavor string) models.PutIndexParams
289289
Timeout: timeout,
290290
}
291291

292-
if serverFlavor != "serverless" {
292+
if serverFlavor != clients.ServerlessFlavor {
293293
params.MasterTimeout = masterTimeout
294294
params.WaitForActiveShards = model.WaitForActiveShards.ValueString()
295295
}

internal/elasticsearch/index/index/models_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
fuzz "github.com/google/gofuzz"
99

10+
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
1011
"github.com/elastic/terraform-provider-elasticstack/internal/models"
1112
"github.com/elastic/terraform-provider-elasticstack/internal/utils/customtypes"
1213
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
@@ -363,7 +364,7 @@ func Test_tfModel_toPutIndexParams(t *testing.T) {
363364

364365
flavor := "not_serverless"
365366
if isServerless {
366-
flavor = "serverless"
367+
flavor = clients.ServerlessFlavor
367368
expectedParams.WaitForActiveShards = ""
368369
expectedParams.MasterTimeout = 0
369370
}

internal/fleet/agent_policy/create.go

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

25-
sVersion, e := r.client.ServerVersion(ctx)
26-
if e != nil {
25+
feat, diags := r.buildFeatures(ctx)
26+
resp.Diagnostics.Append(diags...)
27+
if resp.Diagnostics.HasError() {
2728
return
2829
}
2930

30-
body, diags := planModel.toAPICreateModel(ctx, sVersion)
31+
body, diags := planModel.toAPICreateModel(ctx, feat)
3132
resp.Diagnostics.Append(diags...)
3233
if resp.Diagnostics.HasError() {
3334
return

internal/fleet/agent_policy/models.go

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@ import (
88
"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
99
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
1010

11-
"github.com/hashicorp/go-version"
1211
"github.com/hashicorp/terraform-plugin-framework/attr"
1312
"github.com/hashicorp/terraform-plugin-framework/diag"
1413
"github.com/hashicorp/terraform-plugin-framework/path"
1514
"github.com/hashicorp/terraform-plugin-framework/types"
1615
)
1716

17+
type features struct {
18+
SupportsGlobalDataTags bool
19+
SupportsSupportsAgentless bool
20+
}
21+
1822
type globalDataTagsItemModel struct {
1923
StringValue types.String `tfsdk:"string_value"`
2024
NumberValue types.Float32 `tfsdk:"number_value"`
@@ -34,6 +38,7 @@ type agentPolicyModel struct {
3438
MonitorMetrics types.Bool `tfsdk:"monitor_metrics"`
3539
SysMonitoring types.Bool `tfsdk:"sys_monitoring"`
3640
SkipDestroy types.Bool `tfsdk:"skip_destroy"`
41+
SupportsAgentless types.Bool `tfsdk:"supports_agentless"`
3742
GlobalDataTags types.Map `tfsdk:"global_data_tags"` //> globalDataTagsModel
3843
}
3944

@@ -67,6 +72,7 @@ func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.
6772
model.MonitoringOutputId = types.StringPointerValue(data.MonitoringOutputId)
6873
model.Name = types.StringValue(data.Name)
6974
model.Namespace = types.StringValue(data.Namespace)
75+
model.SupportsAgentless = types.BoolPointerValue(data.SupportsAgentless)
7076
if utils.Deref(data.GlobalDataTags) != nil {
7177
diags := diag.Diagnostics{}
7278
var map0 = make(map[string]globalDataTagsItemModel)
@@ -99,18 +105,18 @@ func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.
99105

100106
// convertGlobalDataTags converts the global data tags from terraform model to API model
101107
// and performs version validation
102-
func (model *agentPolicyModel) convertGlobalDataTags(ctx context.Context, serverVersion *version.Version) (*[]kbapi.AgentPolicyGlobalDataTagsItem, diag.Diagnostics) {
108+
func (model *agentPolicyModel) convertGlobalDataTags(ctx context.Context, feat features) (*[]kbapi.AgentPolicyGlobalDataTagsItem, diag.Diagnostics) {
103109
var diags diag.Diagnostics
104110

105111
if len(model.GlobalDataTags.Elements()) == 0 {
106-
if serverVersion.GreaterThanOrEqual(MinVersionGlobalDataTags) {
112+
if feat.SupportsGlobalDataTags {
107113
emptyList := make([]kbapi.AgentPolicyGlobalDataTagsItem, 0)
108114
return &emptyList, diags
109115
}
110116
return nil, diags
111117
}
112118

113-
if serverVersion.LessThan(MinVersionGlobalDataTags) {
119+
if !feat.SupportsGlobalDataTags {
114120
diags.AddError("global_data_tags ES version error", fmt.Sprintf("Global data tags are only supported in Elastic Stack %s and above", MinVersionGlobalDataTags))
115121
return nil, diags
116122
}
@@ -146,7 +152,7 @@ func (model *agentPolicyModel) convertGlobalDataTags(ctx context.Context, server
146152
return &itemsList, diags
147153
}
148154

149-
func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, serverVersion *version.Version) (kbapi.PostFleetAgentPoliciesJSONRequestBody, diag.Diagnostics) {
155+
func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, feat features) (kbapi.PostFleetAgentPoliciesJSONRequestBody, diag.Diagnostics) {
150156
monitoring := make([]kbapi.PostFleetAgentPoliciesJSONBodyMonitoringEnabled, 0, 2)
151157

152158
if model.MonitorLogs.ValueBool() {
@@ -156,6 +162,16 @@ func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, serverVersi
156162
monitoring = append(monitoring, kbapi.PostFleetAgentPoliciesJSONBodyMonitoringEnabledMetrics)
157163
}
158164

165+
if utils.IsKnown(model.SupportsAgentless) && !feat.SupportsSupportsAgentless {
166+
return kbapi.PostFleetAgentPoliciesJSONRequestBody{}, diag.Diagnostics{
167+
diag.NewAttributeErrorDiagnostic(
168+
path.Root("supports_agentless"),
169+
"Unsupported Elasticsearch version",
170+
fmt.Sprintf("Supports agentless is only supported in Elastic Stack %s and above", MinSupportsAgentlessVersion),
171+
),
172+
}
173+
}
174+
159175
body := kbapi.PostFleetAgentPoliciesJSONRequestBody{
160176
DataOutputId: model.DataOutputId.ValueStringPointer(),
161177
Description: model.Description.ValueStringPointer(),
@@ -166,9 +182,10 @@ func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, serverVersi
166182
MonitoringOutputId: model.MonitoringOutputId.ValueStringPointer(),
167183
Name: model.Name.ValueString(),
168184
Namespace: model.Namespace.ValueString(),
185+
SupportsAgentless: model.SupportsAgentless.ValueBoolPointer(),
169186
}
170187

171-
tags, diags := model.convertGlobalDataTags(ctx, serverVersion)
188+
tags, diags := model.convertGlobalDataTags(ctx, feat)
172189
if diags.HasError() {
173190
return kbapi.PostFleetAgentPoliciesJSONRequestBody{}, diags
174191
}
@@ -177,7 +194,7 @@ func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, serverVersi
177194
return body, nil
178195
}
179196

180-
func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, serverVersion *version.Version) (kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody, diag.Diagnostics) {
197+
func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, feat features) (kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody, diag.Diagnostics) {
181198
monitoring := make([]kbapi.PutFleetAgentPoliciesAgentpolicyidJSONBodyMonitoringEnabled, 0, 2)
182199
if model.MonitorLogs.ValueBool() {
183200
monitoring = append(monitoring, kbapi.Logs)
@@ -186,6 +203,16 @@ func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, serverVersi
186203
monitoring = append(monitoring, kbapi.Metrics)
187204
}
188205

206+
if utils.IsKnown(model.SupportsAgentless) && !feat.SupportsSupportsAgentless {
207+
return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{}, diag.Diagnostics{
208+
diag.NewAttributeErrorDiagnostic(
209+
path.Root("supports_agentless"),
210+
"Unsupported Elasticsearch version",
211+
fmt.Sprintf("Supports agentless is only supported in Elastic Stack %s and above", MinSupportsAgentlessVersion),
212+
),
213+
}
214+
}
215+
189216
body := kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{
190217
DataOutputId: model.DataOutputId.ValueStringPointer(),
191218
Description: model.Description.ValueStringPointer(),
@@ -195,9 +222,10 @@ func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, serverVersi
195222
MonitoringOutputId: model.MonitoringOutputId.ValueStringPointer(),
196223
Name: model.Name.ValueString(),
197224
Namespace: model.Namespace.ValueString(),
225+
SupportsAgentless: model.SupportsAgentless.ValueBoolPointer(),
198226
}
199227

200-
tags, diags := model.convertGlobalDataTags(ctx, serverVersion)
228+
tags, diags := model.convertGlobalDataTags(ctx, feat)
201229
if diags.HasError() {
202230
return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{}, diags
203231
}

internal/fleet/agent_policy/resource.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"fmt"
66

77
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
8+
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
89
"github.com/hashicorp/go-version"
10+
"github.com/hashicorp/terraform-plugin-framework/diag"
911
"github.com/hashicorp/terraform-plugin-framework/path"
1012
"github.com/hashicorp/terraform-plugin-framework/resource"
1113
)
@@ -16,7 +18,10 @@ var (
1618
_ resource.ResourceWithImportState = &agentPolicyResource{}
1719
)
1820

19-
var MinVersionGlobalDataTags = version.Must(version.NewVersion("8.15.0"))
21+
var (
22+
MinVersionGlobalDataTags = version.Must(version.NewVersion("8.15.0"))
23+
MinSupportsAgentlessVersion = version.Must(version.NewVersion("8.15.0"))
24+
)
2025

2126
// NewResource is a helper function to simplify the provider implementation.
2227
func NewResource() resource.Resource {
@@ -40,3 +45,20 @@ func (r *agentPolicyResource) Metadata(ctx context.Context, req resource.Metadat
4045
func (r *agentPolicyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
4146
resource.ImportStatePassthroughID(ctx, path.Root("policy_id"), req, resp)
4247
}
48+
49+
func (r *agentPolicyResource) buildFeatures(ctx context.Context) (features, diag.Diagnostics) {
50+
supportsGDT, diags := r.client.EnforceMinVersion(ctx, MinVersionGlobalDataTags)
51+
if diags.HasError() {
52+
return features{}, utils.FrameworkDiagsFromSDK(diags)
53+
}
54+
55+
supportsSupportsAgentless, diags := r.client.EnforceMinVersion(ctx, MinSupportsAgentlessVersion)
56+
if diags.HasError() {
57+
return features{}, utils.FrameworkDiagsFromSDK(diags)
58+
}
59+
60+
return features{
61+
SupportsGlobalDataTags: supportsGDT,
62+
SupportsSupportsAgentless: supportsSupportsAgentless,
63+
}, nil
64+
}

0 commit comments

Comments
 (0)