@@ -17,13 +17,29 @@ import (
1717 "github.com/hashicorp/terraform-plugin-framework/types"
1818)
1919
20+ const (
21+ // HostNameFormatHostname represents the short hostname format (e.g., "myhost")
22+ HostNameFormatHostname = "hostname"
23+ // HostNameFormatFQDN represents the fully qualified domain name format (e.g., "myhost.example.com")
24+ HostNameFormatFQDN = "fqdn"
25+ // agentFeatureFQDN is the name of the agent feature that enables FQDN host name format
26+ agentFeatureFQDN = "fqdn"
27+ )
28+
29+ // apiAgentFeature is the type expected by the generated API for agent features
30+ type apiAgentFeature = struct {
31+ Enabled bool `json:"enabled"`
32+ Name string `json:"name"`
33+ }
34+
2035type features struct {
2136 SupportsGlobalDataTags bool
2237 SupportsSupportsAgentless bool
2338 SupportsInactivityTimeout bool
2439 SupportsUnenrollmentTimeout bool
2540 SupportsSpaceIds bool
2641 SupportsRequiredVersions bool
42+ SupportsAgentFeatures bool
2743}
2844
2945type globalDataTagsItemModel struct {
@@ -45,6 +61,7 @@ type agentPolicyModel struct {
4561 MonitorMetrics types.Bool `tfsdk:"monitor_metrics"`
4662 SysMonitoring types.Bool `tfsdk:"sys_monitoring"`
4763 SkipDestroy types.Bool `tfsdk:"skip_destroy"`
64+ HostNameFormat types.String `tfsdk:"host_name_format"`
4865 SupportsAgentless types.Bool `tfsdk:"supports_agentless"`
4966 InactivityTimeout customtypes.Duration `tfsdk:"inactivity_timeout"`
5067 UnenrollmentTimeout customtypes.Duration `tfsdk:"unenrollment_timeout"`
@@ -84,6 +101,20 @@ func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.
84101 model .Name = types .StringValue (data .Name )
85102 model .Namespace = types .StringValue (data .Namespace )
86103 model .SupportsAgentless = types .BoolPointerValue (data .SupportsAgentless )
104+
105+ // Determine host_name_format from AgentFeatures
106+ // If AgentFeatures contains {"enabled": true, "name": "fqdn"}, then host_name_format is "fqdn"
107+ // Otherwise, it defaults to "hostname"
108+ model .HostNameFormat = types .StringValue (HostNameFormatHostname )
109+ if data .AgentFeatures != nil {
110+ for _ , feature := range * data .AgentFeatures {
111+ if feature .Name == agentFeatureFQDN && feature .Enabled {
112+ model .HostNameFormat = types .StringValue (HostNameFormatFQDN )
113+ break
114+ }
115+ }
116+ }
117+
87118 if data .InactivityTimeout != nil {
88119 // Convert seconds to duration string
89120 seconds := int64 (* data .InactivityTimeout )
@@ -377,10 +408,30 @@ func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, feat featur
377408 }
378409 body .RequiredVersions = requiredVersions
379410
411+ // Handle host_name_format via AgentFeatures
412+ if agentFeature := model .convertHostNameFormatToAgentFeature (); agentFeature != nil {
413+ if ! feat .SupportsAgentFeatures {
414+ // Only error if user explicitly requests FQDN on unsupported version
415+ // Default "hostname" is fine - just don't send agent_features
416+ if agentFeature .Enabled {
417+ return kbapi.PostFleetAgentPoliciesJSONRequestBody {}, diag.Diagnostics {
418+ diag .NewAttributeErrorDiagnostic (
419+ path .Root ("host_name_format" ),
420+ "Unsupported Elasticsearch version" ,
421+ fmt .Sprintf ("host_name_format (agent_features) is only supported in Elastic Stack %s and above" , MinVersionAgentFeatures ),
422+ ),
423+ }
424+ }
425+ // On unsupported version with default "hostname", don't send agent_features
426+ } else {
427+ body .AgentFeatures = & []apiAgentFeature {* agentFeature }
428+ }
429+ }
430+
380431 return body , nil
381432}
382433
383- func (model * agentPolicyModel ) toAPIUpdateModel (ctx context.Context , feat features ) (kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody , diag.Diagnostics ) {
434+ func (model * agentPolicyModel ) toAPIUpdateModel (ctx context.Context , feat features , existingFeatures [] apiAgentFeature ) (kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody , diag.Diagnostics ) {
384435 monitoring := make ([]kbapi.PutFleetAgentPoliciesAgentpolicyidJSONBodyMonitoringEnabled , 0 , 2 )
385436 if model .MonitorLogs .ValueBool () {
386437 monitoring = append (monitoring , kbapi .PutFleetAgentPoliciesAgentpolicyidJSONBodyMonitoringEnabledLogs )
@@ -481,5 +532,76 @@ func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, feat featur
481532 }
482533 body .RequiredVersions = requiredVersions
483534
535+ // Handle host_name_format via AgentFeatures, preserving other existing features
536+ if agentFeature := model .convertHostNameFormatToAgentFeature (); agentFeature != nil {
537+ if ! feat .SupportsAgentFeatures {
538+ // Only error if user explicitly requests FQDN on unsupported version
539+ // Default "hostname" is fine - just don't send agent_features
540+ if agentFeature .Enabled {
541+ return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody {}, diag.Diagnostics {
542+ diag .NewAttributeErrorDiagnostic (
543+ path .Root ("host_name_format" ),
544+ "Unsupported Elasticsearch version" ,
545+ fmt .Sprintf ("host_name_format (agent_features) is only supported in Elastic Stack %s and above" , MinVersionAgentFeatures ),
546+ ),
547+ }
548+ }
549+ // On unsupported version with default "hostname", don't send agent_features
550+ } else {
551+ body .AgentFeatures = mergeAgentFeature (existingFeatures , agentFeature )
552+ }
553+ } else if feat .SupportsAgentFeatures && len (existingFeatures ) > 0 {
554+ // Preserve existing features even when host_name_format is not set
555+ body .AgentFeatures = & existingFeatures
556+ }
557+
484558 return body , nil
485559}
560+
561+ // convertHostNameFormatToAgentFeature converts the host_name_format field to a single AgentFeature.
562+ // - When host_name_format is "fqdn": returns {"name": "fqdn", "enabled": true}
563+ // - When host_name_format is "hostname": returns {"name": "fqdn", "enabled": false} to explicitly disable
564+ // - When not set: returns nil (no change to existing features)
565+ func (model * agentPolicyModel ) convertHostNameFormatToAgentFeature () * apiAgentFeature {
566+ // If host_name_format is not set or unknown, don't modify AgentFeatures
567+ if model .HostNameFormat .IsNull () || model .HostNameFormat .IsUnknown () {
568+ return nil
569+ }
570+
571+ // Explicitly set enabled based on the host_name_format value
572+ // We need to send enabled: false when hostname is selected to override any existing fqdn setting
573+ return & apiAgentFeature {
574+ Enabled : model .HostNameFormat .ValueString () == HostNameFormatFQDN ,
575+ Name : agentFeatureFQDN ,
576+ }
577+ }
578+
579+ // mergeAgentFeature merges a single feature into existing features, replacing any feature with the same name.
580+ // If newFeature is nil, returns existing features unchanged (nil if existing is empty).
581+ func mergeAgentFeature (existing []apiAgentFeature , newFeature * apiAgentFeature ) * []apiAgentFeature {
582+ if newFeature == nil {
583+ if len (existing ) == 0 {
584+ return nil
585+ }
586+ return & existing
587+ }
588+
589+ // Check if the feature already exists and replace it, otherwise append
590+ result := make ([]apiAgentFeature , 0 , len (existing )+ 1 )
591+ found := false
592+
593+ for _ , f := range existing {
594+ if f .Name == newFeature .Name {
595+ result = append (result , * newFeature )
596+ found = true
597+ } else {
598+ result = append (result , f )
599+ }
600+ }
601+
602+ if ! found {
603+ result = append (result , * newFeature )
604+ }
605+
606+ return & result
607+ }
0 commit comments