pkg/agent/baker.go (1,714 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. package agent import ( "archive/zip" "bytes" "encoding/base64" "fmt" "reflect" "strconv" "strings" "text/template" "github.com/Azure/agentbaker/parts" "github.com/Azure/agentbaker/pkg/agent/datamodel" "github.com/Azure/go-autorest/autorest/to" ) // TemplateGenerator represents the object that performs the template generation. type TemplateGenerator struct{} // InitializeTemplateGenerator creates a new template generator object. func InitializeTemplateGenerator() *TemplateGenerator { t := &TemplateGenerator{} return t } // GetNodeBootstrappingPayload get node bootstrapping data. // This function only can be called after the validation of the input NodeBootstrappingConfiguration. func (t *TemplateGenerator) getNodeBootstrappingPayload(config *datamodel.NodeBootstrappingConfiguration) string { if config.AgentPoolProfile.IsWindows() { return t.getWindowsNodeBootstrappingPayload(config) } return t.getLinuxNodeBootstrappingPayload(config) } func (t *TemplateGenerator) getWindowsNodeBootstrappingPayload(config *datamodel.NodeBootstrappingConfiguration) string { // this might seem strange that we're encoding the custom data to a JSON string and then extracting it, but without that serialisation and deserialisation // lots of tests fail. customData := getCustomDataFromJSON(t.getWindowsNodeCustomDataJSONObject(config)) return base64.StdEncoding.EncodeToString([]byte(customData)) } func (t *TemplateGenerator) getLinuxNodeBootstrappingPayload(config *datamodel.NodeBootstrappingConfiguration) string { // this might seem strange that we're encoding the custom data to a JSON string and then extracting it, but without that serialisation and deserialisation // lots of tests fail. customData := getCustomDataFromJSON(t.getLinuxNodeCustomDataJSONObject(config)) return getBase64EncodedGzippedCustomScriptFromStr(customData) } // GetLinuxNodeCustomDataJSONObject returns Linux customData JSON object in the form. // { "customData": "<customData string>" }. func (t *TemplateGenerator) getLinuxNodeCustomDataJSONObject(config *datamodel.NodeBootstrappingConfiguration) string { // get parameters parameters := getParameters(config) // get variable cloudInit variables := getCustomDataVariables(config) str, e := t.getSingleLineForTemplate(kubernetesNodeCustomDataYaml, config.AgentPoolProfile, getBakerFuncMap(config, parameters, variables), true) if e != nil { panic(e) } return fmt.Sprintf("{\"customData\": \"%s\"}", str) } // GetWindowsNodeCustomDataJSONObject returns Windows customData JSON object in the form. // { "customData": "<customData string>" }. func (t *TemplateGenerator) getWindowsNodeCustomDataJSONObject(config *datamodel.NodeBootstrappingConfiguration) string { cs := config.ContainerService profile := config.AgentPoolProfile // get parameters parameters := getParameters(config) // get variable custom data variables := getWindowsCustomDataVariables(config) str, e := t.getSingleLineForTemplate(kubernetesWindowsAgentCustomDataPS1, profile, getBakerFuncMap(config, parameters, variables), false) if e != nil { panic(e) } preprovisionCmd := "" if profile.PreprovisionExtension != nil { preprovisionCmd = makeAgentExtensionScriptCommands(cs, profile) } str = strings.ReplaceAll(str, "PREPROVISION_EXTENSION", escapeSingleLine(strings.TrimSpace(preprovisionCmd))) return fmt.Sprintf("{\"customData\": \"%s\"}", str) } // GetNodeBootstrappingCmd get node bootstrapping cmd. // This function only can be called after the validation of the input NodeBootstrappingConfiguration. func (t *TemplateGenerator) getNodeBootstrappingCmd(config *datamodel.NodeBootstrappingConfiguration) string { if config.AgentPoolProfile.IsWindows() { return t.getWindowsNodeCSECommand(config) } return t.getLinuxNodeCSECommand(config) } // getLinuxNodeCSECommand returns Linux node custom script extension execution command. func (t *TemplateGenerator) getLinuxNodeCSECommand(config *datamodel.NodeBootstrappingConfiguration) string { // get parameters parameters := getParameters(config) // get variable variables := getCSECommandVariables(config) // NOTE: that CSE command will be executed by VM/VMSS extension so it doesn't need extra escaping like custom data does str, e := t.getSingleLine( kubernetesCSECommandString, config.AgentPoolProfile, getBakerFuncMap(config, parameters, variables), true, ) if e != nil { panic(e) } // NOTE: we break the one-line CSE command into different lines in a file for better management // so we need to combine them into one line here return strings.ReplaceAll(str, "\n", " ") } // getWindowsNodeCSECommand returns Windows node custom script extension execution command. func (t *TemplateGenerator) getWindowsNodeCSECommand(config *datamodel.NodeBootstrappingConfiguration) string { // get parameters parameters := getParameters(config) // get variable variables := getCSECommandVariables(config) // NOTE: that CSE command will be executed by VMSS extension so it doesn't need extra escaping like custom data does str, e := t.getSingleLine( kubernetesWindowsAgentCSECommandPS1, config.AgentPoolProfile, getBakerFuncMap(config, parameters, variables), false, ) if e != nil { panic(e) } /* NOTE(qinahao): windows cse cmd uses escaped \" to quote Powershell command in [csecmd.p1](https://github.com/Azure/AgentBaker/blob/master/parts/windows/csecmd.ps1). */ // to not break go template parsing. We switch \" back to " otherwise Azure ARM template will escape \ to be \\\" str = strings.ReplaceAll(str, `\"`, `"`) // NOTE: we break the one-line CSE command into different lines in a file for better management // so we need to combine them into one line here return strings.ReplaceAll(str, "\n", " ") } // getSingleLineForTemplate returns the file as a single line for embedding in an arm template. func (t *TemplateGenerator) getSingleLineForTemplate(textFilename string, profile interface{}, funcMap template.FuncMap, isLinux bool) (string, error) { expandedTemplate, err := t.getSingleLine(textFilename, profile, funcMap, isLinux) if err != nil { return "", err } textStr := escapeSingleLine(expandedTemplate) return textStr, nil } // getSingleLine returns the file as a single line. func (t *TemplateGenerator) getSingleLine(textFilename string, profile interface{}, funcMap template.FuncMap, isLinux bool) (string, error) { b, err := parts.Templates.ReadFile(textFilename) if err != nil { return "", fmt.Errorf("yaml file %s does not exist", textFilename) } if isLinux { b = removeComments(b) } // use go templates to process the text filename templ := template.New("customdata template").Option("missingkey=zero").Funcs(funcMap) if _, err = templ.New(textFilename).Parse(string(b)); err != nil { return "", fmt.Errorf("error parsing file %s: %w", textFilename, err) } var buffer bytes.Buffer if err = templ.ExecuteTemplate(&buffer, textFilename, profile); err != nil { return "", fmt.Errorf("error executing template for file %s: %w", textFilename, err) } expandedTemplate := buffer.String() return expandedTemplate, nil } // getTemplateFuncMap returns the general purpose template func map from getContainerServiceFuncMap. func getBakerFuncMap(config *datamodel.NodeBootstrappingConfiguration, params paramsMap, variables paramsMap) template.FuncMap { funcMap := getContainerServiceFuncMap(config) funcMap["GetParameter"] = func(s string) interface{} { if v, ok := params[s].(paramsMap); ok && v != nil { if v["value"] == nil { // return empty string so we don't get <no value> from go template return "" } return v["value"] } return "" } // TODO: GetParameterPropertyLower funcMap["GetParameterProperty"] = func(s, p string) interface{} { if v, ok := params[s].(paramsMap); ok && v != nil { //nolint:errcheck // this code been writen before linter was added param := v["value"].(paramsMap)[p] if param == nil { // return empty string so we don't get <no value> from go template return "" } return param } return "" } funcMap["GetVariable"] = func(s string) interface{} { if variables[s] == nil { // return empty string so we don't get <no value> from go template return "" } return variables[s] } funcMap["GetVariableProperty"] = func(v, p string) interface{} { if v, ok := variables[v].(paramsMap); ok && v != nil { if v[p] == nil { // return empty string so we don't get <no value> from go template return "" } return v[p] } return "" } return funcMap } /* normalizeResourceGroupNameForLabel normalizes resource group name to be used as a label, similar to what the ARM template used to do. When ARM template was used, the following is used: variables('labelResourceGroup') which is defined as: [if(or(or(endsWith(variables('truncatedResourceGroup'), '-'), endsWith(variables('truncatedResourceGroup'), '_')), endsWith(variables('truncatedResourceGroup'), '.')), concat(take(variables('truncatedResourceGroup'), 62), 'z'), variables('truncatedResourceGroup'))] the "truncatedResourceGroup" is defined as: [take(replace(replace(resourceGroup().name, '(', '-'), ')', '-'), 63)]*/ // This function does the same processing. func normalizeResourceGroupNameForLabel(resourceGroupName string) string { truncated := resourceGroupName truncated = strings.ReplaceAll(truncated, "(", "-") truncated = strings.ReplaceAll(truncated, ")", "-") const maxKubernetesLabelLength = 63 if len(truncated) > maxKubernetesLabelLength { truncated = truncated[0:maxKubernetesLabelLength] } if strings.HasSuffix(truncated, "-") || strings.HasSuffix(truncated, "_") || strings.HasSuffix(truncated, ".") { if len(truncated) > maxKubernetesLabelLength-1 { return truncated[0:len(truncated)-1] + "z" } return truncated + "z" } return truncated } // ValidateAndSetLinuxNodeBootstrappingConfiguration is exported only for temporary usage in e2e testing of new config. func ValidateAndSetLinuxNodeBootstrappingConfiguration(config *datamodel.NodeBootstrappingConfiguration) { if config.KubeletConfig == nil { return } profile := config.AgentPoolProfile kubeletFlags := config.KubeletConfig // If using kubelet config file, disable DynamicKubeletConfig feature gate and remove dynamic-config-dir // we should only allow users to configure from API (20201101 and later) delete(kubeletFlags, "--dynamic-config-dir") delete(kubeletFlags, "--non-masquerade-cidr") if profile != nil && profile.KubernetesConfig != nil && profile.KubernetesConfig.ContainerRuntime == "containerd" { dockerShimFlags := []string{ "--cni-bin-dir", "--cni-cache-dir", "--cni-conf-dir", "--docker-endpoint", "--image-pull-progress-deadline", "--network-plugin", "--network-plugin-mtu", } for _, flag := range dockerShimFlags { delete(kubeletFlags, flag) } } if IsKubeletServingCertificateRotationEnabled(config) { // ensure the required feature gate is set kubeletFlags["--feature-gates"] = addFeatureGateString(kubeletFlags["--feature-gates"], "RotateKubeletServerCertificate", true) } if IsKubernetesVersionGe(config.ContainerService.Properties.OrchestratorProfile.OrchestratorVersion, "1.24.0") { kubeletFlags["--feature-gates"] = removeFeatureGateString(kubeletFlags["--feature-gates"], "DynamicKubeletConfig") } else if IsKubernetesVersionGe(config.ContainerService.Properties.OrchestratorProfile.OrchestratorVersion, "1.11.0") { kubeletFlags["--feature-gates"] = addFeatureGateString(kubeletFlags["--feature-gates"], "DynamicKubeletConfig", false) } /* ContainerInsights depends on GPU accelerator Usage metrics from Kubelet cAdvisor endpoint but deprecation of this feature moved to beta which breaks the ContainerInsights customers with K8s version 1.20 or higher */ /* Until Container Insights move to new API adding this feature gate to get the GPU metrics continue to work */ /* Reference - https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/1867-disable-accelerator-usage-metrics */ if IsKubernetesVersionGe(config.ContainerService.Properties.OrchestratorProfile.OrchestratorVersion, "1.20.0") && !IsKubernetesVersionGe(config.ContainerService.Properties.OrchestratorProfile.OrchestratorVersion, "1.25.0") { kubeletFlags["--feature-gates"] = addFeatureGateString(kubeletFlags["--feature-gates"], "DisableAcceleratorUsageMetrics", false) } } func validateAndSetWindowsNodeBootstrappingConfiguration(config *datamodel.NodeBootstrappingConfiguration) { if IsTLSBootstrappingEnabledWithHardCodedToken(config.KubeletClientTLSBootstrapToken) { // backfill proper flags for Windows agent node TLS bootstrapping if config.KubeletConfig == nil { config.KubeletConfig = make(map[string]string) } config.KubeletConfig["--bootstrap-kubeconfig"] = "c:\\k\\bootstrap-config" config.KubeletConfig["--cert-dir"] = "c:\\k\\pki" } if config.KubeletConfig != nil { kubeletFlags := config.KubeletConfig delete(kubeletFlags, "--dynamic-config-dir") if IsKubernetesVersionGe(config.ContainerService.Properties.OrchestratorProfile.OrchestratorVersion, "1.24.0") { kubeletFlags["--feature-gates"] = removeFeatureGateString(kubeletFlags["--feature-gates"], "DynamicKubeletConfig") } else if IsKubernetesVersionGe(config.ContainerService.Properties.OrchestratorProfile.OrchestratorVersion, "1.11.0") { kubeletFlags["--feature-gates"] = addFeatureGateString(kubeletFlags["--feature-gates"], "DynamicKubeletConfig", false) } if IsKubeletServingCertificateRotationEnabled(config) { kubeletFlags["--feature-gates"] = addFeatureGateString(kubeletFlags["--feature-gates"], "RotateKubeletServerCertificate", true) } } } // getContainerServiceFuncMap returns all functions used in template generation. /* These funcs are a thin wrapper for template generation operations, all business logic is implemented in the underlying func. */ //nolint:gocognit, funlen, cyclop, gocyclo func getContainerServiceFuncMap(config *datamodel.NodeBootstrappingConfiguration) template.FuncMap { cs := config.ContainerService profile := config.AgentPoolProfile return template.FuncMap{ "Disable1804SystemdResolved": func() bool { return config.Disable1804SystemdResolved }, // This was DisableUnattendedUpgrade when we had UU enabled by default in image. // Now we don't, so we have to deliberately enable it. // Someone smarter than me can fix the API. "EnableUnattendedUpgrade": func() bool { return !config.DisableUnattendedUpgrades }, "IsIPMasqAgentEnabled": func() bool { return cs.Properties.IsIPMasqAgentEnabled() }, "IsKubernetesVersionGe": func(version string) bool { return cs.Properties.OrchestratorProfile.IsKubernetes() && IsKubernetesVersionGe(cs.Properties.OrchestratorProfile.OrchestratorVersion, version) }, "GetAgentKubernetesLabels": func(profile *datamodel.AgentPoolProfile) string { return profile.GetKubernetesLabels() }, "GetAgentKubernetesLabelsDeprecated": func(profile *datamodel.AgentPoolProfile) string { return profile.GetKubernetesLabels() }, "GetGPUInstanceProfile": func() string { return config.GPUInstanceProfile }, "IsMIGEnabledNode": func() bool { return config.GPUInstanceProfile != "" }, "GetKubeletConfigFileContent": func() string { return GetKubeletConfigFileContent(config.KubeletConfig, profile.CustomKubeletConfig) }, "GetKubeletConfigFileContentBase64": func() string { return base64.StdEncoding.EncodeToString([]byte(GetKubeletConfigFileContent(config.KubeletConfig, profile.CustomKubeletConfig))) }, "IsKubeletConfigFileEnabled": func() bool { return IsKubeletConfigFileEnabled(cs, profile, config.EnableKubeletConfigFile) }, "EnableSecureTLSBootstrapping": func() bool { // this will be true when we can perform TLS bootstrapping without the use of a hard-coded bootstrap token. return config.EnableSecureTLSBootstrapping }, "GetCustomSecureTLSBootstrapAADServerAppID": func() string { return config.CustomSecureTLSBootstrapAADServerAppID }, "GetTLSBootstrapTokenForKubeConfig": func() string { return GetTLSBootstrapTokenForKubeConfig(config.KubeletClientTLSBootstrapToken) }, "EnableKubeletServingCertificateRotation": func() bool { return IsKubeletServingCertificateRotationEnabled(config) }, "GetKubeletConfigKeyVals": func() string { return GetOrderedKubeletConfigFlagString(config) }, "GetKubeletConfigKeyValsPsh": func() string { return config.GetOrderedKubeletConfigStringForPowershell(profile.CustomKubeletConfig) }, "GetKubeproxyConfigKeyValsPsh": func() string { return config.GetOrderedKubeproxyConfigStringForPowershell() }, "IsCgroupV2": func() bool { return profile.Is2204VHDDistro() || profile.IsAzureLinuxCgroupV2VHDDistro() || profile.Is2404VHDDistro() }, "GetKubeProxyFeatureGatesPsh": func() string { return cs.Properties.GetKubeProxyFeatureGatesWindowsArguments() }, "ShouldConfigCustomSysctl": func() bool { return profile.CustomLinuxOSConfig != nil && profile.CustomLinuxOSConfig.Sysctls != nil }, "GetCustomSysctlConfigByName": func(fn string) interface{} { if profile.CustomLinuxOSConfig != nil && profile.CustomLinuxOSConfig.Sysctls != nil { v := reflect.ValueOf(*profile.CustomLinuxOSConfig.Sysctls) return v.FieldByName(fn).Interface() } return nil }, "ShouldConfigTransparentHugePage": func() bool { return profile.CustomLinuxOSConfig != nil && (profile.CustomLinuxOSConfig.TransparentHugePageEnabled != "" || profile.CustomLinuxOSConfig.TransparentHugePageDefrag != "") }, "GetTransparentHugePageEnabled": func() string { if profile.CustomLinuxOSConfig == nil { return "" } return profile.CustomLinuxOSConfig.TransparentHugePageEnabled }, "GetTransparentHugePageDefrag": func() string { if profile.CustomLinuxOSConfig == nil { return "" } return profile.CustomLinuxOSConfig.TransparentHugePageDefrag }, "ShouldConfigSwapFile": func() bool { // only configure swap file when FailSwapOn is false and SwapFileSizeMB is valid return profile.CustomKubeletConfig != nil && profile.CustomKubeletConfig.FailSwapOn != nil && !*profile.CustomKubeletConfig.FailSwapOn && profile.CustomLinuxOSConfig != nil && profile.CustomLinuxOSConfig.SwapFileSizeMB != nil && *profile.CustomLinuxOSConfig.SwapFileSizeMB > 0 }, "GetSwapFileSizeMB": func() int32 { if profile.CustomLinuxOSConfig != nil && profile.CustomLinuxOSConfig.SwapFileSizeMB != nil { return *profile.CustomLinuxOSConfig.SwapFileSizeMB } return 0 }, "ShouldConfigContainerdUlimits": func() bool { return profile.GetCustomLinuxOSConfig().GetUlimitConfig() != nil }, "GetContainerdUlimitString": func() string { ulimitConfig := profile.GetCustomLinuxOSConfig().GetUlimitConfig() if ulimitConfig == nil { return "" } var sb strings.Builder sb.WriteString("[Service]\n") if ulimitConfig.MaxLockedMemory != "" { sb.WriteString(fmt.Sprintf("LimitMEMLOCK=%s\n", ulimitConfig.MaxLockedMemory)) } if ulimitConfig.NoFile != "" { // ulimit is removed in containerd 2.0+, which is available only in ubuntu2404 distro // https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md#limitnofile-configuration-has-been-removed if !profile.Is2404VHDDistro() { sb.WriteString(fmt.Sprintf("LimitNOFILE=%s\n", ulimitConfig.NoFile)) } } return sb.String() }, "IsKubernetes": func() bool { return cs.Properties.OrchestratorProfile.IsKubernetes() }, "GetKubernetesEndpoint": func() string { return GetKubernetesEndpoint(cs) }, "IsAzureCNI": func() bool { return cs.Properties.OrchestratorProfile.IsAzureCNI() }, "IsNoneCNI": func() bool { return cs.Properties.OrchestratorProfile.IsNoneCNI() }, "IsMariner": func() bool { // TODO(ace): do we care about both? 2nd one should be more general and catch custom VHD for mariner return profile.Distro.IsAzureLinuxDistro() || isMariner(config.OSSKU) }, "IsKata": func() bool { return profile.Distro.IsKataDistro() }, "IsCustomImage": func() bool { return profile.Distro == datamodel.CustomizedImage || profile.Distro == datamodel.CustomizedImageKata }, "EnableHostsConfigAgent": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig != nil && cs.Properties.OrchestratorProfile.KubernetesConfig.PrivateCluster != nil && to.Bool(cs.Properties.OrchestratorProfile.KubernetesConfig.PrivateCluster.EnableHostsConfigAgent) }, "UseManagedIdentity": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.UseManagedIdentity }, "GetSshPublicKeysPowerShell": func() string { return getSSHPublicKeysPowerShell(cs.Properties.LinuxProfile) }, "GetKubernetesAgentPreprovisionYaml": func(profile *datamodel.AgentPoolProfile) string { str := "" if profile.PreprovisionExtension != nil { str += "\n" str += makeAgentExtensionScriptCommands(cs, profile) } return str }, "GetKubernetesWindowsAgentFunctions": func() string { // Collect all the parts into a zip neededParts := []string{ kubernetesWindowsCSEHelperPS1, kubernetesWindowsSendLogsPS1, } // Create a buffer, new zip buf := new(bytes.Buffer) zw := zip.NewWriter(buf) for _, part := range neededParts { f, err := zw.Create(part) if err != nil { panic(err) } partContents, err := parts.Templates.ReadFile(part) if err != nil { panic(err) } _, err = f.Write(partContents) if err != nil { panic(err) } } err := zw.Close() if err != nil { panic(err) } return base64.StdEncoding.EncodeToString(buf.Bytes()) }, "IsNSeriesSKU": func() bool { return config.EnableNvidia }, "HasCustomSearchDomain": func() bool { return cs.Properties.LinuxProfile != nil && cs.Properties.LinuxProfile.HasSearchDomain() }, "GetSearchDomainName": func() string { if cs.Properties.LinuxProfile != nil && cs.Properties.LinuxProfile.HasSearchDomain() { return cs.Properties.LinuxProfile.CustomSearchDomain.Name } return "" }, "GetSearchDomainRealmUser": func() string { if cs.Properties.LinuxProfile != nil && cs.Properties.LinuxProfile.HasSearchDomain() { return cs.Properties.LinuxProfile.CustomSearchDomain.RealmUser } return "" }, "GetSearchDomainRealmPassword": func() string { if cs.Properties.LinuxProfile != nil && cs.Properties.LinuxProfile.HasSearchDomain() { return cs.Properties.LinuxProfile.CustomSearchDomain.RealmPassword } return "" }, "HasCalicoNetworkPolicy": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.NetworkPolicy == NetworkPolicyCalico }, "HasAntreaNetworkPolicy": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.NetworkPolicy == NetworkPolicyAntrea }, "HasFlannelNetworkPlugin": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.NetworkPlugin == NetworkPluginFlannel }, "HasKubeletClientKey": func() bool { return cs.Properties.CertificateProfile != nil && cs.Properties.CertificateProfile.ClientPrivateKey != "" }, "GetKubeletClientKey": func() string { if cs.Properties.CertificateProfile != nil && cs.Properties.CertificateProfile.ClientPrivateKey != "" { encoded := base64.StdEncoding.EncodeToString([]byte(cs.Properties.CertificateProfile.ClientPrivateKey)) return encoded } return "" }, "GetKubeletClientCert": func() string { if cs.Properties.CertificateProfile != nil && cs.Properties.CertificateProfile.ClientCertificate != "" { encoded := base64.StdEncoding.EncodeToString([]byte(cs.Properties.CertificateProfile.ClientCertificate)) return encoded } return "" }, "HasServicePrincipalSecret": func() bool { return cs.Properties.ServicePrincipalProfile != nil && cs.Properties.ServicePrincipalProfile.Secret != "" }, "GetServicePrincipalSecret": func() string { if cs.Properties.ServicePrincipalProfile != nil && cs.Properties.ServicePrincipalProfile.Secret != "" { encoded := base64.StdEncoding.EncodeToString([]byte(cs.Properties.ServicePrincipalProfile.Secret)) return encoded } return "" }, "WindowsSSHEnabled": func() bool { return cs.Properties.WindowsProfile.GetSSHEnabled() }, "IsIPv6DualStackFeatureEnabled": func() bool { return cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") }, "IsAzureCNIOverlayFeatureEnabled": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.IsUsingNetworkPluginMode("overlay") }, "CiliumDataplaneEnabled": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.EbpfDataplane == datamodel.EbpfDataplane_cilium }, "GetBase64EncodedEnvironmentJSON": func() string { customEnvironmentJSON, _ := cs.Properties.GetCustomEnvironmentJSON(false) return base64.StdEncoding.EncodeToString([]byte(customEnvironmentJSON)) }, "GetIdentitySystem": func() string { return datamodel.AzureADIdentitySystem }, "GetPodInfraContainerSpec": func() string { return config.K8sComponents.PodInfraContainerImageURL }, "IsKubenet": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.NetworkPlugin == NetworkPluginKubenet }, "NeedsContainerd": func() bool { if profile != nil && profile.KubernetesConfig != nil && profile.KubernetesConfig.ContainerRuntime != "" { return profile.KubernetesConfig.NeedsContainerd() } return cs.Properties.OrchestratorProfile.KubernetesConfig.NeedsContainerd() }, "UseRuncShimV2": func() bool { return config.EnableRuncShimV2 }, "IsDockerContainerRuntime": func() bool { if profile != nil && profile.KubernetesConfig != nil && profile.KubernetesConfig.ContainerRuntime != "" { return profile.KubernetesConfig.ContainerRuntime == datamodel.Docker } return cs.Properties.OrchestratorProfile.KubernetesConfig.ContainerRuntime == datamodel.Docker }, "RequiresDocker": func() bool { if profile != nil && profile.KubernetesConfig != nil && profile.KubernetesConfig.ContainerRuntime != "" { return profile.KubernetesConfig.RequiresDocker() } return cs.Properties.OrchestratorProfile.KubernetesConfig.RequiresDocker() }, "HasDataDir": func() bool { return HasDataDir(config) }, "GetDataDir": func() string { return GetDataDir(config) }, "HasKubeletDiskType": func() bool { return profile != nil && profile.KubeletDiskType != "" && profile.KubeletDiskType != datamodel.OSDisk }, "GetKubeletDiskType": func() string { if profile != nil && profile.KubeletDiskType != "" && profile.KubeletDiskType != datamodel.OSDisk { return string(profile.KubeletDiskType) } return "" }, "IsKrustlet": func() bool { return strings.EqualFold(string(profile.WorkloadRuntime), string(datamodel.WasmWasi)) }, "GetBase64CertificateAuthorityData": func() string { if cs != nil && cs.Properties != nil && cs.Properties.CertificateProfile != nil && cs.Properties.CertificateProfile.CaCertificate != "" { data := cs.Properties.CertificateProfile.CaCertificate return base64.StdEncoding.EncodeToString([]byte(data)) } return "" }, "GetKubenetTemplate": func() string { return base64.StdEncoding.EncodeToString([]byte(kubenetCniTemplate)) }, "GetContainerdConfigContent": func() string { output, err := containerdConfigFromTemplate(config, profile, func(profile *datamodel.AgentPoolProfile) ContainerdConfigTemplate { if profile.Is2404VHDDistro() { return containerdV2ConfigTemplate } return containerdV1ConfigTemplate }(profile)) if err != nil { panic(err) } return output }, "GetContainerdConfigNoGPUContent": func() string { output, err := containerdConfigFromTemplate(config, profile, func(profile *datamodel.AgentPoolProfile) ContainerdConfigTemplate { if profile.Is2404VHDDistro() { return containerdV2NoGPUConfigTemplate } return containerdV1NoGPUConfigTemplate }(profile)) if err != nil { panic(err) } return output }, "TeleportEnabled": func() bool { return config.EnableACRTeleportPlugin }, "HasDCSeriesSKU": func() bool { return cs.Properties.HasDCSeriesSKU() }, "GetHyperkubeImageReference": func() string { return config.K8sComponents.HyperkubeImageURL }, "GetLinuxPrivatePackageURL": func() string { return config.K8sComponents.LinuxPrivatePackageURL }, "GetTargetEnvironment": func() string { if cs.IsAKSCustomCloud() { return cs.Properties.CustomCloudEnv.Name } return GetCloudTargetEnv(cs.Location) }, "IsAKSCustomCloud": func() bool { return cs.IsAKSCustomCloud() }, "GetInitAKSCustomCloudFilepath": func() string { return initAKSCustomCloudFilepath }, "AKSCustomCloudRepoDepotEndpoint": func() string { return cs.Properties.CustomCloudEnv.RepoDepotEndpoint }, "AKSCustomCloudManagementPortalURL": func() string { return cs.Properties.CustomCloudEnv.ManagementPortalURL }, "AKSCustomCloudPublishSettingsURL": func() string { return cs.Properties.CustomCloudEnv.PublishSettingsURL }, "AKSCustomCloudServiceManagementEndpoint": func() string { return cs.Properties.CustomCloudEnv.ServiceManagementEndpoint }, "AKSCustomCloudResourceManagerEndpoint": func() string { return cs.Properties.CustomCloudEnv.ResourceManagerEndpoint }, "AKSCustomCloudActiveDirectoryEndpoint": func() string { return cs.Properties.CustomCloudEnv.ActiveDirectoryEndpoint }, "AKSCustomCloudGalleryEndpoint": func() string { return cs.Properties.CustomCloudEnv.GalleryEndpoint }, "AKSCustomCloudKeyVaultEndpoint": func() string { return cs.Properties.CustomCloudEnv.KeyVaultEndpoint }, "AKSCustomCloudGraphEndpoint": func() string { return cs.Properties.CustomCloudEnv.GraphEndpoint }, "AKSCustomCloudServiceBusEndpoint": func() string { return cs.Properties.CustomCloudEnv.ServiceBusEndpoint }, "AKSCustomCloudBatchManagementEndpoint": func() string { return cs.Properties.CustomCloudEnv.BatchManagementEndpoint }, "AKSCustomCloudStorageEndpointSuffix": func() string { return cs.Properties.CustomCloudEnv.StorageEndpointSuffix }, "AKSCustomCloudSqlDatabaseDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.SQLDatabaseDNSSuffix }, "AKSCustomCloudTrafficManagerDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.TrafficManagerDNSSuffix }, "AKSCustomCloudKeyVaultDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.KeyVaultDNSSuffix }, "AKSCustomCloudServiceBusEndpointSuffix": func() string { return cs.Properties.CustomCloudEnv.ServiceBusEndpointSuffix }, "AKSCustomCloudServiceManagementVMDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.ServiceManagementVMDNSSuffix }, "AKSCustomCloudResourceManagerVMDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.ResourceManagerVMDNSSuffix }, "AKSCustomCloudContainerRegistryDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.ContainerRegistryDNSSuffix }, "AKSCustomCloudCosmosDBDNSSuffix": func() string { return cs.Properties.CustomCloudEnv.CosmosDBDNSSuffix }, "AKSCustomCloudTokenAudience": func() string { return cs.Properties.CustomCloudEnv.TokenAudience }, "AKSCustomCloudResourceIdentifiersGraph": func() string { return cs.Properties.CustomCloudEnv.ResourceIdentifiers.Graph }, "AKSCustomCloudResourceIdentifiersKeyVault": func() string { return cs.Properties.CustomCloudEnv.ResourceIdentifiers.KeyVault }, "AKSCustomCloudResourceIdentifiersDatalake": func() string { return cs.Properties.CustomCloudEnv.ResourceIdentifiers.Datalake }, "AKSCustomCloudResourceIdentifiersBatch": func() string { return cs.Properties.CustomCloudEnv.ResourceIdentifiers.Batch }, "AKSCustomCloudResourceIdentifiersOperationalInsights": func() string { return cs.Properties.CustomCloudEnv.ResourceIdentifiers.OperationalInsights }, "AKSCustomCloudResourceIdentifiersStorage": func() string { return cs.Properties.CustomCloudEnv.ResourceIdentifiers.Storage }, "GetCSEHelpersScriptFilepath": func() string { return cseHelpersScriptFilepath }, "GetCSEHelpersScriptDistroFilepath": func() string { return cseHelpersScriptDistroFilepath }, "GetCSEInstallScriptFilepath": func() string { return cseInstallScriptFilepath }, "GetCSEInstallScriptDistroFilepath": func() string { return cseInstallScriptDistroFilepath }, "GetCSEConfigScriptFilepath": func() string { return cseConfigScriptFilepath }, "GetCustomSearchDomainsCSEScriptFilepath": func() string { return customSearchDomainsCSEScriptFilepath }, "GetDHCPv6ServiceCSEScriptFilepath": func() string { return dhcpV6ServiceCSEScriptFilepath }, "GetDHCPv6ConfigCSEScriptFilepath": func() string { return dhcpV6ConfigCSEScriptFilepath }, "HasPrivateAzureRegistryServer": func() bool { return cs.Properties.OrchestratorProfile.KubernetesConfig.PrivateAzureRegistryServer != "" }, "GetPrivateAzureRegistryServer": func() string { return cs.Properties.OrchestratorProfile.KubernetesConfig.PrivateAzureRegistryServer }, "OpenBraces": func() string { return "{{" }, "CloseBraces": func() string { return "}}" }, "BoolPtrToInt": func(p *bool) int { if p == nil { return 0 } if v := *p; v { return 1 } return 0 }, "UserAssignedIDEnabled": func() bool { // TODO(qinhao): we need to move this to NodeBootstrappingConfiguration as cs.Properties // is to be moved away from NodeBootstrappingConfiguration return cs.Properties.OrchestratorProfile.KubernetesConfig.UserAssignedIDEnabled() }, // HTTP proxy related funcs "ShouldConfigureHTTPProxy": func() bool { return config.HTTPProxyConfig != nil && (config.HTTPProxyConfig.HTTPProxy != nil || config.HTTPProxyConfig.HTTPSProxy != nil) }, "HasHTTPProxy": func() bool { return config.HTTPProxyConfig != nil && config.HTTPProxyConfig.HTTPProxy != nil }, "HasHTTPSProxy": func() bool { return config.HTTPProxyConfig != nil && config.HTTPProxyConfig.HTTPSProxy != nil }, "HasNoProxy": func() bool { return config.HTTPProxyConfig != nil && config.HTTPProxyConfig.NoProxy != nil }, "GetHTTPProxy": func() string { if config.HTTPProxyConfig != nil && config.HTTPProxyConfig.HTTPProxy != nil { return *config.HTTPProxyConfig.HTTPProxy } return "" }, "GetHTTPSProxy": func() string { if config.HTTPProxyConfig != nil && config.HTTPProxyConfig.HTTPSProxy != nil { return *config.HTTPProxyConfig.HTTPSProxy } return "" }, "GetNoProxy": func() string { if config.HTTPProxyConfig != nil && config.HTTPProxyConfig.NoProxy != nil { return strings.Join(*config.HTTPProxyConfig.NoProxy, ",") } return "" }, "ShouldConfigureHTTPProxyCA": func() bool { return config.HTTPProxyConfig != nil && config.HTTPProxyConfig.TrustedCA != nil }, "GetHTTPProxyCA": func() string { if config.HTTPProxyConfig != nil && config.HTTPProxyConfig.TrustedCA != nil { return *config.HTTPProxyConfig.TrustedCA } return "" }, "FIPSEnabled": func() bool { return config.FIPSEnabled }, "GetMessageOfTheDay": func() string { return profile.MessageOfTheDay }, "GetProxyVariables": func() string { return getProxyVariables(config) }, "GetOutboundCommand": func() string { return getOutBoundCmd(config, config.CloudSpecConfig) }, "BlockOutboundNetwork": func() bool { if config.OutboundType == datamodel.OutboundTypeBlock || config.OutboundType == datamodel.OutboundTypeNone { return true } return false }, "GPUNeedsFabricManager": func() bool { return GPUNeedsFabricManager(profile.VMSize) }, "GPUDriverVersion": func() string { return GetGPUDriverVersion(profile.VMSize) }, "GPUImageSHA": func() string { return GetAKSGPUImageSHA(profile.VMSize) }, "GPUDriverType": func() string { return GetGPUDriverType(profile.VMSize) }, "GetHnsRemediatorIntervalInMinutes": func() uint32 { // Only need to enable HNSRemediator for Windows 2019 if cs.Properties.WindowsProfile != nil && profile.Distro == datamodel.AKSWindows2019Containerd { return cs.Properties.WindowsProfile.GetHnsRemediatorIntervalInMinutes() } return 0 }, "ShouldConfigureCustomCATrust": func() bool { return areCustomCATrustCertsPopulated(*config) }, "GetCustomCATrustConfigCerts": func() []string { if areCustomCATrustCertsPopulated(*config) { return config.CustomCATrustConfig.CustomCATrustCerts } return []string{} }, "GetLogGeneratorIntervalInMinutes": func() uint32 { if cs.Properties.WindowsProfile != nil { return cs.Properties.WindowsProfile.GetLogGeneratorIntervalInMinutes() } return 0 }, "ShouldDisableSSH": func() bool { return config.SSHStatus == datamodel.SSHOff }, "GetSysctlContent": func() (string, error) { templateFuncMap := make(template.FuncMap) templateFuncMap["getPortRangeEndValue"] = getPortRangeEndValue sysctlTemplate, err := template.New("sysctl").Funcs(templateFuncMap).Parse(sysctlTemplateString) if err != nil { return "", fmt.Errorf("failed to parse sysctl template: %w", err) } var b bytes.Buffer if err = sysctlTemplate.Execute(&b, profile); err != nil { return "", fmt.Errorf("failed to execute sysctl template: %w", err) } return base64.StdEncoding.EncodeToString(b.Bytes()), nil }, "ShouldEnableCustomData": func() bool { return !config.DisableCustomData }, "GetPrivateEgressProxyAddress": func() string { return config.ContainerService.Properties.SecurityProfile.GetProxyAddress() }, "GetBootstrapProfileContainerRegistryServer": func() string { return config.ContainerService.Properties.SecurityProfile.GetPrivateEgressContainerRegistryServer() }, "GetMCRRepositoryBase": func() string { if config.CloudSpecConfig.KubernetesSpecConfig.MCRKubernetesImageBase == "" { return "mcr.microsoft.com" } return config.CloudSpecConfig.KubernetesSpecConfig.MCRKubernetesImageBase }, "IsArtifactStreamingEnabled": func() bool { return config.EnableArtifactStreaming }, "EnableIMDSRestriction": func() bool { return config.EnableIMDSRestriction }, "InsertIMDSRestrictionRuleToMangleTable": func() bool { return config.InsertIMDSRestrictionRuleToMangleTable }, "ShouldEnableLocalDNS": func() bool { return profile.ShouldEnableLocalDNS() }, "GetGeneratedLocalDNSCoreFile": func() (string, error) { output, err := GenerateLocalDNSCoreFile(config, profile, localDNSCoreFileTemplateString) if err != nil { return "", fmt.Errorf("failed generate corefile for localdns using template: %w", err) } return output, nil }, "GetLocalDNSCPULimitInPercentage": func() string { return profile.GetLocalDNSCPULimitInPercentage() }, "GetLocalDNSMemoryLimitInMB": func() string { return profile.GetLocalDNSMemoryLimitInMB() }, } } func GetDataDir(config *datamodel.NodeBootstrappingConfiguration) string { cs := config.ContainerService profile := config.AgentPoolProfile if profile != nil && profile.KubernetesConfig != nil && profile.KubernetesConfig.ContainerRuntimeConfig != nil && profile.KubernetesConfig.ContainerRuntimeConfig[datamodel.ContainerDataDirKey] != "" { return profile.KubernetesConfig.ContainerRuntimeConfig[datamodel.ContainerDataDirKey] } if profile.KubeletDiskType == datamodel.TempDisk { return datamodel.TempDiskContainerDataDir } return cs.Properties.OrchestratorProfile.KubernetesConfig.ContainerRuntimeConfig[datamodel.ContainerDataDirKey] } func HasDataDir(config *datamodel.NodeBootstrappingConfiguration) bool { cs := config.ContainerService profile := config.AgentPoolProfile if profile != nil && profile.KubernetesConfig != nil && profile.KubernetesConfig.ContainerRuntimeConfig != nil && profile.KubernetesConfig.ContainerRuntimeConfig[datamodel.ContainerDataDirKey] != "" { return true } if profile.KubeletDiskType == datamodel.TempDisk { return true } return cs.Properties.OrchestratorProfile.KubernetesConfig.ContainerRuntimeConfig != nil && cs.Properties.OrchestratorProfile.KubernetesConfig.ContainerRuntimeConfig[datamodel.ContainerDataDirKey] != "" } func GetKubernetesEndpoint(cs *datamodel.ContainerService) string { if cs.Properties.HostedMasterProfile == nil { return "" } if cs.Properties.HostedMasterProfile.IPAddress != "" { return cs.Properties.HostedMasterProfile.IPAddress } return cs.Properties.HostedMasterProfile.FQDN } func getPortRangeEndValue(portRange string) int { arr := strings.Split(portRange, " ") num, err := strconv.Atoi(arr[1]) if err != nil { return -1 } return num } // NV series GPUs target graphics workloads vs NC which targets compute. // they typically use GRID, not CUDA drivers, and will fail to install CUDA drivers. // NVv1 seems to run with CUDA, NVv5 requires GRID. // NVv3 is untested on AKS, NVv4 is AMD so n/a, and NVv2 no longer seems to exist (?). func GetGPUDriverVersion(size string) string { if useGridDrivers(size) { return datamodel.NvidiaGridDriverVersion } if isStandardNCv1(size) { return datamodel.Nvidia470CudaDriverVersion } return datamodel.NvidiaCudaDriverVersion } func isStandardNCv1(size string) bool { tmp := strings.ToLower(size) return strings.HasPrefix(tmp, "standard_nc") && !strings.Contains(tmp, "_v") } func useGridDrivers(size string) bool { return datamodel.ConvergedGPUDriverSizes[strings.ToLower(size)] } func GetAKSGPUImageSHA(size string) string { if useGridDrivers(size) { return datamodel.AKSGPUGridVersionSuffix } return datamodel.AKSGPUCudaVersionSuffix } func GetGPUDriverType(size string) string { if useGridDrivers(size) { return "grid" } return "cuda" } func GPUNeedsFabricManager(size string) bool { return datamodel.FabricManagerGPUSizes[strings.ToLower(size)] } func areCustomCATrustCertsPopulated(config datamodel.NodeBootstrappingConfiguration) bool { return config.CustomCATrustConfig != nil && len(config.CustomCATrustConfig.CustomCATrustCerts) > 0 } func isMariner(osSku string) bool { return osSku == datamodel.OSSKUCBLMariner || osSku == datamodel.OSSKUMariner || osSku == datamodel.OSSKUAzureLinux } const sysctlTemplateString = `# This is a partial workaround to this upstream Kubernetes issue: # https://github.com/kubernetes/kubernetes/issues/41916#issuecomment-312428731 net.ipv4.tcp_retries2=8 net.core.message_burst=80 net.core.message_cost=40 {{- if .CustomLinuxOSConfig}} {{- if .CustomLinuxOSConfig.Sysctls}} {{- if .CustomLinuxOSConfig.Sysctls.NetCoreSomaxconn}} net.core.somaxconn={{.CustomLinuxOSConfig.Sysctls.NetCoreSomaxconn}} {{- else}} net.core.somaxconn=16384 {{- end}} {{- if .CustomLinuxOSConfig.Sysctls.NetIpv4TcpMaxSynBacklog}} net.ipv4.tcp_max_syn_backlog={{.CustomLinuxOSConfig.Sysctls.NetIpv4TcpMaxSynBacklog}} {{- else}} net.ipv4.tcp_max_syn_backlog=16384 {{- end}} {{- if .CustomLinuxOSConfig.Sysctls.NetIpv4NeighDefaultGcThresh1}} net.ipv4.neigh.default.gc_thresh1={{.CustomLinuxOSConfig.Sysctls.NetIpv4NeighDefaultGcThresh1}} {{- else}} net.ipv4.neigh.default.gc_thresh1=4096 {{- end}} {{- if .CustomLinuxOSConfig.Sysctls.NetIpv4NeighDefaultGcThresh2}} net.ipv4.neigh.default.gc_thresh2={{.CustomLinuxOSConfig.Sysctls.NetIpv4NeighDefaultGcThresh2}} {{- else}} net.ipv4.neigh.default.gc_thresh2=8192 {{- end}} {{- if .CustomLinuxOSConfig.Sysctls.NetIpv4NeighDefaultGcThresh3}} net.ipv4.neigh.default.gc_thresh3={{.CustomLinuxOSConfig.Sysctls.NetIpv4NeighDefaultGcThresh3}} {{- else}} net.ipv4.neigh.default.gc_thresh3=16384 {{- end}} {{- else}} net.core.somaxconn=16384 net.ipv4.tcp_max_syn_backlog=16384 net.ipv4.neigh.default.gc_thresh1=4096 net.ipv4.neigh.default.gc_thresh2=8192 net.ipv4.neigh.default.gc_thresh3=16384 {{- end}} {{- else}} net.core.somaxconn=16384 net.ipv4.tcp_max_syn_backlog=16384 net.ipv4.neigh.default.gc_thresh1=4096 net.ipv4.neigh.default.gc_thresh2=8192 net.ipv4.neigh.default.gc_thresh3=16384 {{- end}} {{- if .CustomLinuxOSConfig}} {{- if .CustomLinuxOSConfig.Sysctls}} # The following are sysctl configs passed from API {{- $s:=.CustomLinuxOSConfig.Sysctls}} {{- if $s.NetCoreNetdevMaxBacklog}} net.core.netdev_max_backlog={{$s.NetCoreNetdevMaxBacklog}} {{- end}} {{- if $s.NetCoreRmemDefault}} net.core.rmem_default={{$s.NetCoreRmemDefault}} {{- end}} {{- if $s.NetCoreRmemMax}} net.core.rmem_max={{$s.NetCoreRmemMax}} {{- end}} {{- if $s.NetCoreWmemDefault}} net.core.wmem_default={{$s.NetCoreWmemDefault}} {{- end}} {{- if $s.NetCoreWmemMax}} net.core.wmem_max={{$s.NetCoreWmemMax}} {{- end}} {{- if $s.NetCoreOptmemMax}} net.core.optmem_max={{$s.NetCoreOptmemMax}} {{- end}} {{- if $s.NetIpv4TcpMaxTwBuckets}} net.ipv4.tcp_max_tw_buckets={{$s.NetIpv4TcpMaxTwBuckets}} {{- end}} {{- if $s.NetIpv4TcpFinTimeout}} net.ipv4.tcp_fin_timeout={{$s.NetIpv4TcpFinTimeout}} {{- end}} {{- if $s.NetIpv4TcpKeepaliveTime}} net.ipv4.tcp_keepalive_time={{$s.NetIpv4TcpKeepaliveTime}} {{- end}} {{- if $s.NetIpv4TcpKeepaliveProbes}} net.ipv4.tcp_keepalive_probes={{$s.NetIpv4TcpKeepaliveProbes}} {{- end}} {{- if $s.NetIpv4TcpkeepaliveIntvl}} net.ipv4.tcp_keepalive_intvl={{$s.NetIpv4TcpkeepaliveIntvl}} {{- end}} {{- if $s.NetIpv4TcpTwReuse}} net.ipv4.tcp_tw_reuse={{if $s.NetIpv4TcpTwReuse}}1{{else}}0{{end}} {{- end}} {{- if $s.NetIpv4IpLocalPortRange}} net.ipv4.ip_local_port_range={{$s.NetIpv4IpLocalPortRange}} {{$rangeEnd := getPortRangeEndValue $s.NetIpv4IpLocalPortRange}} {{ if ge $rangeEnd 65330}} net.ipv4.ip_local_reserved_ports=65330 {{- end}} {{- end}} {{- if $s.NetNetfilterNfConntrackMax}} net.netfilter.nf_conntrack_max={{$s.NetNetfilterNfConntrackMax}} {{- end}} {{- if $s.NetNetfilterNfConntrackBuckets}} net.netfilter.nf_conntrack_buckets={{$s.NetNetfilterNfConntrackBuckets}} {{- end}} {{- if $s.FsInotifyMaxUserWatches}} fs.inotify.max_user_watches={{$s.FsInotifyMaxUserWatches}} {{- end}} {{- if $s.FsFileMax}} fs.file-max={{$s.FsFileMax}} {{- end}} {{- if $s.FsAioMaxNr}} fs.aio-max-nr={{$s.FsAioMaxNr}} {{- end}} {{- if $s.FsNrOpen}} fs.nr_open={{$s.FsNrOpen}} {{- end}} {{- if $s.KernelThreadsMax}} kernel.threads-max={{$s.KernelThreadsMax}} {{- end}} {{- if $s.VMMaxMapCount}} vm.max_map_count={{$s.VMMaxMapCount}} {{- end}} {{- if $s.VMSwappiness}} vm.swappiness={{$s.VMSwappiness}} {{- end}} {{- if $s.VMVfsCachePressure}} vm.vfs_cache_pressure={{$s.VMVfsCachePressure}} {{- end}} {{- end}} {{- end}} ` const kubenetCniTemplate = ` { "cniVersion": "0.3.1", "name": "kubenet", "plugins": [{ "type": "bridge", "bridge": "cbr0", "mtu": 1500, "addIf": "eth0", "isGateway": true, "ipMasq": false, "promiscMode": true, "hairpinMode": false, "ipam": { "type": "host-local", "ranges": [{{range $i, $range := .PodCIDRRanges}}{{if $i}}, {{end}}[{"subnet": "{{$range}}"}]{{end}}], "routes": [{{range $i, $route := .Routes}}{{if $i}}, {{end}}{"dst": "{{$route}}"}{{end}}] } }, { "type": "portmap", "capabilities": {"portMappings": true}, "externalSetMarkChain": "KUBE-MARK-MASQ" }] } ` type ContainerdConfigTemplate string // this pains me, but to make it respect mutability of vmss tags, // we cannot use go templates at runtime. // CSE needs to be able to generate the full config, with all params, // with the tags pulled from wireserver. this is a hack to avoid // moving all the go templates to CSE -- we allow two options, // duplicate them in CSE base64-encoded, and pick the right one. // they're identical except for GPU runtime class. const ( containerdV1ConfigTemplate ContainerdConfigTemplate = `version = 2 oom_score = -999{{if HasDataDir }} root = "{{GetDataDir}}"{{- end}} [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "{{GetPodInfraContainerSpec}}" [plugins."io.containerd.grpc.v1.cri".containerd] {{- if TeleportEnabled }} snapshotter = "teleportd" disable_snapshot_annotations = false {{- else}} {{- if IsKata }} disable_snapshot_annotations = false {{- end}} {{- end}} {{- if IsArtifactStreamingEnabled }} snapshotter = "overlaybd" disable_snapshot_annotations = false {{- end}} {{- if IsNSeriesSKU }} default_runtime_name = "nvidia-container-runtime" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-container-runtime] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia-container-runtime.options] BinaryName = "/usr/bin/nvidia-container-runtime" {{- if IsCgroupV2 }} SystemdCgroup = true {{- end}} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted.options] BinaryName = "/usr/bin/nvidia-container-runtime" {{- else}} default_runtime_name = "runc" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] BinaryName = "/usr/bin/runc" {{- if IsCgroupV2 }} SystemdCgroup = true {{- end}} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted.options] BinaryName = "/usr/bin/runc" {{- end}} {{- if IsKrustlet }} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin] runtime_type = "io.containerd.spin.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-3-0] runtime_type = "io.containerd.spin-v0-3-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight-v0-3-0] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-5-1] runtime_type = "io.containerd.spin-v0-5-1.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight-v0-5-1] runtime_type = "io.containerd.slight-v0-5-1.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-8-0] runtime_type = "io.containerd.spin-v0-8-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight-v0-8-0] runtime_type = "io.containerd.slight-v0-8-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wws-v0-8-0] runtime_type = "io.containerd.wws-v0-8-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-15-1] runtime_type = "io.containerd.spin.v2" {{- end}} {{- if and (IsKubenet) (not HasCalicoNetworkPolicy) }} [plugins."io.containerd.grpc.v1.cri".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" conf_template = "/etc/containerd/kubenet_template.conf" {{- end}} {{- if IsKubernetesVersionGe "1.22.0"}} [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" {{- end}} [plugins."io.containerd.grpc.v1.cri".registry.headers] X-Meta-Source-Client = ["azure/aks"] [metrics] address = "0.0.0.0:10257" {{- if TeleportEnabled }} [proxy_plugins] [proxy_plugins.teleportd] type = "snapshot" address = "/run/teleportd/snapshotter.sock" {{- end}} {{- if IsArtifactStreamingEnabled }} [proxy_plugins] [proxy_plugins.overlaybd] type = "snapshot" address = "/run/overlaybd-snapshotter/overlaybd.sock" {{- end}} {{- if IsKata }} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata] runtime_type = "io.containerd.kata.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.katacli] runtime_type = "io.containerd.runc.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.katacli.options] NoPivotRoot = false NoNewKeyring = false ShimCgroup = "" IoUid = 0 IoGid = 0 BinaryName = "/usr/bin/kata-runtime" Root = "" CriuPath = "" SystemdCgroup = false [proxy_plugins] [proxy_plugins.tardev] type = "snapshot" address = "/run/containerd/tardev-snapshotter.sock" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-cc] snapshotter = "tardev" runtime_type = "io.containerd.kata-cc.v2" privileged_without_host_devices = true pod_annotations = ["io.katacontainers.*"] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-cc.options] ConfigPath = "/opt/confidential-containers/share/defaults/kata-containers/configuration-clh-snp.toml" {{- end}} ` containerdV2ConfigTemplate ContainerdConfigTemplate = `version = 2 oom_score = -999{{if HasDataDir }} root = "{{GetDataDir}}"{{- end}} [plugins."io.containerd.cri.v1.images"] {{- if TeleportEnabled }} snapshotter = "teleportd" disable_snapshot_annotations = false {{- end}} {{- if IsArtifactStreamingEnabled }} snapshotter = "overlaybd" disable_snapshot_annotations = false {{- end}} [plugins."io.containerd.cri.v1.images".pinned_images] sandbox = "{{GetPodInfraContainerSpec}}" {{- if IsKubernetesVersionGe "1.22.0"}} [plugins."io.containerd.cri.v1.images".registry] config_path = "/etc/containerd/certs.d" {{- end}} [plugins."io.containerd.cri.v1.images".registry.headers] X-Meta-Source-Client = ["azure/aks"] [plugins."io.containerd.cri.v1.runtime".containerd] {{- if IsNSeriesSKU }} default_runtime_name = "nvidia-container-runtime" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.nvidia-container-runtime] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.nvidia-container-runtime.options] BinaryName = "/usr/bin/nvidia-container-runtime" SystemdCgroup = true [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.untrusted] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.untrusted.options] BinaryName = "/usr/bin/nvidia-container-runtime" {{- else}} default_runtime_name = "runc" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options] BinaryName = "/usr/bin/runc" SystemdCgroup = true [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.untrusted] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.untrusted.options] BinaryName = "/usr/bin/runc" {{- end}} {{- if IsKrustlet }} [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin] runtime_type = "io.containerd.spin.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-3-0] runtime_type = "io.containerd.spin-v0-3-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight-v0-3-0] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-5-1] runtime_type = "io.containerd.spin-v0-5-1.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight-v0-5-1] runtime_type = "io.containerd.slight-v0-5-1.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-8-0] runtime_type = "io.containerd.spin-v0-8-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight-v0-8-0] runtime_type = "io.containerd.slight-v0-8-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.wws-v0-8-0] runtime_type = "io.containerd.wws-v0-8-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-15-1] runtime_type = "io.containerd.spin.v2" {{- end}} {{- if and (IsKubenet) (not HasCalicoNetworkPolicy) }} [plugins."io.containerd.cri.v1.runtime".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" conf_template = "/etc/containerd/kubenet_template.conf" {{- end}} [metrics] address = "0.0.0.0:10257" {{- if TeleportEnabled }} [proxy_plugins] [proxy_plugins.teleportd] type = "snapshot" address = "/run/teleportd/snapshotter.sock" {{- end}} {{- if IsArtifactStreamingEnabled }} [proxy_plugins] [proxy_plugins.overlaybd] type = "snapshot" address = "/run/overlaybd-snapshotter/overlaybd.sock" {{- end}} {{- if IsKata }} [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata] runtime_type = "io.containerd.kata.v2" snapshotter = "tardev" disable_snapshot_annotations = false [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.katacli] runtime_type = "io.containerd.runc.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.katacli.options] NoPivotRoot = false NoNewKeyring = false ShimCgroup = "" IoUid = 0 IoGid = 0 BinaryName = "/usr/bin/kata-runtime" Root = "" SystemdCgroup = false [proxy_plugins] [proxy_plugins.tardev] type = "snapshot" address = "/run/containerd/tardev-snapshotter.sock" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata-cc] runtime_type = "io.containerd.kata-cc.v2" privileged_without_host_devices = true pod_annotations = ["io.katacontainers.*"] [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata-cc.options] ConfigPath = "/opt/confidential-containers/share/defaults/kata-containers/configuration-clh-snp.toml" {{- end}} ` containerdV2NoGPUConfigTemplate ContainerdConfigTemplate = `version = 2 oom_score = -999{{if HasDataDir }} root = "{{GetDataDir}}"{{- end}} [plugins."io.containerd.cri.v1.images"] {{- if TeleportEnabled }} snapshotter = "teleportd" disable_snapshot_annotations = false {{- end}} {{- if IsArtifactStreamingEnabled }} snapshotter = "overlaybd" disable_snapshot_annotations = false {{- end}} [plugins."io.containerd.cri.v1.images".pinned_images] sandbox = "{{GetPodInfraContainerSpec}}" {{- if IsKubernetesVersionGe "1.22.0"}} [plugins."io.containerd.cri.v1.images".registry] config_path = "/etc/containerd/certs.d" {{- end}} [plugins."io.containerd.cri.v1.images".registry.headers] X-Meta-Source-Client = ["azure/aks"] [plugins."io.containerd.cri.v1.runtime".containerd] default_runtime_name = "runc" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options] BinaryName = "/usr/bin/runc" SystemdCgroup = true [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.untrusted] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.untrusted.options] BinaryName = "/usr/bin/runc" {{- if IsKrustlet }} [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin] runtime_type = "io.containerd.spin.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-3-0] runtime_type = "io.containerd.spin-v0-3-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight-v0-3-0] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-5-1] runtime_type = "io.containerd.spin-v0-5-1.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight-v0-5-1] runtime_type = "io.containerd.slight-v0-5-1.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-8-0] runtime_type = "io.containerd.spin-v0-8-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.slight-v0-8-0] runtime_type = "io.containerd.slight-v0-8-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.wws-v0-8-0] runtime_type = "io.containerd.wws-v0-8-0.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin-v0-15-1] runtime_type = "io.containerd.spin.v2" {{- end}} {{- if and (IsKubenet) (not HasCalicoNetworkPolicy) }} [plugins."io.containerd.cri.v1.runtime".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" conf_template = "/etc/containerd/kubenet_template.conf" {{- end}} [metrics] address = "0.0.0.0:10257" {{- if TeleportEnabled }} [proxy_plugins] [proxy_plugins.teleportd] type = "snapshot" address = "/run/teleportd/snapshotter.sock" {{- end}} {{- if IsArtifactStreamingEnabled }} [proxy_plugins] [proxy_plugins.overlaybd] type = "snapshot" address = "/run/overlaybd-snapshotter/overlaybd.sock" {{- end}} {{- if IsKata }} [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.kata] runtime_type = "io.containerd.kata.v2" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.katacli] runtime_type = "io.containerd.runc.v1" [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.katacli.options] NoPivotRoot = false NoNewKeyring = false ShimCgroup = "" IoUid = 0 IoGid = 0 BinaryName = "/usr/bin/kata-runtime" Root = "" SystemdCgroup = false [proxy_plugins] [proxy_plugins.tardev] type = "snapshot" address = "/run/containerd/tardev-snapshotter.sock" {{- end}} ` containerdV1NoGPUConfigTemplate ContainerdConfigTemplate = `version = 2 oom_score = -999{{if HasDataDir }} root = "{{GetDataDir}}"{{- end}} [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "{{GetPodInfraContainerSpec}}" [plugins."io.containerd.grpc.v1.cri".containerd] {{- if TeleportEnabled }} snapshotter = "teleportd" disable_snapshot_annotations = false {{- else}} {{- if IsKata }} disable_snapshot_annotations = false {{- end}} {{- end}} {{- if IsArtifactStreamingEnabled }} snapshotter = "overlaybd" disable_snapshot_annotations = false {{- end}} default_runtime_name = "runc" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] BinaryName = "/usr/bin/runc" {{- if IsCgroupV2 }} SystemdCgroup = true {{- end}} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.untrusted.options] BinaryName = "/usr/bin/runc" {{- if IsKrustlet }} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin] runtime_type = "io.containerd.spin.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-3-0] runtime_type = "io.containerd.spin-v0-3-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight-v0-3-0] runtime_type = "io.containerd.slight-v0-3-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-5-1] runtime_type = "io.containerd.spin-v0-5-1.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight-v0-5-1] runtime_type = "io.containerd.slight-v0-5-1.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-8-0] runtime_type = "io.containerd.spin-v0-8-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.slight-v0-8-0] runtime_type = "io.containerd.slight-v0-8-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wws-v0-8-0] runtime_type = "io.containerd.wws-v0-8-0.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin-v0-15-1] runtime_type = "io.containerd.spin.v2" {{- end}} {{- if and (IsKubenet) (not HasCalicoNetworkPolicy) }} [plugins."io.containerd.grpc.v1.cri".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" conf_template = "/etc/containerd/kubenet_template.conf" {{- end}} {{- if IsKubernetesVersionGe "1.22.0"}} [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" {{- end}} [plugins."io.containerd.grpc.v1.cri".registry.headers] X-Meta-Source-Client = ["azure/aks"] [metrics] address = "0.0.0.0:10257" {{- if TeleportEnabled }} [proxy_plugins] [proxy_plugins.teleportd] type = "snapshot" address = "/run/teleportd/snapshotter.sock" {{- end}} {{- if IsArtifactStreamingEnabled }} [proxy_plugins] [proxy_plugins.overlaybd] type = "snapshot" address = "/run/overlaybd-snapshotter/overlaybd.sock" {{- end}} {{- if IsKata }} [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata] runtime_type = "io.containerd.kata.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.katacli] runtime_type = "io.containerd.runc.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.katacli.options] NoPivotRoot = false NoNewKeyring = false ShimCgroup = "" IoUid = 0 IoGid = 0 BinaryName = "/usr/bin/kata-runtime" Root = "" CriuPath = "" SystemdCgroup = false [proxy_plugins] [proxy_plugins.tardev] type = "snapshot" address = "/run/containerd/tardev-snapshotter.sock" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-cc] snapshotter = "tardev" runtime_type = "io.containerd.kata-cc.v2" privileged_without_host_devices = true pod_annotations = ["io.katacontainers.*"] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-cc.options] ConfigPath = "/opt/confidential-containers/share/defaults/kata-containers/configuration-clh-snp.toml" {{- end}} ` ) func containerdConfigFromTemplate( config *datamodel.NodeBootstrappingConfiguration, profile *datamodel.AgentPoolProfile, tmpl ContainerdConfigTemplate, ) (string, error) { parameters := getParameters(config) variables := getCustomDataVariables(config) bakerFuncMap := getBakerFuncMap(config, parameters, variables) containerdConfigTemplate := template.Must(template.New("kubenet").Funcs(bakerFuncMap).Parse(string(tmpl))) var b bytes.Buffer if err := containerdConfigTemplate.Execute(&b, profile); err != nil { return "", fmt.Errorf("failed to execute sysctl template: %w", err) } return base64.StdEncoding.EncodeToString(b.Bytes()), nil } // ----------------------- Start of changes related to localdns ------------------------------------------. // Parse and generate localdns Corefile from template and LocalDNSProfile. func GenerateLocalDNSCoreFile( config *datamodel.NodeBootstrappingConfiguration, profile *datamodel.AgentPoolProfile, tmpl string, ) (string, error) { parameters := getParameters(config) variables := getCustomDataVariables(config) bakerFuncMap := getBakerFuncMap(config, parameters, variables) if profile.LocalDNSProfile == nil { return "", fmt.Errorf("localdns profile is nil") } if !profile.ShouldEnableLocalDNS() { return "", fmt.Errorf("EnableLocalDNS is set to false, corefile will not be generated") } funcMapForHasSuffix := template.FuncMap{ "hasSuffix": strings.HasSuffix, } localDNSCoreFileData := profile.GetLocalDNSCoreFileData() localDNSCorefileTemplate := template.Must(template.New("localdnscorefile").Funcs(bakerFuncMap).Funcs(funcMapForHasSuffix).Parse(tmpl)) // Generate the Corefile content. var corefileBuffer bytes.Buffer if err := localDNSCorefileTemplate.Execute(&corefileBuffer, localDNSCoreFileData); err != nil { return "", fmt.Errorf("failed to execute localdns corefile template: %w", err) } // Return gzipped base64 encoded Corefile. Used in nodecustomdata. return getBase64EncodedGzippedCustomScriptFromStr(corefileBuffer.String()), nil } // Template to create corefile that will be used by localdns service. const localDNSCoreFileTemplateString = ` # *********************************************************************************** # WARNING: Changes to this file will be overwritten and not persisted. # *********************************************************************************** # whoami (used for health check of DNS) health-check.localdns.local:53 { bind {{$.NodeListenerIP}} {{$.ClusterListenerIP}} whoami } # VnetDNS overrides apply to DNS traffic from pods with dnsPolicy:default or kubelet (referred to as VnetDNS traffic). {{- range $domain, $override := $.VnetDNSOverrides -}} {{- $isRootDomain := eq $domain "." -}} {{- $fwdToClusterCoreDNS := or (hasSuffix $domain "cluster.local") (eq $override.ForwardDestination "ClusterCoreDNS")}} {{- $forwardPolicy := "sequential" -}} {{- if eq $override.ForwardPolicy "RoundRobin" -}} {{- $forwardPolicy = "round_robin" -}} {{- else if eq $override.ForwardPolicy "Random" -}} {{- $forwardPolicy = "random" -}} {{- end }} {{$domain}}:53 { {{- if eq $override.QueryLogging "Error" }} errors {{- else if eq $override.QueryLogging "Log" }} log {{- end }} bind {{$.NodeListenerIP}} {{- if $isRootDomain}} forward . {{$.AzureDNSIP}} { {{- else}} {{- if $fwdToClusterCoreDNS}} forward . {{$.CoreDNSServiceIP}} { {{- else}} forward . {{$.AzureDNSIP}} { {{- end}} {{- end}} {{- if eq $override.Protocol "ForceTCP"}} force_tcp {{- end}} policy {{$forwardPolicy}} max_concurrent {{$override.MaxConcurrent}} } ready {{$.NodeListenerIP}}:8181 cache {{$override.CacheDurationInSeconds}}s { success 9984 denial 9984 {{- if ne $override.ServeStale "Disable"}} {{- if eq $override.ServeStale "Verify"}} serve_stale {{$override.ServeStaleDurationInSeconds}}s verify {{- else if eq $override.ServeStale "Immediate"}} serve_stale {{$override.ServeStaleDurationInSeconds}}s immediate {{- end }} {{- end }} servfail 0 } loop nsid localdns prometheus :9253 {{- if $isRootDomain}} template ANY ANY internal.cloudapp.net { match "^(?:[^.]+\.){4,}internal\.cloudapp\.net\.$" rcode NXDOMAIN fallthrough } template ANY ANY reddog.microsoft.com { rcode NXDOMAIN } {{- end}} } {{- end}} # KubeDNS overrides apply to DNS traffic from pods with dnsPolicy:ClusterFirst (referred to as KubeDNS traffic). {{- range $domain, $override := $.KubeDNSOverrides}} {{- $isRootDomain := eq $domain "." -}} {{- $fwdToClusterCoreDNS := or (hasSuffix $domain "cluster.local") (eq $override.ForwardDestination "ClusterCoreDNS")}} {{- $forwardPolicy := "" }} {{- $forwardPolicy := "sequential" -}} {{- if eq $override.ForwardPolicy "RoundRobin" -}} {{- $forwardPolicy = "round_robin" -}} {{- else if eq $override.ForwardPolicy "Random" -}} {{- $forwardPolicy = "random" -}} {{- end }} {{$domain}}:53 { {{- if eq $override.QueryLogging "Error" }} errors {{- else if eq $override.QueryLogging "Log" }} log {{- end }} bind {{$.ClusterListenerIP}} {{- if $fwdToClusterCoreDNS}} forward . {{$.CoreDNSServiceIP}} { {{- else}} forward . {{$.AzureDNSIP}} { {{- end}} {{- if eq $override.Protocol "ForceTCP"}} force_tcp {{- end}} policy {{$forwardPolicy}} max_concurrent {{$override.MaxConcurrent}} } ready {{$.ClusterListenerIP}}:8181 cache {{$override.CacheDurationInSeconds}}s { success 9984 denial 9984 {{- if ne $override.ServeStale "Disable"}} {{- if eq $override.ServeStale "Verify"}} serve_stale {{$override.ServeStaleDurationInSeconds}}s verify {{- else if eq $override.ServeStale "Immediate"}} serve_stale {{$override.ServeStaleDurationInSeconds}}s immediate {{- end }} {{- end }} servfail 0 } loop nsid localdns-pod prometheus :9253 {{- if $isRootDomain}} template ANY ANY internal.cloudapp.net { match "^(?:[^.]+\.){4,}internal\.cloudapp\.net\.$" rcode NXDOMAIN fallthrough } template ANY ANY reddog.microsoft.com { rcode NXDOMAIN } {{- end}} } {{- end}} ` // ----------------------- End of changes related to localdns ------------------------------------------.