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()
}