diff --git a/docs/releases/1.17-NOTES.md b/docs/releases/1.17-NOTES.md index acd1b151726c5..f3657328758c5 100644 --- a/docs/releases/1.17-NOTES.md +++ b/docs/releases/1.17-NOTES.md @@ -21,6 +21,9 @@ the notes prior to the release). * Since 1.16, a controller is now used to apply labels to nodes. If you are not using AWS, GCE or OpenStack your (non-master) nodes may not have labels applied correctly. + +* As of Kubernetes 1.17 the Canal CNI and the "vxlan" backend of the Flannel CNI + are no longer supported due to [a bug in Flannel](https://github.com/coreos/flannel/issues/1243). # Required Actions diff --git a/pkg/apis/kops/validation/BUILD.bazel b/pkg/apis/kops/validation/BUILD.bazel index fd4795343a8a5..084d03b9b15ea 100644 --- a/pkg/apis/kops/validation/BUILD.bazel +++ b/pkg/apis/kops/validation/BUILD.bazel @@ -45,6 +45,7 @@ go_test( deps = [ "//pkg/apis/kops:go_default_library", "//upup/pkg/fi:go_default_library", + "//vendor/github.com/blang/semver:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", diff --git a/pkg/apis/kops/validation/validation.go b/pkg/apis/kops/validation/validation.go index 77ad44b9b2894..30daee146637c 100644 --- a/pkg/apis/kops/validation/validation.go +++ b/pkg/apis/kops/validation/validation.go @@ -35,7 +35,7 @@ import ( func newValidateCluster(cluster *kops.Cluster) field.ErrorList { allErrs := validation.ValidateObjectMeta(&cluster.ObjectMeta, false, validation.NameIsDNSSubdomain, field.NewPath("metadata")) - allErrs = append(allErrs, validateClusterSpec(&cluster.Spec, field.NewPath("spec"))...) + allErrs = append(allErrs, validateClusterSpec(&cluster.Spec, cluster, field.NewPath("spec"))...) // Additional cloud-specific validation rules switch kops.CloudProviderID(cluster.Spec.CloudProvider) { @@ -48,7 +48,7 @@ func newValidateCluster(cluster *kops.Cluster) field.ErrorList { return allErrs } -func validateClusterSpec(spec *kops.ClusterSpec, fieldPath *field.Path) field.ErrorList { +func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, validateSubnets(spec.Subnets, fieldPath.Child("subnets"))...) @@ -89,7 +89,7 @@ func validateClusterSpec(spec *kops.ClusterSpec, fieldPath *field.Path) field.Er } if spec.Networking != nil { - allErrs = append(allErrs, validateNetworking(spec, spec.Networking, fieldPath.Child("networking"))...) + allErrs = append(allErrs, validateNetworking(spec.Networking, c, fieldPath.Child("networking"))...) if spec.Networking.Calico != nil { allErrs = append(allErrs, validateNetworkingCalico(spec.Networking.Calico, spec.EtcdClusters[0], fieldPath.Child("networking", "calico"))...) } @@ -285,7 +285,7 @@ func validateKubeAPIServer(v *kops.KubeAPIServerConfig, fldPath *field.Path) fie return allErrs } -func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *field.Path) field.ErrorList { +func validateNetworking(v *kops.NetworkingSpec, c *kops.Cluster, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} optionTaken := false @@ -331,7 +331,7 @@ func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *fi } optionTaken = true - allErrs = append(allErrs, validateNetworkingFlannel(v.Flannel, fldPath.Child("flannel"))...) + allErrs = append(allErrs, validateNetworkingFlannel(v.Flannel, c, fldPath.Child("flannel"))...) } if v.Calico != nil { @@ -347,7 +347,7 @@ func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *fi } optionTaken = true - allErrs = append(allErrs, validateNetworkingCanal(v.Canal, fldPath.Child("canal"))...) + allErrs = append(allErrs, validateNetworkingCanal(v.Canal, c, fldPath.Child("canal"))...) } if v.Kuberouter != nil { @@ -370,7 +370,7 @@ func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *fi } optionTaken = true - if c.CloudProvider != "aws" { + if c.Spec.CloudProvider != "aws" { allErrs = append(allErrs, field.Forbidden(fldPath.Child("amazonvpc"), "amazon-vpc-routed-eni networking is supported only in AWS")) } } @@ -381,7 +381,7 @@ func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *fi } optionTaken = true - allErrs = append(allErrs, validateNetworkingCilium(c, v.Cilium, fldPath.Child("cilium"))...) + allErrs = append(allErrs, validateNetworkingCilium(v.Cilium, c, fldPath.Child("cilium"))...) } if v.LyftVPC != nil { @@ -390,7 +390,7 @@ func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *fi } optionTaken = true - if c.CloudProvider != "aws" { + if c.Spec.CloudProvider != "aws" { allErrs = append(allErrs, field.Forbidden(fldPath.Child("lyftvpc"), "amazon-vpc-routed-eni networking is supported only in AWS")) } } @@ -401,27 +401,36 @@ func validateNetworking(c *kops.ClusterSpec, v *kops.NetworkingSpec, fldPath *fi } optionTaken = true - allErrs = append(allErrs, validateNetworkingGCE(c, v.GCE, fldPath.Child("gce"))...) + allErrs = append(allErrs, validateNetworkingGCE(v.GCE, c, fldPath.Child("gce"))...) } return allErrs } -func validateNetworkingFlannel(v *kops.FlannelNetworkingSpec, fldPath *field.Path) field.ErrorList { +func validateNetworkingFlannel(v *kops.FlannelNetworkingSpec, c *kops.Cluster, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} + supported := []string{"udp"} + if !c.IsKubernetesGTE("1.17") { + supported = append(supported, "vxlan") + } + if v.Backend == "" { allErrs = append(allErrs, field.Required(fldPath.Child("backend"), "Flannel backend must be specified")) } else { - allErrs = append(allErrs, IsValidValue(fldPath.Child("backend"), &v.Backend, []string{"udp", "vxlan"})...) + allErrs = append(allErrs, IsValidValue(fldPath.Child("backend"), &v.Backend, supported)...) } return allErrs } -func validateNetworkingCanal(v *kops.CanalNetworkingSpec, fldPath *field.Path) field.ErrorList { +func validateNetworkingCanal(v *kops.CanalNetworkingSpec, c *kops.Cluster, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} + if c.IsKubernetesGTE("1.17") { + return append(allErrs, field.Forbidden(fldPath.Child("canal"), "Canal CNI is not supported as of Kubernetes 1.17")) + } + if v.DefaultEndpointToHostAction != "" { valid := []string{"ACCEPT", "DROP", "RETURN"} allErrs = append(allErrs, IsValidValue(fldPath.Child("defaultEndpointToHostAction"), &v.DefaultEndpointToHostAction, valid)...) @@ -445,10 +454,10 @@ func validateNetworkingCanal(v *kops.CanalNetworkingSpec, fldPath *field.Path) f return allErrs } -func validateNetworkingCilium(c *kops.ClusterSpec, v *kops.CiliumNetworkingSpec, fldPath *field.Path) field.ErrorList { +func validateNetworkingCilium(v *kops.CiliumNetworkingSpec, c *kops.Cluster, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - if v.EnableNodePort && c.KubeProxy != nil && (c.KubeProxy.Enabled == nil || *c.KubeProxy.Enabled) { + if v.EnableNodePort && c.Spec.KubeProxy != nil && (c.Spec.KubeProxy.Enabled == nil || *c.Spec.KubeProxy.Enabled) { allErrs = append(allErrs, field.Forbidden(fldPath.Root().Child("spec", "kubeProxy", "enabled"), "When Cilium NodePort is enabled, kubeProxy must be disabled")) } @@ -473,7 +482,7 @@ func validateNetworkingCilium(c *kops.ClusterSpec, v *kops.CiliumNetworkingSpec, allErrs = append(allErrs, IsValidValue(fldPath.Child("ipam"), &v.Ipam, []string{"crd", "eni"})...) if v.Ipam == kops.CiliumIpamEni { - if c.CloudProvider != string(kops.CloudProviderAWS) { + if c.Spec.CloudProvider != string(kops.CloudProviderAWS) { allErrs = append(allErrs, field.Forbidden(fldPath.Child("ipam"), "Cilum ENI IPAM is supported only in AWS")) } if !v.DisableMasquerade { @@ -485,10 +494,10 @@ func validateNetworkingCilium(c *kops.ClusterSpec, v *kops.CiliumNetworkingSpec, return allErrs } -func validateNetworkingGCE(c *kops.ClusterSpec, v *kops.GCENetworkingSpec, fldPath *field.Path) field.ErrorList { +func validateNetworkingGCE(v *kops.GCENetworkingSpec, c *kops.Cluster, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - if c.CloudProvider != "gce" { + if c.Spec.CloudProvider != "gce" { allErrs = append(allErrs, field.Forbidden(fldPath, "gce networking is supported only when on GCP")) } diff --git a/pkg/apis/kops/validation/validation_test.go b/pkg/apis/kops/validation/validation_test.go index 52ca54d6e2214..b0a1feaef4daa 100644 --- a/pkg/apis/kops/validation/validation_test.go +++ b/pkg/apis/kops/validation/validation_test.go @@ -284,9 +284,10 @@ func Test_Validate_Networking_Flannel(t *testing.T) { networking.Flannel = &g.Input cluster := &kops.Cluster{} + cluster.Spec.KubernetesVersion = "1.16.0" cluster.Spec.Networking = networking - errs := validateNetworking(&cluster.Spec, networking, field.NewPath("networking")) + errs := validateNetworking(networking, cluster, field.NewPath("networking")) testErrors(t, g.Input, errs, g.ExpectedErrors) } } @@ -330,13 +331,16 @@ func Test_Validate_AdditionalPolicies(t *testing.T) { }, } for _, g := range grid { - clusterSpec := &kops.ClusterSpec{ - AdditionalPolicies: &g.Input, - Subnets: []kops.ClusterSubnetSpec{ - {Name: "subnet1"}, + cluster := &kops.Cluster{ + Spec: kops.ClusterSpec{ + KubernetesVersion: "1.16.0", + AdditionalPolicies: &g.Input, + Subnets: []kops.ClusterSubnetSpec{ + {Name: "subnet1"}, + }, }, } - errs := validateClusterSpec(clusterSpec, field.NewPath("spec")) + errs := validateClusterSpec(&cluster.Spec, cluster, field.NewPath("spec")) testErrors(t, g.Input, errs, g.ExpectedErrors) } }