func()

in pkg/api/vlabs/validate.go [1270:1526]


func (k *KubernetesConfig) Validate(k8sVersion string, hasWindows, ipv6DualStackEnabled, isIPv6, isUpdate bool) error {
	// number of minimum retries allowed for kubelet to post node status
	const minKubeletRetries = 4

	// enableIPv6DualStack and enableIPv6Only are mutually exclusive feature flags
	if ipv6DualStackEnabled && isIPv6 {
		return errors.Errorf("featureFlags.EnableIPv6DualStack and featureFlags.EnableIPv6Only can't be enabled at the same time")
	}

	sv, err := semver.Make(k8sVersion)
	if err != nil {
		return errors.Errorf("could not validate version %s", k8sVersion)
	}

	if ipv6DualStackEnabled {
		minVersion, err := semver.Make("1.16.0")
		if err != nil {
			return errors.New("could not validate version")
		}
		if sv.LT(minVersion) {
			return errors.Errorf("IPv6 dual stack not available in kubernetes version %s", k8sVersion)
		}
		// ipv6 dual stack feature is currently only supported with kubenet
		if k.NetworkPlugin != "kubenet" && k.NetworkPlugin != "azure" {
			return errors.Errorf("the OrchestratorProfile.KubernetesConfig.NetworkPlugin '%s' is invalid, IPv6 dual stack supported only with 'kubenet' and 'azure'", k.NetworkPlugin)
		}

		if k.NetworkPlugin == "azure" && k.NetworkPolicy != "" {
			return errors.Errorf("Network policy %s is not supported for azure cni dualstack", k.NetworkPolicy)
		}
	}

	if isIPv6 {
		minVersion, err := semver.Make("1.18.0")
		if err != nil {
			return errors.New("could not validate version")
		}
		if sv.LT(minVersion) {
			return errors.Errorf("IPv6 single stack not available in kubernetes version %s", k8sVersion)
		}
		// single stack IPv6 feature is currently only supported with kubenet
		if k.NetworkPlugin != "kubenet" {
			return errors.Errorf("the OrchestratorProfile.KubernetesConfig.NetworkPlugin '%s' is invalid, IPv6 single stack supported only with kubenet", k.NetworkPlugin)
		}
	}

	if k.ClusterSubnet != "" {
		clusterSubnets := strings.Split(k.ClusterSubnet, ",")
		if !ipv6DualStackEnabled && len(clusterSubnets) > 1 {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.ClusterSubnet '%s' is an invalid subnet", k.ClusterSubnet)
		}
		if ipv6DualStackEnabled && len(clusterSubnets) > 2 {
			return errors.Errorf("the OrchestratorProfile.KubernetesConfig.ClusterSubnet '%s' is an invalid subnet, not more than 2 subnets for ipv6 dual stack", k.ClusterSubnet)
		}

		for _, clusterSubnet := range clusterSubnets {
			_, subnet, err := net.ParseCIDR(clusterSubnet)
			if err != nil {
				return errors.Errorf("OrchestratorProfile.KubernetesConfig.ClusterSubnet '%s' is an invalid subnet", clusterSubnet)
			}

			if k.NetworkPlugin == "azure" {
				ones, bits := subnet.Mask.Size()
				if bits-ones <= 8 {
					return errors.Errorf("OrchestratorProfile.KubernetesConfig.ClusterSubnet '%s' must reserve at least 9 bits for nodes", clusterSubnet)
				}
			}
		}
	}

	if k.DockerBridgeSubnet != "" {
		_, _, err := net.ParseCIDR(k.DockerBridgeSubnet)
		if err != nil {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.DockerBridgeSubnet '%s' is an invalid subnet", k.DockerBridgeSubnet)
		}
	}

	if k.MaxPods != 0 {
		if k.MaxPods < KubernetesMinMaxPods {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.MaxPods '%v' must be at least %v", k.MaxPods, KubernetesMinMaxPods)
		}
	}

	if k.KubeletConfig != nil {
		if _, ok := k.KubeletConfig["--node-status-update-frequency"]; ok {
			val := k.KubeletConfig["--node-status-update-frequency"]
			_, err := time.ParseDuration(val)
			if err != nil {
				return errors.Errorf("--node-status-update-frequency '%s' is not a valid duration", val)
			}
		}
	}

	if _, ok := k.ControllerManagerConfig["--node-monitor-grace-period"]; ok {
		_, err := time.ParseDuration(k.ControllerManagerConfig["--node-monitor-grace-period"])
		if err != nil {
			return errors.Errorf("--node-monitor-grace-period '%s' is not a valid duration", k.ControllerManagerConfig["--node-monitor-grace-period"])
		}
	}

	if k.KubeletConfig != nil {
		if _, ok := k.KubeletConfig["--node-status-update-frequency"]; ok {
			if _, ok := k.ControllerManagerConfig["--node-monitor-grace-period"]; ok {
				nodeStatusUpdateFrequency, _ := time.ParseDuration(k.KubeletConfig["--node-status-update-frequency"])
				ctrlMgrNodeMonitorGracePeriod, _ := time.ParseDuration(k.ControllerManagerConfig["--node-monitor-grace-period"])
				kubeletRetries := ctrlMgrNodeMonitorGracePeriod.Seconds() / nodeStatusUpdateFrequency.Seconds()
				if kubeletRetries < minKubeletRetries {
					return errors.Errorf("aks-engine-azurestack requires that --node-monitor-grace-period(%f)s be larger than nodeStatusUpdateFrequency(%f)s by at least a factor of %d; ", ctrlMgrNodeMonitorGracePeriod.Seconds(), nodeStatusUpdateFrequency.Seconds(), minKubeletRetries)
				}
			}
		}
		// Re-enable this unit test if --non-masquerade-cidr is re-introduced
		/*if _, ok := k.KubeletConfig["--non-masquerade-cidr"]; ok {
			if _, _, err := net.ParseCIDR(k.KubeletConfig["--non-masquerade-cidr"]); err != nil {
				return errors.Errorf("--non-masquerade-cidr kubelet config '%s' is an invalid CIDR string", k.KubeletConfig["--non-masquerade-cidr"])
			}
		}*/
	}

	if _, ok := k.ControllerManagerConfig["--pod-eviction-timeout"]; ok {
		_, err := time.ParseDuration(k.ControllerManagerConfig["--pod-eviction-timeout"])
		if err != nil {
			return errors.Errorf("--pod-eviction-timeout '%s' is not a valid duration", k.ControllerManagerConfig["--pod-eviction-timeout"])
		}
	}

	if _, ok := k.ControllerManagerConfig["--route-reconciliation-period"]; ok {
		_, err := time.ParseDuration(k.ControllerManagerConfig["--route-reconciliation-period"])
		if err != nil {
			return errors.Errorf("--route-reconciliation-period '%s' is not a valid duration", k.ControllerManagerConfig["--route-reconciliation-period"])
		}
	}

	if k.DNSServiceIP != "" || k.ServiceCidr != "" {
		if k.DNSServiceIP == "" {
			return errors.New("OrchestratorProfile.KubernetesConfig.DNSServiceIP must be specified when ServiceCidr is")
		}
		if k.ServiceCidr == "" {
			return errors.New("OrchestratorProfile.KubernetesConfig.ServiceCidr must be specified when DNSServiceIP is")
		}

		dnsIP := net.ParseIP(k.DNSServiceIP)
		if dnsIP == nil {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.DNSServiceIP '%s' is an invalid IP address", k.DNSServiceIP)
		}

		primaryServiceCIDR := k.ServiceCidr
		if ipv6DualStackEnabled {
			// split the service cidr to see if there are multiple cidrs
			serviceCidrs := strings.Split(k.ServiceCidr, ",")
			if len(serviceCidrs) > 2 {
				return errors.Errorf("OrchestratorProfile.KubernetesConfig.ServiceCidr '%s' is an invalid CIDR subnet. More than 2 CIDRs not allowed for dualstack", k.ServiceCidr)
			}
			if len(serviceCidrs) == 2 {
				firstServiceCIDR, secondServiceCIDR := serviceCidrs[0], serviceCidrs[1]
				_, _, err := net.ParseCIDR(secondServiceCIDR)
				if err != nil {
					return errors.Errorf("OrchestratorProfile.KubernetesConfig.ServiceCidr '%s' is an invalid CIDR subnet", secondServiceCIDR)
				}
				// use the primary service cidr for further validation
				primaryServiceCIDR = firstServiceCIDR
			}
			// if # of service cidrs is 1, then continues with the default validation
		}

		_, serviceCidr, err := net.ParseCIDR(primaryServiceCIDR)
		if err != nil {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.ServiceCidr '%s' is an invalid CIDR subnet", primaryServiceCIDR)
		}

		// Finally validate that the DNS ip is within the subnet
		if !serviceCidr.Contains(dnsIP) {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.DNSServiceIP '%s' is not within the ServiceCidr '%s'", k.DNSServiceIP, primaryServiceCIDR)
		}

		// and that the DNS IP is _not_ the subnet broadcast address
		broadcast := common.IP4BroadcastAddress(serviceCidr)
		if dnsIP.Equal(broadcast) {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.DNSServiceIP '%s' cannot be the broadcast address of ServiceCidr '%s'", k.DNSServiceIP, primaryServiceCIDR)
		}

		// and that the DNS IP is _not_ the first IP in the service subnet
		firstServiceIP := common.CidrFirstIP(serviceCidr.IP)
		if firstServiceIP.Equal(dnsIP) {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.DNSServiceIP '%s' cannot be the first IP of ServiceCidr '%s'", k.DNSServiceIP, primaryServiceCIDR)
		}
	}

	if k.ProxyMode != "" && k.ProxyMode != KubeProxyModeIPTables && k.ProxyMode != KubeProxyModeIPVS {
		return errors.Errorf("Invalid KubeProxyMode %v. Allowed modes are %v and %v", k.ProxyMode, KubeProxyModeIPTables, KubeProxyModeIPVS)
	}

	// dualstack IPVS mode supported from 1.16+
	// dualstack IPtables mode supported from 1.18+
	if ipv6DualStackEnabled && k.ProxyMode == KubeProxyModeIPTables {
		minVersion, err := semver.Make("1.18.0")
		if err != nil {
			return errors.New("could not validate version")
		}
		if sv.LT(minVersion) {
			return errors.Errorf("KubeProxyMode %v in dualstack not supported with %s version", k.ProxyMode, k8sVersion)
		}
	}

	// Validate that we have a valid etcd version
	if e := validateEtcdVersion(k.EtcdVersion); e != nil {
		return e
	}

	// Validate containerd scenarios
	if k.ContainerRuntime == Docker || k.ContainerRuntime == "" {
		if k.MobyVersion != "" && k.ContainerdVersion != "" && versions.LessThan(k.MobyVersion, "19.03") {
			return errors.Errorf("containerdVersion is only valid in a non-docker context, use %s containerRuntime value instead if you wish to provide a containerdVersion", Containerd)
		}
	}
	if e := validateContainerdVersion(k.ContainerdVersion); e != nil {
		return e
	}

	if to.Bool(k.UseCloudControllerManager) || k.CustomCcmImage != "" {
		sv, err := semver.Make(k8sVersion)
		if err != nil {
			return errors.Errorf("could not validate version %s", k8sVersion)
		}
		minVersion, err := semver.Make("1.8.0")
		if err != nil {
			return errors.New("could not validate version")
		}
		if sv.LT(minVersion) {
			return errors.Errorf("OrchestratorProfile.KubernetesConfig.UseCloudControllerManager and OrchestratorProfile.KubernetesConfig.CustomCcmImage not available in kubernetes version %s", k8sVersion)
		}
	}

	if e := k.validateNetworkPlugin(hasWindows, isUpdate); e != nil {
		return e
	}
	if e := k.validateNetworkPolicy(k8sVersion, hasWindows); e != nil {
		return e
	}
	if e := k.validateNetworkPluginPlusPolicy(); e != nil {
		return e
	}
	if e := k.validateNetworkMode(); e != nil {
		return e
	}
	if e := k.validateKubernetesImageBaseType(); e != nil {
		return e
	}

	if to.Bool(k.EnableMultipleStandardLoadBalancers) && !common.IsKubernetesVersionGe(k8sVersion, "1.20.0-beta.1") {
		return errors.Errorf("OrchestratorProfile.KubernetesConfig.EnableMultipleStandardLoadBalancers is available since kubernetes version v1.20.0-beta.1, current version is %s", k8sVersion)
	}
	if k.Tags != "" && !common.IsKubernetesVersionGe(k8sVersion, "1.20.0-beta.1") {
		return errors.Errorf("OrchestratorProfile.KubernetesConfig.Tags is available since kubernetes version v1.20.0-beta.1, current version is %s", k8sVersion)
	}
	return k.validateContainerRuntimeConfig()
}