diff --git a/pkg/apis/kops/validation/validation.go b/pkg/apis/kops/validation/validation.go index c1b1613345..af2b5c6f0d 100644 --- a/pkg/apis/kops/validation/validation.go +++ b/pkg/apis/kops/validation/validation.go @@ -130,6 +130,10 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie allErrs = append(allErrs, validateKubeAPIServer(spec.KubeAPIServer, c, fieldPath.Child("kubeAPIServer"), strict)...) } + if spec.KubeControllerManager != nil { + allErrs = append(allErrs, validateKubeControllerManager(spec.KubeControllerManager, c, fieldPath.Child("kubeControllerManager"), strict)...) + } + if spec.KubeProxy != nil { allErrs = append(allErrs, validateKubeProxy(spec.KubeProxy, fieldPath.Child("kubeProxy"))...) } @@ -824,6 +828,20 @@ func validateKubeAPIServer(v *kops.KubeAPIServerConfig, c *kops.Cluster, fldPath return allErrs } +func validateKubeControllerManager(v *kops.KubeControllerManagerConfig, c *kops.Cluster, fldPath *field.Path, strict bool) field.ErrorList { + allErrs := field.ErrorList{} + + // We aren't aiming to do comprehensive validation, but we can add some best-effort validation where it helps guide users + // Users reported encountered this in #15909 + if v.ExperimentalClusterSigningDuration != nil { + if c.IsKubernetesGTE("1.25") { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("experimentalClusterSigningDuration"), "experimentalClusterSigningDuration has been replaced with clusterSigningDuration as of kubernetes 1.25")) + } + } + + return allErrs +} + func validateKubeProxy(k *kops.KubeProxyConfig, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} diff --git a/pkg/apis/kops/validation/validation_test.go b/pkg/apis/kops/validation/validation_test.go index 279c86f7f9..278742a57b 100644 --- a/pkg/apis/kops/validation/validation_test.go +++ b/pkg/apis/kops/validation/validation_test.go @@ -19,7 +19,9 @@ package validation import ( "net" "testing" + "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" @@ -339,6 +341,64 @@ func TestValidateKubeAPIServer(t *testing.T) { } } +func TestValidateKubeControllermanager(t *testing.T) { + grid := []struct { + Input kops.KubeControllerManagerConfig + Cluster *kops.Cluster + ExpectedErrors []string + ExpectedDetail string + }{ + { + Input: kops.KubeControllerManagerConfig{ + ExperimentalClusterSigningDuration: &metav1.Duration{Duration: time.Hour}, + }, + ExpectedErrors: []string{ + "Forbidden::kubeControllerManager.experimentalClusterSigningDuration", + }, + ExpectedDetail: "experimentalClusterSigningDuration has been replaced with clusterSigningDuration as of kubernetes 1.25", + }, + { + Input: kops.KubeControllerManagerConfig{ + ExperimentalClusterSigningDuration: &metav1.Duration{Duration: time.Hour}, + }, + Cluster: &kops.Cluster{ + Spec: kops.ClusterSpec{ + KubernetesVersion: "1.24.0", + }, + }, + ExpectedErrors: []string{}, + }, + } + for _, g := range grid { + if g.Cluster == nil { + g.Cluster = &kops.Cluster{ + Spec: kops.ClusterSpec{ + KubernetesVersion: "1.28.0", + }, + } + } + errs := validateKubeControllerManager(&g.Input, g.Cluster, field.NewPath("kubeControllerManager"), true) + + testErrors(t, g.Input, errs, g.ExpectedErrors) + + if g.ExpectedDetail != "" { + found := false + for _, err := range errs { + if err.Detail == g.ExpectedDetail { + found = true + } + } + if !found { + for _, err := range errs { + t.Logf("found detail: %q", err.Detail) + } + + t.Errorf("did not find expected error %q", g.ExpectedDetail) + } + } + } +} + func Test_Validate_Networking_Flannel(t *testing.T) { grid := []struct { Input kops.FlannelNetworkingSpec