-
Notifications
You must be signed in to change notification settings - Fork 123
Add required_versions attribute to fleet_agent_policy for automatic agent upgrades #1436
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
base: main
Are you sure you want to change the base?
Changes from 10 commits
fcc606e
668e370
bc8388e
7a36730
8f05705
487e591
e858db4
dc3a938
42771fc
ab8f8ab
fc97622
ec49d5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,7 @@ type features struct { | |||||||||||
| SupportsInactivityTimeout bool | ||||||||||||
| SupportsUnenrollmentTimeout bool | ||||||||||||
| SupportsSpaceIds bool | ||||||||||||
| SupportsRequiredVersions bool | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| type globalDataTagsItemModel struct { | ||||||||||||
|
|
@@ -48,6 +49,7 @@ type agentPolicyModel struct { | |||||||||||
| UnenrollmentTimeout customtypes.Duration `tfsdk:"unenrollment_timeout"` | ||||||||||||
| GlobalDataTags types.Map `tfsdk:"global_data_tags"` //> globalDataTagsModel | ||||||||||||
| SpaceIds types.Set `tfsdk:"space_ids"` | ||||||||||||
| RequiredVersions types.Map `tfsdk:"required_versions"` | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.AgentPolicy) diag.Diagnostics { | ||||||||||||
|
|
@@ -134,6 +136,25 @@ func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi. | |||||||||||
| model.SpaceIds = types.SetNull(types.StringType) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Handle required_versions | ||||||||||||
| if data.RequiredVersions != nil { | ||||||||||||
| versionMap := make(map[string]attr.Value) | ||||||||||||
|
|
||||||||||||
| for _, rv := range *data.RequiredVersions { | ||||||||||||
| // Round the float32 percentage to nearest integer since we use Int32 in the schema | ||||||||||||
| percentage := int32(rv.Percentage + 0.5) | ||||||||||||
tobio marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
| versionMap[rv.Version] = types.Int32Value(percentage) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| reqVersions, d := types.MapValue(types.Int32Type, versionMap) | ||||||||||||
| if d.HasError() { | ||||||||||||
| return d | ||||||||||||
| } | ||||||||||||
| model.RequiredVersions = reqVersions | ||||||||||||
| } else { | ||||||||||||
| model.RequiredVersions = types.MapNull(types.Int32Type) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -186,6 +207,72 @@ func (model *agentPolicyModel) convertGlobalDataTags(ctx context.Context, feat f | |||||||||||
| return &itemsList, diags | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // convertRequiredVersions converts the required versions from terraform model to API model | ||||||||||||
| func (model *agentPolicyModel) convertRequiredVersions(ctx context.Context, feat features) (*[]struct { | ||||||||||||
| Percentage float32 `json:"percentage"` | ||||||||||||
| Version string `json:"version"` | ||||||||||||
| }, diag.Diagnostics) { | ||||||||||||
| var diags diag.Diagnostics | ||||||||||||
|
|
||||||||||||
| if model.RequiredVersions.IsNull() || model.RequiredVersions.IsUnknown() { | ||||||||||||
| return nil, diags | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Check if required_versions is supported | ||||||||||||
| if !feat.SupportsRequiredVersions { | ||||||||||||
| return nil, diag.Diagnostics{ | ||||||||||||
| diag.NewAttributeErrorDiagnostic( | ||||||||||||
| path.Root("required_versions"), | ||||||||||||
| "Unsupported Elasticsearch version", | ||||||||||||
| fmt.Sprintf("Required versions (automatic agent upgrades) are only supported in Elastic Stack %s and above", MinVersionRequiredVersions), | ||||||||||||
| ), | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| elements := model.RequiredVersions.Elements() | ||||||||||||
|
|
||||||||||||
| // If the map is empty (required_versions = {}), return an empty array to clear upgrades | ||||||||||||
| if len(elements) == 0 { | ||||||||||||
| emptyArray := make([]struct { | ||||||||||||
| Percentage float32 `json:"percentage"` | ||||||||||||
| Version string `json:"version"` | ||||||||||||
| }, 0) | ||||||||||||
| return &emptyArray, diags | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| result := make([]struct { | ||||||||||||
| Percentage float32 `json:"percentage"` | ||||||||||||
| Version string `json:"version"` | ||||||||||||
| }, 0, len(elements)) | ||||||||||||
|
|
||||||||||||
| for version, percentageVal := range elements { | ||||||||||||
| percentageInt32, ok := percentageVal.(types.Int32) | ||||||||||||
| if !ok { | ||||||||||||
| diags.AddError("required_versions conversion error", fmt.Sprintf("Expected Int32 value, got %T", percentageVal)) | ||||||||||||
| continue | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if percentageInt32.IsNull() || percentageInt32.IsUnknown() { | ||||||||||||
| diags.AddError("required_versions validation error", "percentage cannot be null or unknown") | ||||||||||||
| continue | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| result = append(result, struct { | ||||||||||||
| Percentage float32 `json:"percentage"` | ||||||||||||
| Version string `json:"version"` | ||||||||||||
| }{ | ||||||||||||
| Percentage: float32(percentageInt32.ValueInt32()), | ||||||||||||
|
||||||||||||
| Percentage: float32(percentageInt32.ValueInt32()), | |
| Percentage: func() float32 { | |
| val := percentageInt32.ValueInt32() | |
| return float32(val) | |
| }(), |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import ( | |
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapdefault" | ||
| "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" | ||
| "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" | ||
|
|
@@ -145,6 +146,15 @@ func getSchema() schema.Schema { | |
| Optional: true, | ||
| Computed: true, | ||
| }, | ||
| "required_versions": schema.MapAttribute{ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Support for automatic upgrades was added in 9.1.0. Add a version check to the model code validating that this attribute is only set when the backing Stack supports automatic upgrades (i.e is 9.1.0 or higher). Similarly skip the acceptance tests when this feature is unsupported.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added version check for 9.1.0+. Created |
||
| Description: "Map of agent versions to target percentages for automatic upgrade. The key is the target version and the value is the percentage of agents to upgrade to that version.", | ||
| ElementType: types.Int32Type, | ||
| Optional: true, | ||
| Computed: true, | ||
| PlanModifiers: []planmodifier.Map{ | ||
| mapplanmodifier.UseStateForUnknown(), | ||
| }, | ||
| }, | ||
| }} | ||
| } | ||
| func getGlobalDataTagsAttrTypes() attr.Type { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| variable "policy_name" { | ||
| type = string | ||
| description = "Name for the agent policy" | ||
| } | ||
|
|
||
| provider "elasticstack" { | ||
| elasticsearch {} | ||
| kibana {} | ||
| } | ||
|
|
||
| resource "elasticstack_fleet_agent_policy" "test_policy" { | ||
| name = var.policy_name | ||
| namespace = "default" | ||
| description = "Test Agent Policy with Multiple Required Versions" | ||
| monitor_logs = true | ||
| monitor_metrics = false | ||
| skip_destroy = false | ||
| required_versions = { | ||
| "8.15.0" = 50 | ||
| "8.16.0" = 50 | ||
| } | ||
| } | ||
|
|
||
| data "elasticstack_fleet_enrollment_tokens" "test_policy" { | ||
| policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| variable "policy_name" { | ||
| type = string | ||
| description = "Name for the agent policy" | ||
| } | ||
|
|
||
| provider "elasticstack" { | ||
| elasticsearch {} | ||
| kibana {} | ||
| } | ||
|
|
||
| resource "elasticstack_fleet_agent_policy" "test_policy" { | ||
| name = var.policy_name | ||
| namespace = "default" | ||
| description = "Test Agent Policy with Required Versions" | ||
| monitor_logs = true | ||
| monitor_metrics = false | ||
| skip_destroy = false | ||
| required_versions = { | ||
| "8.15.0" = 100 | ||
| } | ||
| } | ||
|
|
||
| data "elasticstack_fleet_enrollment_tokens" "test_policy" { | ||
| policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| variable "policy_name" { | ||
| type = string | ||
| description = "Name for the agent policy" | ||
| } | ||
|
|
||
| provider "elasticstack" { | ||
| elasticsearch {} | ||
| kibana {} | ||
| } | ||
|
|
||
| resource "elasticstack_fleet_agent_policy" "test_policy" { | ||
| name = var.policy_name | ||
| namespace = "default" | ||
| description = "Test Agent Policy without Required Versions" | ||
| monitor_logs = true | ||
| monitor_metrics = false | ||
| skip_destroy = false | ||
| required_versions = {} | ||
| } | ||
|
|
||
| data "elasticstack_fleet_enrollment_tokens" "test_policy" { | ||
| policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| variable "policy_name" { | ||
| type = string | ||
| description = "Name for the agent policy" | ||
| } | ||
|
|
||
| provider "elasticstack" { | ||
| elasticsearch {} | ||
| kibana {} | ||
| } | ||
|
|
||
| resource "elasticstack_fleet_agent_policy" "test_policy" { | ||
| name = var.policy_name | ||
| namespace = "default" | ||
| description = "Test Agent Policy without Required Versions" | ||
| monitor_logs = true | ||
| monitor_metrics = false | ||
| skip_destroy = false | ||
| } | ||
|
|
||
| data "elasticstack_fleet_enrollment_tokens" "test_policy" { | ||
| policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.