Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/resources/node_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,11 +442,29 @@ Optional:

- `fips_mode` (String) FIPS 140-2 mode. Valid values: `FIPS`, `Disabled`.
- `image_family` (String) Azure image family. Valid values: `Ubuntu`, `Ubuntu2204`, `Ubuntu2404`, `AzureLinux`.
- `kubelet` (Attributes) Azure kubelet configuration overrides (see [below for nested schema](#nestedatt--azure--kubelet))
- `max_pods` (Number) Maximum number of pods per node
- `os_disk_size_gb` (Number) OS disk size in GB
- `tags` (Map of String) Azure tags to apply to resources
- `vnet_subnet_id` (String) VNet subnet ID

<a id="nestedatt--azure--kubelet"></a>
### Nested Schema for `azure.kubelet`

Optional:

- `allowed_unsafe_sysctls` (List of String) List of allowed unsafe sysctls
- `container_log_max_files` (Number) Maximum number of container log files
- `container_log_max_size` (String) Maximum container log file size
- `cpu_cfs_quota` (Boolean) Enable CPU CFS quota enforcement
- `cpu_cfs_quota_period` (String) CPU CFS quota period
- `cpu_manager_policy` (String) CPU manager policy (None, static)
- `image_gc_high_threshold_percent` (Number) Image GC high threshold percent
- `image_gc_low_threshold_percent` (Number) Image GC low threshold percent
- `pod_pids_limit` (Number) Maximum number of PIDs per pod
- `topology_manager_policy` (String) Topology manager policy



<a id="nestedatt--capacity_types"></a>
### Nested Schema for `capacity_types`
Expand Down
47 changes: 47 additions & 0 deletions internal/provider/node_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,53 @@ func (r *NodePolicyResource) Schema(ctx context.Context, req resource.SchemaRequ
Description: "Maximum number of pods per node",
Optional: true,
},
"kubelet": schema.SingleNestedAttribute{
Description: "Azure kubelet configuration overrides",
Optional: true,
Attributes: map[string]schema.Attribute{
"cpu_manager_policy": schema.StringAttribute{
Description: "CPU manager policy (None, static)",
Optional: true,
},
"cpu_cfs_quota": schema.BoolAttribute{
Description: "Enable CPU CFS quota enforcement",
Optional: true,
},
"cpu_cfs_quota_period": schema.StringAttribute{
Description: "CPU CFS quota period",
Optional: true,
},
"image_gc_high_threshold_percent": schema.Int32Attribute{
Description: "Image GC high threshold percent",
Optional: true,
},
"image_gc_low_threshold_percent": schema.Int32Attribute{
Description: "Image GC low threshold percent",
Optional: true,
},
"topology_manager_policy": schema.StringAttribute{
Description: "Topology manager policy",
Optional: true,
},
"allowed_unsafe_sysctls": schema.ListAttribute{
Description: "List of allowed unsafe sysctls",
Optional: true,
ElementType: types.StringType,
},
"container_log_max_size": schema.StringAttribute{
Description: "Maximum container log file size",
Optional: true,
},
"container_log_max_files": schema.Int32Attribute{
Description: "Maximum number of container log files",
Optional: true,
},
"pod_pids_limit": schema.Int64Attribute{
Description: "Maximum number of PIDs per pod",
Optional: true,
},
},
},
},
},
// Raw Karpenter specs
Expand Down
73 changes: 73 additions & 0 deletions internal/provider/node_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ func TestNodePolicyResourceModel(t *testing.T) {
FipsMode: types.StringValue("Disabled"),
Tags: types.MapValueMust(types.StringType, map[string]attr.Value{"Environment": types.StringValue("production")}),
MaxPods: types.Int32Value(110),
Kubelet: &AzureKubeletConfiguration{
CpuManagerPolicy: types.StringValue("static"),
CpuCfsQuota: types.BoolValue(true),
CpuCfsQuotaPeriod: types.StringValue("100ms"),
ImageGcHighThresholdPercent: types.Int32Value(85),
ImageGcLowThresholdPercent: types.Int32Value(70),
TopologyManagerPolicy: types.StringValue("restricted"),
AllowedUnsafeSysctls: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("net.ipv4.tcp_syncookies")}),
ContainerLogMaxSize: types.StringValue("50Mi"),
ContainerLogMaxFiles: types.Int32Value(5),
PodPidsLimit: types.Int64Value(4096),
},
}

if azureConfig.VnetSubnetId.ValueString() == "" {
Expand All @@ -168,6 +180,18 @@ func TestNodePolicyResourceModel(t *testing.T) {
if azureConfig.MaxPods.ValueInt32() != 110 {
t.Errorf("Expected max_pods to be 110, got %d", azureConfig.MaxPods.ValueInt32())
}
if azureConfig.Kubelet == nil {
t.Fatal("Expected Kubelet to be non-nil")
}
if azureConfig.Kubelet.CpuManagerPolicy.ValueString() != "static" {
t.Errorf("Expected CpuManagerPolicy to be 'static', got %s", azureConfig.Kubelet.CpuManagerPolicy.ValueString())
}
if !azureConfig.Kubelet.CpuCfsQuota.ValueBool() {
t.Error("Expected CpuCfsQuota to be true")
}
if azureConfig.Kubelet.PodPidsLimit.ValueInt64() != 4096 {
t.Errorf("Expected PodPidsLimit to be 4096, got %d", azureConfig.Kubelet.PodPidsLimit.ValueInt64())
}
})

// Test RawKarpenterSpec
Expand Down Expand Up @@ -1014,6 +1038,10 @@ func TestNodePolicyResourceModel(t *testing.T) {
})
}

// singleNestedSchema is a local alias to allow type-asserting schema.Attribute
// to access nested Attributes without importing internal framework packages.
type singleNestedSchema = schema.SingleNestedAttribute

func validateNodePolicySchema(t *testing.T, schema schema.Schema) {
// Validate required attributes
requiredAttrs := []string{"name"}
Expand Down Expand Up @@ -1070,4 +1098,49 @@ func validateNodePolicySchema(t *testing.T, schema schema.Schema) {
if _, exists := schema.Attributes["azure"]; !exists {
t.Error("Azure configuration not found in schema")
}

// Validate azure nested attributes include kubelet (regression test for missing kubelet schema bug)
azureRaw, ok := schema.Attributes["azure"]
if !ok {
t.Fatal("azure attribute not found in schema")
}
azureSingle, ok := azureRaw.(singleNestedSchema)
if !ok {
t.Fatal("azure attribute is not a SingleNestedAttribute")
}
azureKubeletFields := []string{
"kubelet",
"vnet_subnet_id",
"os_disk_size_gb",
"image_family",
"fips_mode",
"tags",
"max_pods",
}
for _, field := range azureKubeletFields {
if _, exists := azureSingle.Attributes[field]; !exists {
t.Errorf("azure schema is missing '%s' attribute", field)
}
}

// Validate kubelet sub-attributes
kubeletRaw, ok := azureSingle.Attributes["kubelet"]
if !ok {
t.Fatal("azure schema is missing 'kubelet' attribute")
}
kubeletSingle, ok := kubeletRaw.(singleNestedSchema)
if !ok {
t.Fatal("azure.kubelet is not a SingleNestedAttribute")
}
kubeletFields := []string{
"cpu_manager_policy", "cpu_cfs_quota", "cpu_cfs_quota_period",
"image_gc_high_threshold_percent", "image_gc_low_threshold_percent",
"topology_manager_policy", "allowed_unsafe_sysctls",
"container_log_max_size", "container_log_max_files", "pod_pids_limit",
}
for _, field := range kubeletFields {
if _, exists := kubeletSingle.Attributes[field]; !exists {
t.Errorf("azure.kubelet schema is missing '%s' attribute", field)
}
}
}
Loading