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
8 changes: 4 additions & 4 deletions aks-node-controller/Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Run buf in docker, mounting the full repo into the container
# Emulate running "buf" in the current directory
BUF = docker run --volume "$(CURDIR)/../:$(CURDIR)/../" --workdir $(CURDIR) bufbuild/buf:1.47.2
BUF = docker run --platform linux/amd64 --volume "$(CURDIR)/../:$(CURDIR)/../" --workdir $(CURDIR) bufbuild/buf:1.47.2

.PHONY: proto-generate
proto-generate:
@($(BUF) format -w)
rm -rf pkg/gen/aksnodeconfig/v1
docker build --platform $(shell uname -m) -t protoc-docker - < protoc.Dockerfile
docker run --rm -v $(shell pwd):/$(shell pwd) --workdir=$(shell pwd) protoc-docker protoc --go_opt=module=github.com/Azure/agentbaker/aks-node-controller --go_out=./ --proto_path=proto $(shell find proto/aksnodeconfig/v1 -name '*.proto')
docker build --platform linux/amd64 -t protoc-docker - < protoc.Dockerfile
docker run --platform linux/amd64 --rm -v $(shell pwd):/$(shell pwd) --workdir=$(shell pwd) protoc-docker protoc --go_opt=module=github.com/Azure/agentbaker/aks-node-controller --go_out=./ --proto_path=proto $(shell find proto/aksnodeconfig/v1 -name '*.proto')
$(MAKE) proto-lint

.PHONY: proto-lint
proto-lint:
@($(BUF) lint)
@($(BUF) breaking --against '../.git#branch=master,subdir=aks-node-controller')
@($(BUF) breaking --against '../.git#branch=main,subdir=aks-node-controller')
6 changes: 6 additions & 0 deletions aks-node-controller/parser/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,13 @@ func getDisableSSH(v *aksnodeconfigv1.Configuration) bool {
return !v.GetEnableSsh()
}

// getKubeletFlags returns the kubelet command-line flags as a string.
// When kubelet_cmd_flags is present, it takes precedence and is returned as-is (raw string).
// Otherwise, it falls back to constructing flags from the kubelet_flags map (sorted key-value pairs).
func getKubeletFlags(kubeletConfig *aksnodeconfigv1.KubeletConfig) string {
if kubeletConfig.GetKubeletCmdFlags() != "" {
return kubeletConfig.GetKubeletCmdFlags()
}
Comment on lines +603 to +605
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function now has two different return paths with distinct behaviors (raw string vs. sorted map), but this is not documented. Add a comment explaining that kubelet_cmd_flags takes precedence when present, and that the fallback behavior constructs flags from the map.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@awesomenix could you please document this new field here? KubeletConfig in aks-node-controller/proto/README.md. Thanks for making this change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

return createSortedKeyValuePairs(kubeletConfig.GetKubeletFlags(), " ")
}

Expand Down
86 changes: 54 additions & 32 deletions aks-node-controller/parser/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (

"github.com/Azure/agentbaker/aks-node-controller/helpers"
aksnodeconfigv1 "github.com/Azure/agentbaker/aks-node-controller/pkg/gen/aksnodeconfig/v1"
"github.com/Azure/agentbaker/pkg/agent"
"github.com/Azure/agentbaker/pkg/agent/datamodel"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/google/go-cmp/cmp"
)
Expand Down Expand Up @@ -1380,6 +1382,47 @@ func Test_getKubeletFlags(t *testing.T) {
type args struct {
kubeletConfig *aksnodeconfigv1.KubeletConfig
}
kubeletFlags := map[string]string{
"--address": "0.0.0.0",
"--pod-manifest-path": "/etc/kubernetes/manifests",
"--cluster-domain": "cluster.local",
"--cluster-dns": "10.0.0.10",
"--cgroups-per-qos": "true",
"--tls-cert-file": "/etc/kubernetes/certs/kubeletserver.crt",
"--tls-private-key-file": "/etc/kubernetes/certs/kubeletserver.key",
"--tls-cipher-suites": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256", //nolint:lll
"--max-pods": "110",
"--node-status-update-frequency": "10s",
"--image-gc-high-threshold": "85",
"--image-gc-low-threshold": "80",
"--event-qps": "0",
"--pod-max-pids": "-1",
"--enforce-node-allocatable": "pods",
"--streaming-connection-idle-timeout": "4h0m0s",
"--rotate-certificates": "true",
"--rotate-server-certificates": "true",
"--read-only-port": "10255",
"--protect-kernel-defaults": "true",
"--resolv-conf": "/etc/resolv.conf",
"--anonymous-auth": "false",
"--client-ca-file": "/etc/kubernetes/certs/ca.crt",
"--authentication-token-webhook": "true",
"--authorization-mode": "Webhook",
"--eviction-hard": "memory.available<750Mi,nodefs.available<10%,nodefs.inodesFree<5%",
"--feature-gates": "RotateKubeletServerCertificate=true,DynamicKubeletConfig=false", //nolint:lll // what if you turn off dynamic kubelet using dynamic kubelet?
"--system-reserved": "cpu=2,memory=1Gi",
"--kube-reserved": "cpu=100m,memory=1638Mi",
}
config := &datamodel.NodeBootstrappingConfiguration{
KubeletConfig: kubeletFlags,
ContainerService: &datamodel.ContainerService{
Location: "southcentralus",
Type: "Microsoft.ContainerService/ManagedClusters",
Properties: &datamodel.Properties{},
},
EnableKubeletConfigFile: false,
AgentPoolProfile: &datamodel.AgentPoolProfile{},
}
tests := []struct {
name string
args args
Expand All @@ -1389,45 +1432,24 @@ func Test_getKubeletFlags(t *testing.T) {
name: "Default KubeletFlags",
args: args{
kubeletConfig: &aksnodeconfigv1.KubeletConfig{
KubeletFlags: map[string]string{
"--address": "0.0.0.0",
"--pod-manifest-path": "/etc/kubernetes/manifests",
"--cluster-domain": "cluster.local",
"--cluster-dns": "10.0.0.10",
"--cgroups-per-qos": "true",
"--tls-cert-file": "/etc/kubernetes/certs/kubeletserver.crt",
"--tls-private-key-file": "/etc/kubernetes/certs/kubeletserver.key",
"--tls-cipher-suites": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256", //nolint:lll
"--max-pods": "110",
"--node-status-update-frequency": "10s",
"--image-gc-high-threshold": "85",
"--image-gc-low-threshold": "80",
"--event-qps": "0",
"--pod-max-pids": "-1",
"--enforce-node-allocatable": "pods",
"--streaming-connection-idle-timeout": "4h0m0s",
"--rotate-certificates": "true",
"--rotate-server-certificates": "true",
"--read-only-port": "10255",
"--protect-kernel-defaults": "true",
"--resolv-conf": "/etc/resolv.conf",
"--anonymous-auth": "false",
"--client-ca-file": "/etc/kubernetes/certs/ca.crt",
"--authentication-token-webhook": "true",
"--authorization-mode": "Webhook",
"--eviction-hard": "memory.available<750Mi,nodefs.available<10%,nodefs.inodesFree<5%",
"--feature-gates": "RotateKubeletServerCertificate=true,DynamicKubeletConfig=false", //nolint:lll // what if you turn off dynamic kubelet using dynamic kubelet?
"--system-reserved": "cpu=2,memory=1Gi",
"--kube-reserved": "cpu=100m,memory=1638Mi",
},
KubeletFlags: kubeletFlags,
},
},
want: expectedKubeletConfigFlags,
},
{
name: "Default KubeletCmdFlags",
args: args{
kubeletConfig: &aksnodeconfigv1.KubeletConfig{
KubeletCmdFlags: agent.GetOrderedKubeletConfigFlagString(config),
},
},
want: expectedKubeletConfigFlags,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getKubeletFlags(tt.args.kubeletConfig); got != tt.want {
if got := getKubeletFlags(tt.args.kubeletConfig); strings.TrimSpace(got) != strings.TrimSpace(tt.want) {
t.Errorf("getKubeletFlags() = %v, want %v", got, tt.want)
}
})
Expand Down
13 changes: 12 additions & 1 deletion aks-node-controller/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Azure/agentbaker/aks-node-controller/helpers"
aksnodeconfigv1 "github.com/Azure/agentbaker/aks-node-controller/pkg/gen/aksnodeconfig/v1"
"github.com/Azure/agentbaker/aks-node-controller/pkg/nodeconfigutils"
"github.com/Azure/agentbaker/pkg/agent"
"github.com/Azure/agentbaker/pkg/agent/datamodel"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -274,6 +275,16 @@ oom_score = -999
}

helpers.ValidateAndSetLinuxKubeletFlags(kubeletConfig, cs, agentPool)
config := &datamodel.NodeBootstrappingConfiguration{
KubeletConfig: kubeletConfig,
ContainerService: &datamodel.ContainerService{
Location: "southcentralus",
Type: "Microsoft.ContainerService/ManagedClusters",
Properties: &datamodel.Properties{},
},
EnableKubeletConfigFile: false,
AgentPoolProfile: &datamodel.AgentPoolProfile{},
}
aksNodeConfig := &aksnodeconfigv1.Configuration{
LinuxAdminUsername: "azureuser",
VmSize: "Standard_DS1_v2",
Expand Down Expand Up @@ -312,7 +323,7 @@ oom_score = -999
OutboundCommand: helpers.GetDefaultOutboundCommand(),
KubeletConfig: &aksnodeconfigv1.KubeletConfig{
EnableKubeletConfigFile: false,
KubeletFlags: helpers.GetKubeletConfigFlag(kubeletConfig, cs, agentPool, false),
KubeletCmdFlags: agent.GetOrderedKubeletConfigFlagString(config),
KubeletNodeLabels: helpers.GetKubeletNodeLabels(agentPool),
},
CustomCloudConfig: &aksnodeconfigv1.CustomCloudConfig{},
Expand Down
Loading
Loading