sigs.k8s.io/cluster-api@v1.7.1/internal/webhooks/machine_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  	"testing"
    21  
    22  	. "github.com/onsi/gomega"
    23  	corev1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/utils/ptr"
    26  
    27  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    28  	"sigs.k8s.io/cluster-api/internal/webhooks/util"
    29  )
    30  
    31  func TestMachineDefault(t *testing.T) {
    32  	g := NewWithT(t)
    33  
    34  	m := &clusterv1.Machine{
    35  		ObjectMeta: metav1.ObjectMeta{
    36  			Namespace: "foobar",
    37  		},
    38  		Spec: clusterv1.MachineSpec{
    39  			Bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}},
    40  			Version:   ptr.To("1.17.5"),
    41  		},
    42  	}
    43  
    44  	webhook := &Machine{}
    45  
    46  	t.Run("for Machine", util.CustomDefaultValidateTest(ctx, m, webhook))
    47  	g.Expect(webhook.Default(ctx, m)).To(Succeed())
    48  
    49  	g.Expect(m.Labels[clusterv1.ClusterNameLabel]).To(Equal(m.Spec.ClusterName))
    50  	g.Expect(m.Spec.Bootstrap.ConfigRef.Namespace).To(Equal(m.Namespace))
    51  	g.Expect(m.Spec.InfrastructureRef.Namespace).To(Equal(m.Namespace))
    52  	g.Expect(*m.Spec.Version).To(Equal("v1.17.5"))
    53  	g.Expect(m.Spec.NodeDeletionTimeout.Duration).To(Equal(defaultNodeDeletionTimeout))
    54  }
    55  
    56  func TestMachineBootstrapValidation(t *testing.T) {
    57  	tests := []struct {
    58  		name      string
    59  		bootstrap clusterv1.Bootstrap
    60  		expectErr bool
    61  	}{
    62  		{
    63  			name:      "should return error if configref and data are nil",
    64  			bootstrap: clusterv1.Bootstrap{ConfigRef: nil, DataSecretName: nil},
    65  			expectErr: true,
    66  		},
    67  		{
    68  			name:      "should not return error if dataSecretName is set",
    69  			bootstrap: clusterv1.Bootstrap{ConfigRef: nil, DataSecretName: ptr.To("test")},
    70  			expectErr: false,
    71  		},
    72  		{
    73  			name:      "should not return error if config ref is set",
    74  			bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}, DataSecretName: nil},
    75  			expectErr: false,
    76  		},
    77  	}
    78  
    79  	for _, tt := range tests {
    80  		t.Run(tt.name, func(t *testing.T) {
    81  			g := NewWithT(t)
    82  			m := &clusterv1.Machine{
    83  				Spec: clusterv1.MachineSpec{Bootstrap: tt.bootstrap},
    84  			}
    85  			webhook := &Machine{}
    86  
    87  			if tt.expectErr {
    88  				warnings, err := webhook.ValidateCreate(ctx, m)
    89  				g.Expect(err).To(HaveOccurred())
    90  				g.Expect(warnings).To(BeEmpty())
    91  				warnings, err = webhook.ValidateUpdate(ctx, m, m)
    92  				g.Expect(err).To(HaveOccurred())
    93  				g.Expect(warnings).To(BeEmpty())
    94  			} else {
    95  				warnings, err := webhook.ValidateCreate(ctx, m)
    96  				g.Expect(err).ToNot(HaveOccurred())
    97  				g.Expect(warnings).To(BeEmpty())
    98  				warnings, err = webhook.ValidateUpdate(ctx, m, m)
    99  				g.Expect(err).ToNot(HaveOccurred())
   100  				g.Expect(warnings).To(BeEmpty())
   101  			}
   102  		})
   103  	}
   104  }
   105  
   106  func TestMachineNamespaceValidation(t *testing.T) {
   107  	tests := []struct {
   108  		name      string
   109  		expectErr bool
   110  		bootstrap clusterv1.Bootstrap
   111  		infraRef  corev1.ObjectReference
   112  		namespace string
   113  	}{
   114  		{
   115  			name:      "should succeed if all namespaces match",
   116  			expectErr: false,
   117  			namespace: "foobar",
   118  			bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar"}},
   119  			infraRef:  corev1.ObjectReference{Namespace: "foobar"},
   120  		},
   121  		{
   122  			name:      "should return error if namespace and bootstrap namespace don't match",
   123  			expectErr: true,
   124  			namespace: "foobar",
   125  			bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar123"}},
   126  			infraRef:  corev1.ObjectReference{Namespace: "foobar"},
   127  		},
   128  		{
   129  			name:      "should return error if namespace and infrastructure ref namespace don't match",
   130  			expectErr: true,
   131  			namespace: "foobar",
   132  			bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar"}},
   133  			infraRef:  corev1.ObjectReference{Namespace: "foobar123"},
   134  		},
   135  		{
   136  			name:      "should return error if no namespaces match",
   137  			expectErr: true,
   138  			namespace: "foobar1",
   139  			bootstrap: clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{Namespace: "foobar2"}},
   140  			infraRef:  corev1.ObjectReference{Namespace: "foobar3"},
   141  		},
   142  	}
   143  
   144  	for _, tt := range tests {
   145  		t.Run(tt.name, func(t *testing.T) {
   146  			g := NewWithT(t)
   147  
   148  			m := &clusterv1.Machine{
   149  				ObjectMeta: metav1.ObjectMeta{Namespace: tt.namespace},
   150  				Spec:       clusterv1.MachineSpec{Bootstrap: tt.bootstrap, InfrastructureRef: tt.infraRef},
   151  			}
   152  			webhook := &Machine{}
   153  
   154  			if tt.expectErr {
   155  				warnings, err := webhook.ValidateCreate(ctx, m)
   156  				g.Expect(err).To(HaveOccurred())
   157  				g.Expect(warnings).To(BeEmpty())
   158  				warnings, err = webhook.ValidateUpdate(ctx, m, m)
   159  				g.Expect(err).To(HaveOccurred())
   160  				g.Expect(warnings).To(BeEmpty())
   161  			} else {
   162  				warnings, err := webhook.ValidateCreate(ctx, m)
   163  				g.Expect(err).ToNot(HaveOccurred())
   164  				g.Expect(warnings).To(BeEmpty())
   165  				warnings, err = webhook.ValidateUpdate(ctx, m, m)
   166  				g.Expect(err).ToNot(HaveOccurred())
   167  				g.Expect(warnings).To(BeEmpty())
   168  			}
   169  		})
   170  	}
   171  }
   172  
   173  func TestMachineClusterNameImmutable(t *testing.T) {
   174  	tests := []struct {
   175  		name           string
   176  		oldClusterName string
   177  		newClusterName string
   178  		expectErr      bool
   179  	}{
   180  		{
   181  			name:           "when the cluster name has not changed",
   182  			oldClusterName: "foo",
   183  			newClusterName: "foo",
   184  			expectErr:      false,
   185  		},
   186  		{
   187  			name:           "when the cluster name has changed",
   188  			oldClusterName: "foo",
   189  			newClusterName: "bar",
   190  			expectErr:      true,
   191  		},
   192  	}
   193  
   194  	for _, tt := range tests {
   195  		t.Run(tt.name, func(t *testing.T) {
   196  			g := NewWithT(t)
   197  
   198  			newMachine := &clusterv1.Machine{
   199  				Spec: clusterv1.MachineSpec{
   200  					ClusterName: tt.newClusterName,
   201  					Bootstrap:   clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}},
   202  				},
   203  			}
   204  			oldMachine := &clusterv1.Machine{
   205  				Spec: clusterv1.MachineSpec{
   206  					ClusterName: tt.oldClusterName,
   207  					Bootstrap:   clusterv1.Bootstrap{ConfigRef: &corev1.ObjectReference{}},
   208  				},
   209  			}
   210  
   211  			warnings, err := (&Machine{}).ValidateUpdate(ctx, oldMachine, newMachine)
   212  			if tt.expectErr {
   213  				g.Expect(err).To(HaveOccurred())
   214  				g.Expect(warnings).To(BeEmpty())
   215  			} else {
   216  				g.Expect(err).ToNot(HaveOccurred())
   217  				g.Expect(warnings).To(BeEmpty())
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  func TestMachineVersionValidation(t *testing.T) {
   224  	tests := []struct {
   225  		name      string
   226  		version   string
   227  		expectErr bool
   228  	}{
   229  		{
   230  			name:      "should succeed when given a valid semantic version with prepended 'v'",
   231  			version:   "v1.17.2",
   232  			expectErr: false,
   233  		},
   234  		{
   235  			name:      "should return error when given a valid semantic version without 'v'",
   236  			version:   "1.17.2",
   237  			expectErr: true,
   238  		},
   239  		{
   240  			name:      "should return error when given an invalid semantic version",
   241  			version:   "1",
   242  			expectErr: true,
   243  		},
   244  		{
   245  			name:      "should return error when given an invalid semantic version",
   246  			version:   "v1",
   247  			expectErr: true,
   248  		},
   249  		{
   250  			name:      "should return error when given an invalid semantic version",
   251  			version:   "wrong_version",
   252  			expectErr: true,
   253  		},
   254  	}
   255  
   256  	for i := range tests {
   257  		tt := tests[i]
   258  		t.Run(tt.name, func(t *testing.T) {
   259  			g := NewWithT(t)
   260  
   261  			m := &clusterv1.Machine{
   262  				Spec: clusterv1.MachineSpec{
   263  					Version:   &tt.version,
   264  					Bootstrap: clusterv1.Bootstrap{ConfigRef: nil, DataSecretName: ptr.To("test")},
   265  				},
   266  			}
   267  			webhook := &Machine{}
   268  
   269  			if tt.expectErr {
   270  				warnings, err := webhook.ValidateCreate(ctx, m)
   271  				g.Expect(err).To(HaveOccurred())
   272  				g.Expect(warnings).To(BeEmpty())
   273  				warnings, err = webhook.ValidateUpdate(ctx, m, m)
   274  				g.Expect(err).To(HaveOccurred())
   275  				g.Expect(warnings).To(BeEmpty())
   276  			} else {
   277  				warnings, err := webhook.ValidateCreate(ctx, m)
   278  				g.Expect(err).ToNot(HaveOccurred())
   279  				g.Expect(warnings).To(BeEmpty())
   280  				warnings, err = webhook.ValidateUpdate(ctx, m, m)
   281  				g.Expect(err).ToNot(HaveOccurred())
   282  				g.Expect(warnings).To(BeEmpty())
   283  			}
   284  		})
   285  	}
   286  }