in pkg/api/vlabs/validate.go [675:849]
func (a *Properties) validateAddons(isUpdate bool) error {
if a.OrchestratorProfile.KubernetesConfig != nil && a.OrchestratorProfile.KubernetesConfig.Addons != nil {
var isAvailabilitySets bool
var kubeDNSEnabled bool
var corednsEnabled bool
for _, agentPool := range a.AgentPoolProfiles {
if agentPool.IsAvailabilitySets() {
isAvailabilitySets = true
}
}
for _, addon := range a.OrchestratorProfile.KubernetesConfig.Addons {
if addon.Data != "" {
if len(addon.Config) > 0 || len(addon.Containers) > 0 {
return errors.New("Config and containers should be empty when addon.Data is specified")
}
if _, err := base64.StdEncoding.DecodeString(addon.Data); err != nil {
return errors.Errorf("Addon %s's data should be base64 encoded", addon.Name)
}
}
if addon.Mode != "" {
if addon.Mode != AddonModeEnsureExists && addon.Mode != AddonModeReconcile {
return errors.Errorf("addon %s has a mode configuration '%s', must be either %s or %s", addon.Name, addon.Mode, AddonModeEnsureExists, AddonModeReconcile)
}
}
// Validation for addons if they are enabled
if to.Bool(addon.Enabled) {
switch addon.Name {
case "cluster-autoscaler":
if isAvailabilitySets {
return errors.Errorf("cluster-autoscaler addon can only be used with VirtualMachineScaleSets. Please specify \"availabilityProfile\": \"%s\"", VirtualMachineScaleSets)
}
for _, pool := range addon.Pools {
if pool.Name == "" {
return errors.Errorf("cluster-autoscaler addon pools configuration must have a 'name' property that correlates with a pool name in the agentPoolProfiles array")
}
if a.GetAgentPoolByName(pool.Name) == nil {
return errors.Errorf("cluster-autoscaler addon pool 'name' %s does not match any agentPoolProfiles nodepool name", pool.Name)
}
if pool.Config != nil {
var min, max int
var err error
if pool.Config["min-nodes"] != "" {
min, err = strconv.Atoi(pool.Config["min-nodes"])
if err != nil {
return errors.Errorf("cluster-autoscaler addon pool 'name' %s has invalid 'min-nodes' config, must be a string int, got %s", pool.Name, pool.Config["min-nodes"])
}
}
if pool.Config["max-nodes"] != "" {
max, err = strconv.Atoi(pool.Config["max-nodes"])
if err != nil {
return errors.Errorf("cluster-autoscaler addon pool 'name' %s has invalid 'max-nodes' config, must be a string int, got %s", pool.Name, pool.Config["max-nodes"])
}
}
if min > max {
return errors.Errorf("cluster-autoscaler addon pool 'name' %s has invalid config, 'max-nodes' %d must be greater than or equal to 'min-nodes' %d", pool.Name, max, min)
}
}
}
case "aad":
if !a.HasAADAdminGroupID() {
return errors.New("aad addon can't be enabled without a valid aadProfile w/ adminGroupID")
}
case "appgw-ingress":
if (a.ServicePrincipalProfile == nil || len(a.ServicePrincipalProfile.ObjectID) == 0) &&
!to.Bool(a.OrchestratorProfile.KubernetesConfig.UseManagedIdentity) {
return errors.New("appgw-ingress add-ons requires 'objectID' to be specified or UseManagedIdentity to be true")
}
if a.OrchestratorProfile.KubernetesConfig.NetworkPlugin != "azure" {
return errors.New("appgw-ingress add-ons can only be used with Network Plugin as 'azure'")
}
if len(addon.Config["appgw-subnet"]) == 0 {
return errors.New("appgw-ingress add-ons requires 'appgw-subnet' in the Config. It is used to provision the subnet for Application Gateway in the vnet")
}
case "cloud-node-manager":
if !to.Bool(a.OrchestratorProfile.KubernetesConfig.UseCloudControllerManager) {
return errors.Errorf("%s add-on requires useCloudControllerManager to be true", addon.Name)
}
if !a.ShouldEnableAzureCloudAddon(addon.Name) {
minVersion := "1.16.0"
if a.HasWindows() {
minVersion = "1.18.0"
}
return errors.Errorf("%s add-on can only be used Kubernetes %s or above", addon.Name, minVersion)
}
case common.CiliumAddonName:
if !common.IsKubernetesVersionGe(a.OrchestratorProfile.OrchestratorVersion, "1.16.0") {
if a.OrchestratorProfile.KubernetesConfig.NetworkPolicy != NetworkPolicyCilium {
return errors.Errorf("%s addon may only be enabled if the networkPolicy=%s", common.CiliumAddonName, NetworkPolicyCilium)
}
} else {
return errors.Errorf("%s addon is not supported on Kubernetes v1.16.0 or greater", common.CiliumAddonName)
}
case common.AntreaAddonName:
if a.OrchestratorProfile.KubernetesConfig.NetworkPolicy != NetworkPolicyAntrea {
return errors.Errorf("%s addon may only be enabled if the networkPolicy=%s", common.AntreaAddonName, NetworkPolicyAntrea)
}
case common.FlannelAddonName:
if isUpdate {
if a.OrchestratorProfile.KubernetesConfig.NetworkPolicy != "" {
return errors.Errorf("%s addon does not support NetworkPolicy, replace %s with \"\"", common.FlannelAddonName, a.OrchestratorProfile.KubernetesConfig.NetworkPolicy)
}
networkPlugin := a.OrchestratorProfile.KubernetesConfig.NetworkPlugin
if networkPlugin != "" {
if networkPlugin != NetworkPluginFlannel {
return errors.Errorf("%s addon is not supported with networkPlugin=%s, please use networkPlugin=%s", common.FlannelAddonName, networkPlugin, NetworkPluginFlannel)
}
}
if a.OrchestratorProfile.KubernetesConfig.ContainerRuntime != Containerd {
return errors.Errorf("%s addon is only supported with containerRuntime=%s", common.FlannelAddonName, Containerd)
}
} else {
return errors.Errorf("%s addon is deprecated for new clusters", common.FlannelAddonName)
}
case common.KubeDNSAddonName:
kubeDNSEnabled = true
case common.CoreDNSAddonName:
corednsEnabled = true
case common.SecretsStoreCSIDriverAddonName:
if !common.IsKubernetesVersionGe(a.OrchestratorProfile.OrchestratorVersion, "1.16.0") {
return errors.Errorf("%s add-on can only be used in 1.16+", addon.Name)
}
case common.PodSecurityPolicyAddonName:
if common.ShouldDisablePodSecurityPolicyAddon(a.OrchestratorProfile.OrchestratorVersion) {
log.Warn("The PodSecurityPolicy admission was removed in Kubernetes v1.25+. " +
"The pod security standards will be enforced by the built-in PodSecurity admission controller instead. " +
"See https://github.com/Azure/aks-engine-azurestack/blob/master/docs/topics/pod-security.md")
}
case common.AzureArcOnboardingAddonName:
if err := addon.validateArcAddonConfig(); err != nil {
return err
}
case common.ReschedulerAddonName:
if isUpdate {
log.Warnf("The rescheduler addon has been deprecated and disabled, it will be removed during this update")
}
return errors.Errorf("The rescheduler addon has been deprecated and disabled, please remove it from your cluster configuration before creating a new cluster")
case common.ContainerMonitoringAddonName:
if isUpdate {
log.Warnf("The container monitoring addon has been deprecated and disabled, it will be removed during this update")
}
return errors.Errorf("The container monitoring addon has been deprecated and disabled, please remove it from your cluster configuration before creating a new cluster")
case common.DashboardAddonName:
log.Warnf("The kube-dashboard addon is deprecated, we recommend you install the dashboard yourself, see https://github.com/kubernetes/dashboard")
case common.AzureCNINetworkMonitorAddonName:
if isUpdate {
log.Warnf("The Azure CNI networkmonitor addon has been deprecated, it will be marked as disabled")
}
}
} else {
// Validation for addons if they are disabled
switch addon.Name {
case "cloud-node-manager":
if a.ShouldEnableAzureCloudAddon(addon.Name) && !a.IsAzureStackCloud() {
minVersion := "1.16.0"
if a.HasWindows() {
minVersion = "1.18.0"
}
return errors.Errorf("%s add-on is required when useCloudControllerManager is true in Kubernetes %s or above", addon.Name, minVersion)
}
case common.AzureCloudProviderAddonName:
return errors.Errorf("%s add-on is required, it cannot be disabled", addon.Name)
}
}
}
if kubeDNSEnabled && corednsEnabled {
return errors.New("Both kube-dns and coredns addons are enabled, only one of these may be enabled on a cluster")
}
}
return nil
}