Skip to content

Commit 9a7a0b1

Browse files
DevinwongCopilot
andauthored
fix: fixing some scriptless bugs (#7698)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 191c38c commit 9a7a0b1

File tree

8 files changed

+180
-16
lines changed

8 files changed

+180
-16
lines changed

aks-node-controller/helpers/const.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const (
88
NetworkPolicyAzure = "azure"
99
NetworkPolicyCalico = "calico"
1010
LoadBalancerBasic = "basic"
11-
LoadBalancerStandard = "Standard"
11+
LoadBalancerStandard = "standard"
1212
VMSizeStandardDc2s = "Standard_DC2s"
1313
VMSizeStandardDc4s = "Standard_DC4s"
1414
DefaultLinuxUser = "azureuser"

aks-node-controller/helpers/utils_test.go

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ func Test_getLoadBalancerSKU(t *testing.T) {
2323
{
2424
name: "LoadBalancerSKU Standard",
2525
args: args{
26-
sku: "Standard",
26+
sku: "standard",
2727
},
2828
want: aksnodeconfigv1.LoadBalancerSku_LOAD_BALANCER_SKU_STANDARD,
2929
},
3030
{
3131
name: "LoadBalancerSKU Basic",
3232
args: args{
33-
sku: "Basic",
33+
sku: "basic",
3434
},
3535
want: aksnodeconfigv1.LoadBalancerSku_LOAD_BALANCER_SKU_BASIC,
3636
},
@@ -299,3 +299,110 @@ func TestIsKubeletServingCertificateRotationEnabled(t *testing.T) {
299299
})
300300
}
301301
}
302+
303+
func TestValidateAndSetLinuxKubeletFlags_RemovesDeprecatedFlags(t *testing.T) {
304+
kubeletFlags := map[string]string{
305+
"--dynamic-config-dir": "/var/lib/kubelet",
306+
"--non-masquerade-cidr": "10.240.0.0/12",
307+
"--cni-bin-dir": "/opt/cni/bin",
308+
"--cni-cache-dir": "/var/lib/cni",
309+
"--cni-conf-dir": "/etc/cni/net.d",
310+
"--docker-endpoint": "npipe:////./pipe/docker_engine",
311+
"--image-pull-progress-deadline": "30m",
312+
"--network-plugin": "cni",
313+
"--network-plugin-mtu": "1500",
314+
"--feature-gates": "",
315+
}
316+
317+
ValidateAndSetLinuxKubeletFlags(kubeletFlags, newTestContainerService("1.27.3"), &datamodel.AgentPoolProfile{})
318+
319+
removedFlags := []string{
320+
"--dynamic-config-dir",
321+
"--non-masquerade-cidr",
322+
"--cni-bin-dir",
323+
"--cni-cache-dir",
324+
"--cni-conf-dir",
325+
"--docker-endpoint",
326+
"--image-pull-progress-deadline",
327+
"--network-plugin",
328+
"--network-plugin-mtu",
329+
}
330+
331+
for _, flag := range removedFlags {
332+
if _, exists := kubeletFlags[flag]; exists {
333+
t.Fatalf("expected flag %s to be removed", flag)
334+
}
335+
}
336+
}
337+
338+
func TestValidateAndSetLinuxKubeletFlags_FeatureGatesByVersion(t *testing.T) {
339+
testCases := []struct {
340+
name string
341+
version string
342+
initialFeatureGates string
343+
rotateServerCerts bool
344+
expectedFeatureGates map[string]bool
345+
}{
346+
{
347+
name: "removes dynamic gate when version >= 1.24",
348+
version: "1.26.0",
349+
initialFeatureGates: "DynamicKubeletConfig=false,OtherFeature=true",
350+
expectedFeatureGates: map[string]bool{
351+
"OtherFeature": true,
352+
},
353+
},
354+
{
355+
name: "adds dynamic and disable accelerator gates for 1.22",
356+
version: "1.22.6",
357+
initialFeatureGates: "FooBar=true",
358+
expectedFeatureGates: map[string]bool{
359+
"FooBar": true,
360+
"DynamicKubeletConfig": false,
361+
"DisableAcceleratorUsageMetrics": false,
362+
},
363+
},
364+
{
365+
name: "does not add dynamic gate before 1.11",
366+
version: "1.10.13",
367+
initialFeatureGates: "",
368+
expectedFeatureGates: map[string]bool{},
369+
},
370+
{
371+
name: "adds rotate kubelet server certificate gate when enabled",
372+
version: "1.28.2",
373+
initialFeatureGates: "",
374+
rotateServerCerts: true,
375+
expectedFeatureGates: map[string]bool{
376+
"RotateKubeletServerCertificate": true,
377+
},
378+
},
379+
}
380+
381+
for _, tc := range testCases {
382+
t.Run(tc.name, func(t *testing.T) {
383+
kubeletFlags := map[string]string{
384+
"--feature-gates": tc.initialFeatureGates,
385+
}
386+
if tc.rotateServerCerts {
387+
kubeletFlags["--rotate-server-certificates"] = "true"
388+
}
389+
390+
ValidateAndSetLinuxKubeletFlags(kubeletFlags, newTestContainerService(tc.version), &datamodel.AgentPoolProfile{})
391+
392+
featureGateMap := strKeyValToMapBool(kubeletFlags["--feature-gates"], ",", "=")
393+
if !reflect.DeepEqual(featureGateMap, tc.expectedFeatureGates) {
394+
t.Fatalf("unexpected feature gates: got %v, want %v", featureGateMap, tc.expectedFeatureGates)
395+
}
396+
})
397+
}
398+
}
399+
400+
func newTestContainerService(version string) *datamodel.ContainerService {
401+
return &datamodel.ContainerService{
402+
Properties: &datamodel.Properties{
403+
OrchestratorProfile: &datamodel.OrchestratorProfile{
404+
OrchestratorVersion: version,
405+
},
406+
},
407+
}
408+
}

aks-node-controller/parser/helper.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -596,13 +596,6 @@ func getDisableSSH(v *aksnodeconfigv1.Configuration) bool {
596596
return !v.GetEnableSsh()
597597
}
598598

599-
func getServicePrincipalFileContent(authConfig *aksnodeconfigv1.AuthConfig) string {
600-
if authConfig.GetServicePrincipalSecret() == "" {
601-
return ""
602-
}
603-
return base64.StdEncoding.EncodeToString([]byte(authConfig.GetServicePrincipalSecret()))
604-
}
605-
606599
func getKubeletFlags(kubeletConfig *aksnodeconfigv1.KubeletConfig) string {
607600
return createSortedKeyValuePairs(kubeletConfig.GetKubeletFlags(), " ")
608601
}

aks-node-controller/parser/parser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func getCSEEnv(config *aksnodeconfigv1.Configuration) map[string]string {
136136
"DHCPV6_CONFIG_FILEPATH": getDHCPV6ConfigFilepath(),
137137
"THP_ENABLED": config.GetCustomLinuxOsConfig().GetTransparentHugepageSupport(),
138138
"THP_DEFRAG": config.GetCustomLinuxOsConfig().GetTransparentDefrag(),
139-
"SERVICE_PRINCIPAL_FILE_CONTENT": getServicePrincipalFileContent(config.AuthConfig),
139+
"SERVICE_PRINCIPAL_FILE_CONTENT": config.GetAuthConfig().GetServicePrincipalSecret(),
140140
"KUBELET_CLIENT_CONTENT": config.GetKubeletConfig().GetKubeletClientKey(),
141141
"KUBELET_CLIENT_CERT_CONTENT": config.GetKubeletConfig().GetKubeletClientCertContent(),
142142
"KUBELET_CONFIG_FILE_ENABLED": fmt.Sprintf("%v", config.GetKubeletConfig().GetEnableKubeletConfigFile()),

aks-node-controller/parser/parser_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,20 @@ oom_score = -999
334334
}
335335
}
336336

337+
func TestBuildCSECmd_SetsServicePrincipalFileContent(t *testing.T) {
338+
secret := "super-secret-value"
339+
cmd, err := BuildCSECmd(context.TODO(), &aksnodeconfigv1.Configuration{
340+
AuthConfig: &aksnodeconfigv1.AuthConfig{ServicePrincipalSecret: secret},
341+
})
342+
require.NoError(t, err)
343+
344+
vars := environToMap(cmd.Env)
345+
346+
// The value should be exactly the secret, without additional base64 encoding.
347+
// Actually the client which passes the secret to aks-node-controller should have base64 encoded it first.
348+
assert.Equal(t, secret, vars["SERVICE_PRINCIPAL_FILE_CONTENT"])
349+
}
350+
337351
func TestAKSNodeConfigCompatibilityFromJsonToCSECommand(t *testing.T) {
338352
tests := []struct {
339353
name string

e2e/node_config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ func baseTemplateLinux(t testing.TB, location string, k8sVersion string, arch st
532532
},
533533
ServicePrincipalProfile: &datamodel.ServicePrincipalProfile{
534534
ClientID: "msi",
535-
Secret: "**msi**",
535+
Secret: base64.StdEncoding.EncodeToString([]byte("msi")),
536536
},
537537
CertificateProfile: &datamodel.CertificateProfile{},
538538
HostedMasterProfile: &datamodel.HostedMasterProfile{},
@@ -816,7 +816,7 @@ func baseTemplateWindows(t testing.TB, location string) *datamodel.NodeBootstrap
816816
},
817817
ServicePrincipalProfile: &datamodel.ServicePrincipalProfile{
818818
ClientID: "msi",
819-
Secret: "**msi**",
819+
Secret: base64.StdEncoding.EncodeToString([]byte("msi")),
820820
},
821821
FeatureFlags: &datamodel.FeatureFlags{
822822
EnableWinDSR: true,

e2e/validators.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ func ValidateLeakedSecrets(ctx context.Context, s *Scenario) {
199199
for _, logFile := range []string{"/var/log/azure/cluster-provision.log", "/var/log/azure/aks-node-controller.log"} {
200200
for _, secretValue := range secrets {
201201
if secretValue != "" {
202-
ValidateFileExcludesContent(ctx, s, logFile, secretValue)
202+
ValidateFileExcludesExactContent(ctx, s, logFile, secretValue)
203203
}
204204
}
205205
}
@@ -384,20 +384,65 @@ func fileHasContent(ctx context.Context, s *Scenario, fileName string, contents
384384
}
385385
}
386386

387+
func fileHasExactContent(ctx context.Context, s *Scenario, fileName string, contents string) bool {
388+
s.T.Helper()
389+
require.NotEmpty(s.T, contents, "Test setup failure: Can't validate that a file has contents with an empty string. Filename: %s", fileName)
390+
encodedPattern := base64.StdEncoding.EncodeToString([]byte(contents))
391+
if s.IsWindows() {
392+
steps := []string{
393+
"$ErrorActionPreference = \"Stop\"",
394+
fmt.Sprintf("if ( -not ( Test-Path -Path %s ) ) { exit 2 }", fileName),
395+
fmt.Sprintf("$pattern = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('%s'))", encodedPattern),
396+
fmt.Sprintf("$content = Get-Content -Path %s -Raw", fileName),
397+
"$escaped = [regex]::Escape($pattern)",
398+
"if ([regex]::Match($content, \"(?<!\\w)\" + $escaped + \"(?!\\w)\").Success) { exit 0 } else { exit 1 }",
399+
}
400+
execResult := execScriptOnVMForScenario(ctx, s, strings.Join(steps, "\n"))
401+
return execResult.exitCode == "0"
402+
} else {
403+
steps := []string{
404+
"set -ex",
405+
fmt.Sprintf("if [ ! -f %s ]; then exit 2; fi", fileName),
406+
fmt.Sprintf("pattern=$(printf '%%s' '%s' | base64 -d)", encodedPattern),
407+
"escaped=$(printf '%s\n' \"$pattern\" | sed -e 's/[.\\[\\()*?^$+{}|]/\\\\&/g')",
408+
"regex='(^|[^[:alnum:]_])'\"$escaped\"'([^[:alnum:]_]|$)'",
409+
fmt.Sprintf("if sudo grep -Eq \"$regex\" %s; then exit 0; else exit 1; fi", fileName),
410+
}
411+
execResult := execScriptOnVMForScenario(ctx, s, strings.Join(steps, "\n"))
412+
return execResult.exitCode == "0"
413+
}
414+
}
415+
416+
// ValidateFileHasContent passes the test if the specified file contains the specified contents.
417+
// The contents doesn't need to be surrounded by non-word characters.
418+
// E.g.: searching "bcd" in "abcdef" is a match, thus the validation passes.
387419
func ValidateFileHasContent(ctx context.Context, s *Scenario, fileName string, contents string) {
388420
s.T.Helper()
389421
if !fileHasContent(ctx, s, fileName, contents) {
390422
s.T.Fatalf("expected file %s to have contents %q, but it does not", fileName, contents)
391423
}
392424
}
393425

426+
// ValidateFileExcludesContent fails the test if the specified file contains the specified contents.
427+
// The contents doesn't need to be surrounded by non-word characters.
428+
// E.g.: searching "bcd" in "abcdef" is a match, thus the validation fails.
394429
func ValidateFileExcludesContent(ctx context.Context, s *Scenario, fileName string, contents string) {
395430
s.T.Helper()
396431
if fileHasContent(ctx, s, fileName, contents) {
397432
s.T.Fatalf("expected file %s to not have contents %q, but it does", fileName, contents)
398433
}
399434
}
400435

436+
// ValidateFileExcludesExactContent fails the test if the specified file contains the specified contents.
437+
// The contents needs to be surrounded by non-word characters.
438+
// E.g.: searching "bcd" in "abcdef" is not a match, thus the validation passes.
439+
func ValidateFileExcludesExactContent(ctx context.Context, s *Scenario, fileName string, contents string) {
440+
s.T.Helper()
441+
if fileHasExactContent(ctx, s, fileName, contents) {
442+
s.T.Fatalf("expected file %s to not have exact contents %q, but it does", fileName, contents)
443+
}
444+
}
445+
401446
func ServiceCanRestartValidator(ctx context.Context, s *Scenario, serviceName string, restartTimeoutInSeconds int) {
402447
s.T.Helper()
403448
steps := []string{

e2e/vmss.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,8 +548,13 @@ func extractLogsFromVMLinux(ctx context.Context, s *Scenario, vm *ScenarioVM) er
548548
"cluster-provision-cse-output.log": "sudo cat /var/log/azure/cluster-provision-cse-output.log",
549549
"sysctl-out.log": "sudo sysctl -a",
550550
"aks-node-controller.log": "sudo cat /var/log/azure/aks-node-controller.log",
551-
"syslog": "sudo cat /var/log/" + syslogHandle,
552-
"journalctl": "sudo journalctl --boot=0 --no-pager",
551+
"aks-node-controller-config.json": "sudo cat /opt/azure/containers/aks-node-controller-config.json", // Only available in Scriptless.
552+
553+
// Only available in Scriptless. By default, e2e enables aks-node-controller-hack, so this is the actual config used. Only in e2e. Not used in production.
554+
"aks-node-controller-config-hack.json": "sudo cat /opt/azure/containers/aks-node-controller-config-hack.json",
555+
"syslog": "sudo cat /var/log/" + syslogHandle,
556+
"journalctl": "sudo journalctl --boot=0 --no-pager",
557+
"azure.json": "sudo cat /etc/kubernetes/azure.json",
553558
}
554559
if s.SecureTLSBootstrappingEnabled() {
555560
commandList["secure-tls-bootstrap.log"] = "sudo cat /var/log/azure/aks/secure-tls-bootstrap.log"

0 commit comments

Comments
 (0)