sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/webhooks/kubeadmcontrolplanetemplate_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package webhooks
    18  
    19  import (
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	. "github.com/onsi/gomega"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	utilfeature "k8s.io/component-base/featuregate/testing"
    27  
    28  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    29  	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
    30  	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
    31  	"sigs.k8s.io/cluster-api/feature"
    32  	"sigs.k8s.io/cluster-api/internal/webhooks/util"
    33  )
    34  
    35  func TestKubeadmControlPlaneTemplateDefault(t *testing.T) {
    36  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
    37  
    38  	g := NewWithT(t)
    39  
    40  	kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
    41  		ObjectMeta: metav1.ObjectMeta{
    42  			Namespace: "foo",
    43  		},
    44  		Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
    45  			Template: controlplanev1.KubeadmControlPlaneTemplateResource{
    46  				Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
    47  					MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
    48  						NodeDrainTimeout: &metav1.Duration{Duration: 10 * time.Second},
    49  					},
    50  				},
    51  			},
    52  		},
    53  	}
    54  	updateDefaultingValidationKCPTemplate := kcpTemplate.DeepCopy()
    55  	updateDefaultingValidationKCPTemplate.Spec.Template.Spec.MachineTemplate.NodeDrainTimeout = &metav1.Duration{Duration: 20 * time.Second}
    56  	webhook := &KubeadmControlPlaneTemplate{}
    57  	t.Run("for KubeadmControlPlaneTemplate", util.CustomDefaultValidateTest(ctx, updateDefaultingValidationKCPTemplate, webhook))
    58  	g.Expect(webhook.Default(ctx, kcpTemplate)).To(Succeed())
    59  
    60  	g.Expect(kcpTemplate.Spec.Template.Spec.KubeadmConfigSpec.Format).To(Equal(bootstrapv1.CloudConfig))
    61  	g.Expect(kcpTemplate.Spec.Template.Spec.RolloutStrategy.Type).To(Equal(controlplanev1.RollingUpdateStrategyType))
    62  	g.Expect(kcpTemplate.Spec.Template.Spec.RolloutStrategy.RollingUpdate.MaxSurge.IntVal).To(Equal(int32(1)))
    63  }
    64  
    65  func TestKubeadmControlPlaneTemplateValidationFeatureGateEnabled(t *testing.T) {
    66  	defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.ClusterTopology, true)()
    67  
    68  	t.Run("create kubeadmcontrolplanetemplate should pass if gate enabled and valid kubeadmcontrolplanetemplate", func(t *testing.T) {
    69  		testnamespace := "test"
    70  		g := NewWithT(t)
    71  		kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
    72  			ObjectMeta: metav1.ObjectMeta{
    73  				Name:      "kubeadmcontrolplanetemplate-test",
    74  				Namespace: testnamespace,
    75  			},
    76  			Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
    77  				Template: controlplanev1.KubeadmControlPlaneTemplateResource{
    78  					Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
    79  						MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
    80  							NodeDrainTimeout: &metav1.Duration{Duration: time.Second},
    81  						},
    82  					},
    83  				},
    84  			},
    85  		}
    86  		webhook := &KubeadmControlPlaneTemplate{}
    87  		warnings, err := webhook.ValidateCreate(ctx, kcpTemplate)
    88  		g.Expect(err).ToNot(HaveOccurred())
    89  		g.Expect(warnings).To(BeEmpty())
    90  	})
    91  }
    92  
    93  func TestKubeadmControlPlaneTemplateValidationFeatureGateDisabled(t *testing.T) {
    94  	// NOTE: ClusterTopology feature flag is disabled by default, thus preventing to create KubeadmControlPlaneTemplate.
    95  	t.Run("create kubeadmcontrolplanetemplate should not pass if gate disabled and valid kubeadmcontrolplanetemplate", func(t *testing.T) {
    96  		testnamespace := "test"
    97  		g := NewWithT(t)
    98  		kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
    99  			ObjectMeta: metav1.ObjectMeta{
   100  				Name:      "kubeadmcontrolplanetemplate-test",
   101  				Namespace: testnamespace,
   102  			},
   103  			Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
   104  				Template: controlplanev1.KubeadmControlPlaneTemplateResource{
   105  					Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
   106  						MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
   107  							NodeDrainTimeout: &metav1.Duration{Duration: time.Second},
   108  						},
   109  					},
   110  				},
   111  			},
   112  		}
   113  		webhook := &KubeadmControlPlaneTemplate{}
   114  		warnings, err := webhook.ValidateCreate(ctx, kcpTemplate)
   115  		g.Expect(err).To(HaveOccurred())
   116  		g.Expect(warnings).To(BeEmpty())
   117  	})
   118  }
   119  
   120  func TestKubeadmControlPlaneTemplateValidationMetadata(t *testing.T) {
   121  	t.Run("create kubeadmcontrolplanetemplate should not pass if metadata is invalid", func(t *testing.T) {
   122  		g := NewWithT(t)
   123  		kcpTemplate := &controlplanev1.KubeadmControlPlaneTemplate{
   124  			Spec: controlplanev1.KubeadmControlPlaneTemplateSpec{
   125  				Template: controlplanev1.KubeadmControlPlaneTemplateResource{
   126  					ObjectMeta: clusterv1.ObjectMeta{
   127  						Labels: map[string]string{
   128  							"foo":          "$invalid-key",
   129  							"bar":          strings.Repeat("a", 64) + "too-long-value",
   130  							"/invalid-key": "foo",
   131  						},
   132  						Annotations: map[string]string{
   133  							"/invalid-key": "foo",
   134  						},
   135  					},
   136  					Spec: controlplanev1.KubeadmControlPlaneTemplateResourceSpec{
   137  						MachineTemplate: &controlplanev1.KubeadmControlPlaneTemplateMachineTemplate{
   138  							ObjectMeta: clusterv1.ObjectMeta{
   139  								Labels: map[string]string{
   140  									"foo":          "$invalid-key",
   141  									"bar":          strings.Repeat("a", 64) + "too-long-value",
   142  									"/invalid-key": "foo",
   143  								},
   144  								Annotations: map[string]string{
   145  									"/invalid-key": "foo",
   146  								},
   147  							},
   148  						},
   149  					},
   150  				},
   151  			},
   152  		}
   153  		webhook := &KubeadmControlPlaneTemplate{}
   154  		warnings, err := webhook.ValidateCreate(ctx, kcpTemplate)
   155  		g.Expect(err).To(HaveOccurred())
   156  		g.Expect(warnings).To(BeEmpty())
   157  	})
   158  }