sigs.k8s.io/cluster-api@v1.6.3/controlplane/kubeadm/internal/filters_test.go (about)

     1  /*
     2  Copyright 2020 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 internal
    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/apimachinery/pkg/apis/meta/v1/unstructured"
    26  
    27  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    28  	bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
    29  	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
    30  )
    31  
    32  func TestMatchClusterConfiguration(t *testing.T) {
    33  	t.Run("machine without the ClusterConfiguration annotation should match (not enough information to make a decision)", func(t *testing.T) {
    34  		g := NewWithT(t)
    35  		kcp := &controlplanev1.KubeadmControlPlane{}
    36  		m := &clusterv1.Machine{}
    37  		g.Expect(matchClusterConfiguration(kcp, m)).To(BeTrue())
    38  	})
    39  	t.Run("machine without an invalid ClusterConfiguration annotation should not match (only solution is to rollout)", func(t *testing.T) {
    40  		g := NewWithT(t)
    41  		kcp := &controlplanev1.KubeadmControlPlane{}
    42  		m := &clusterv1.Machine{
    43  			ObjectMeta: metav1.ObjectMeta{
    44  				Annotations: map[string]string{
    45  					controlplanev1.KubeadmClusterConfigurationAnnotation: "$|^^_",
    46  				},
    47  			},
    48  		}
    49  		g.Expect(matchClusterConfiguration(kcp, m)).To(BeFalse())
    50  	})
    51  	t.Run("Return true if cluster configuration matches", func(t *testing.T) {
    52  		g := NewWithT(t)
    53  		kcp := &controlplanev1.KubeadmControlPlane{
    54  			Spec: controlplanev1.KubeadmControlPlaneSpec{
    55  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
    56  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{
    57  						ClusterName: "foo",
    58  					},
    59  				},
    60  			},
    61  		}
    62  		m := &clusterv1.Machine{
    63  			ObjectMeta: metav1.ObjectMeta{
    64  				Annotations: map[string]string{
    65  					controlplanev1.KubeadmClusterConfigurationAnnotation: "{\n  \"clusterName\": \"foo\"\n}",
    66  				},
    67  			},
    68  		}
    69  		g.Expect(matchClusterConfiguration(kcp, m)).To(BeTrue())
    70  	})
    71  	t.Run("Return false if cluster configuration does not match", func(t *testing.T) {
    72  		g := NewWithT(t)
    73  		kcp := &controlplanev1.KubeadmControlPlane{
    74  			Spec: controlplanev1.KubeadmControlPlaneSpec{
    75  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
    76  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{
    77  						ClusterName: "foo",
    78  					},
    79  				},
    80  			},
    81  		}
    82  		m := &clusterv1.Machine{
    83  			ObjectMeta: metav1.ObjectMeta{
    84  				Annotations: map[string]string{
    85  					controlplanev1.KubeadmClusterConfigurationAnnotation: "{\n  \"clusterName\": \"bar\"\n}",
    86  				},
    87  			},
    88  		}
    89  		g.Expect(matchClusterConfiguration(kcp, m)).To(BeFalse())
    90  	})
    91  	t.Run("Return true if cluster configuration is nil (special case)", func(t *testing.T) {
    92  		g := NewWithT(t)
    93  		kcp := &controlplanev1.KubeadmControlPlane{
    94  			Spec: controlplanev1.KubeadmControlPlaneSpec{
    95  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{},
    96  			},
    97  		}
    98  		m := &clusterv1.Machine{
    99  			ObjectMeta: metav1.ObjectMeta{
   100  				Annotations: map[string]string{
   101  					controlplanev1.KubeadmClusterConfigurationAnnotation: "null",
   102  				},
   103  			},
   104  		}
   105  		g.Expect(matchClusterConfiguration(kcp, m)).To(BeTrue())
   106  	})
   107  }
   108  
   109  func TestGetAdjustedKcpConfig(t *testing.T) {
   110  	t.Run("if the machine is the first control plane, kcp config should get InitConfiguration", func(t *testing.T) {
   111  		g := NewWithT(t)
   112  		kcp := &controlplanev1.KubeadmControlPlane{
   113  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   114  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   115  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   116  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   117  				},
   118  			},
   119  		}
   120  		machineConfig := &bootstrapv1.KubeadmConfig{
   121  			Spec: bootstrapv1.KubeadmConfigSpec{
   122  				InitConfiguration: &bootstrapv1.InitConfiguration{}, // first control-plane
   123  			},
   124  		}
   125  		kcpConfig := getAdjustedKcpConfig(kcp, machineConfig)
   126  		g.Expect(kcpConfig.InitConfiguration).ToNot(BeNil())
   127  		g.Expect(kcpConfig.JoinConfiguration).To(BeNil())
   128  	})
   129  	t.Run("if the machine is a joining control plane, kcp config should get JoinConfiguration", func(t *testing.T) {
   130  		g := NewWithT(t)
   131  		kcp := &controlplanev1.KubeadmControlPlane{
   132  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   133  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   134  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   135  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   136  				},
   137  			},
   138  		}
   139  		machineConfig := &bootstrapv1.KubeadmConfig{
   140  			Spec: bootstrapv1.KubeadmConfigSpec{
   141  				JoinConfiguration: &bootstrapv1.JoinConfiguration{}, // joining control-plane
   142  			},
   143  		}
   144  		kcpConfig := getAdjustedKcpConfig(kcp, machineConfig)
   145  		g.Expect(kcpConfig.InitConfiguration).To(BeNil())
   146  		g.Expect(kcpConfig.JoinConfiguration).ToNot(BeNil())
   147  	})
   148  }
   149  
   150  func TestCleanupConfigFields(t *testing.T) {
   151  	t.Run("ClusterConfiguration gets removed from KcpConfig and MachineConfig", func(t *testing.T) {
   152  		g := NewWithT(t)
   153  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   154  			ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   155  		}
   156  		machineConfig := &bootstrapv1.KubeadmConfig{
   157  			Spec: bootstrapv1.KubeadmConfigSpec{
   158  				ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   159  			},
   160  		}
   161  		cleanupConfigFields(kcpConfig, machineConfig)
   162  		g.Expect(kcpConfig.ClusterConfiguration).To(BeNil())
   163  		g.Expect(machineConfig.Spec.ClusterConfiguration).To(BeNil())
   164  	})
   165  	t.Run("JoinConfiguration gets removed from MachineConfig if it was not derived by KCPConfig", func(t *testing.T) {
   166  		g := NewWithT(t)
   167  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   168  			JoinConfiguration: nil, // KCP not providing a JoinConfiguration
   169  		}
   170  		machineConfig := &bootstrapv1.KubeadmConfig{
   171  			Spec: bootstrapv1.KubeadmConfigSpec{
   172  				JoinConfiguration: &bootstrapv1.JoinConfiguration{}, // Machine gets a default JoinConfiguration from CABPK
   173  			},
   174  		}
   175  		cleanupConfigFields(kcpConfig, machineConfig)
   176  		g.Expect(kcpConfig.JoinConfiguration).To(BeNil())
   177  		g.Expect(machineConfig.Spec.JoinConfiguration).To(BeNil())
   178  	})
   179  	t.Run("JoinConfiguration.Discovery gets removed because it is not relevant for compare", func(t *testing.T) {
   180  		g := NewWithT(t)
   181  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   182  			JoinConfiguration: &bootstrapv1.JoinConfiguration{
   183  				Discovery: bootstrapv1.Discovery{TLSBootstrapToken: "aaa"},
   184  			},
   185  		}
   186  		machineConfig := &bootstrapv1.KubeadmConfig{
   187  			Spec: bootstrapv1.KubeadmConfigSpec{
   188  				JoinConfiguration: &bootstrapv1.JoinConfiguration{
   189  					Discovery: bootstrapv1.Discovery{TLSBootstrapToken: "aaa"},
   190  				},
   191  			},
   192  		}
   193  		cleanupConfigFields(kcpConfig, machineConfig)
   194  		g.Expect(kcpConfig.JoinConfiguration.Discovery).To(BeComparableTo(bootstrapv1.Discovery{}))
   195  		g.Expect(machineConfig.Spec.JoinConfiguration.Discovery).To(BeComparableTo(bootstrapv1.Discovery{}))
   196  	})
   197  	t.Run("JoinConfiguration.ControlPlane gets removed from MachineConfig if it was not derived by KCPConfig", func(t *testing.T) {
   198  		g := NewWithT(t)
   199  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   200  			JoinConfiguration: &bootstrapv1.JoinConfiguration{
   201  				ControlPlane: nil, // Control plane configuration missing in KCP
   202  			},
   203  		}
   204  		machineConfig := &bootstrapv1.KubeadmConfig{
   205  			Spec: bootstrapv1.KubeadmConfigSpec{
   206  				JoinConfiguration: &bootstrapv1.JoinConfiguration{
   207  					ControlPlane: &bootstrapv1.JoinControlPlane{}, // Machine gets a default JoinConfiguration.ControlPlane from CABPK
   208  				},
   209  			},
   210  		}
   211  		cleanupConfigFields(kcpConfig, machineConfig)
   212  		g.Expect(kcpConfig.JoinConfiguration).ToNot(BeNil())
   213  		g.Expect(machineConfig.Spec.JoinConfiguration.ControlPlane).To(BeNil())
   214  	})
   215  	t.Run("JoinConfiguration.NodeRegistrationOptions gets removed from MachineConfig if it was not derived by KCPConfig", func(t *testing.T) {
   216  		g := NewWithT(t)
   217  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   218  			JoinConfiguration: &bootstrapv1.JoinConfiguration{
   219  				NodeRegistration: bootstrapv1.NodeRegistrationOptions{}, // NodeRegistrationOptions configuration missing in KCP
   220  			},
   221  		}
   222  		machineConfig := &bootstrapv1.KubeadmConfig{
   223  			Spec: bootstrapv1.KubeadmConfigSpec{
   224  				JoinConfiguration: &bootstrapv1.JoinConfiguration{
   225  					NodeRegistration: bootstrapv1.NodeRegistrationOptions{Name: "test"}, // Machine gets a some JoinConfiguration.NodeRegistrationOptions
   226  				},
   227  			},
   228  		}
   229  		cleanupConfigFields(kcpConfig, machineConfig)
   230  		g.Expect(kcpConfig.JoinConfiguration).ToNot(BeNil())
   231  		g.Expect(machineConfig.Spec.JoinConfiguration.NodeRegistration).To(BeComparableTo(bootstrapv1.NodeRegistrationOptions{}))
   232  	})
   233  	t.Run("InitConfiguration.TypeMeta gets removed from MachineConfig", func(t *testing.T) {
   234  		g := NewWithT(t)
   235  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   236  			InitConfiguration: &bootstrapv1.InitConfiguration{},
   237  		}
   238  		machineConfig := &bootstrapv1.KubeadmConfig{
   239  			Spec: bootstrapv1.KubeadmConfigSpec{
   240  				InitConfiguration: &bootstrapv1.InitConfiguration{
   241  					TypeMeta: metav1.TypeMeta{
   242  						Kind:       "JoinConfiguration",
   243  						APIVersion: bootstrapv1.GroupVersion.String(),
   244  					},
   245  				},
   246  			},
   247  		}
   248  		cleanupConfigFields(kcpConfig, machineConfig)
   249  		g.Expect(kcpConfig.InitConfiguration).ToNot(BeNil())
   250  		g.Expect(machineConfig.Spec.InitConfiguration.TypeMeta).To(BeComparableTo(metav1.TypeMeta{}))
   251  	})
   252  	t.Run("JoinConfiguration.TypeMeta gets removed from MachineConfig", func(t *testing.T) {
   253  		g := NewWithT(t)
   254  		kcpConfig := &bootstrapv1.KubeadmConfigSpec{
   255  			JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   256  		}
   257  		machineConfig := &bootstrapv1.KubeadmConfig{
   258  			Spec: bootstrapv1.KubeadmConfigSpec{
   259  				JoinConfiguration: &bootstrapv1.JoinConfiguration{
   260  					TypeMeta: metav1.TypeMeta{
   261  						Kind:       "JoinConfiguration",
   262  						APIVersion: bootstrapv1.GroupVersion.String(),
   263  					},
   264  				},
   265  			},
   266  		}
   267  		cleanupConfigFields(kcpConfig, machineConfig)
   268  		g.Expect(kcpConfig.JoinConfiguration).ToNot(BeNil())
   269  		g.Expect(machineConfig.Spec.JoinConfiguration.TypeMeta).To(BeComparableTo(metav1.TypeMeta{}))
   270  	})
   271  }
   272  
   273  func TestMatchInitOrJoinConfiguration(t *testing.T) {
   274  	t.Run("returns true if the machine does not have a bootstrap config", func(t *testing.T) {
   275  		g := NewWithT(t)
   276  		kcp := &controlplanev1.KubeadmControlPlane{}
   277  		g.Expect(matchInitOrJoinConfiguration(nil, kcp)).To(BeTrue())
   278  	})
   279  	t.Run("returns true if the there are problems reading the bootstrap config", func(t *testing.T) {
   280  		g := NewWithT(t)
   281  		kcp := &controlplanev1.KubeadmControlPlane{}
   282  		g.Expect(matchInitOrJoinConfiguration(nil, kcp)).To(BeTrue())
   283  	})
   284  	t.Run("returns true if one format is empty and the other one cloud-config", func(t *testing.T) {
   285  		g := NewWithT(t)
   286  		kcp := &controlplanev1.KubeadmControlPlane{
   287  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   288  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   289  					Format: bootstrapv1.CloudConfig,
   290  				},
   291  			},
   292  		}
   293  		m := &clusterv1.Machine{
   294  			TypeMeta: metav1.TypeMeta{
   295  				Kind:       "KubeadmConfig",
   296  				APIVersion: clusterv1.GroupVersion.String(),
   297  			},
   298  			ObjectMeta: metav1.ObjectMeta{
   299  				Namespace: "default",
   300  				Name:      "test",
   301  			},
   302  			Spec: clusterv1.MachineSpec{
   303  				Bootstrap: clusterv1.Bootstrap{
   304  					ConfigRef: &corev1.ObjectReference{
   305  						Kind:       "KubeadmConfig",
   306  						Namespace:  "default",
   307  						Name:       "test",
   308  						APIVersion: bootstrapv1.GroupVersion.String(),
   309  					},
   310  				},
   311  			},
   312  		}
   313  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   314  			m.Name: {
   315  				TypeMeta: metav1.TypeMeta{
   316  					Kind:       "KubeadmConfig",
   317  					APIVersion: bootstrapv1.GroupVersion.String(),
   318  				},
   319  				ObjectMeta: metav1.ObjectMeta{
   320  					Namespace: "default",
   321  					Name:      "test",
   322  				},
   323  				Spec: bootstrapv1.KubeadmConfigSpec{
   324  					Format: "",
   325  				},
   326  			},
   327  		}
   328  		g.Expect(matchInitOrJoinConfiguration(machineConfigs[m.Name], kcp)).To(BeTrue())
   329  	})
   330  	t.Run("returns true if InitConfiguration is equal", func(t *testing.T) {
   331  		g := NewWithT(t)
   332  		kcp := &controlplanev1.KubeadmControlPlane{
   333  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   334  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   335  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   336  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   337  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   338  				},
   339  			},
   340  		}
   341  		m := &clusterv1.Machine{
   342  			TypeMeta: metav1.TypeMeta{
   343  				Kind:       "KubeadmConfig",
   344  				APIVersion: clusterv1.GroupVersion.String(),
   345  			},
   346  			ObjectMeta: metav1.ObjectMeta{
   347  				Namespace: "default",
   348  				Name:      "test",
   349  			},
   350  			Spec: clusterv1.MachineSpec{
   351  				Bootstrap: clusterv1.Bootstrap{
   352  					ConfigRef: &corev1.ObjectReference{
   353  						Kind:       "KubeadmConfig",
   354  						Namespace:  "default",
   355  						Name:       "test",
   356  						APIVersion: bootstrapv1.GroupVersion.String(),
   357  					},
   358  				},
   359  			},
   360  		}
   361  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   362  			m.Name: {
   363  				TypeMeta: metav1.TypeMeta{
   364  					Kind:       "KubeadmConfig",
   365  					APIVersion: bootstrapv1.GroupVersion.String(),
   366  				},
   367  				ObjectMeta: metav1.ObjectMeta{
   368  					Namespace: "default",
   369  					Name:      "test",
   370  				},
   371  				Spec: bootstrapv1.KubeadmConfigSpec{
   372  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   373  				},
   374  			},
   375  		}
   376  		g.Expect(matchInitOrJoinConfiguration(machineConfigs[m.Name], kcp)).To(BeTrue())
   377  	})
   378  	t.Run("returns false if InitConfiguration is NOT equal", func(t *testing.T) {
   379  		g := NewWithT(t)
   380  		kcp := &controlplanev1.KubeadmControlPlane{
   381  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   382  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   383  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   384  					InitConfiguration: &bootstrapv1.InitConfiguration{
   385  						NodeRegistration: bootstrapv1.NodeRegistrationOptions{
   386  							Name: "A new name", // This is a change
   387  						},
   388  					},
   389  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   390  				},
   391  			},
   392  		}
   393  		m := &clusterv1.Machine{
   394  			TypeMeta: metav1.TypeMeta{
   395  				Kind:       "KubeadmConfig",
   396  				APIVersion: clusterv1.GroupVersion.String(),
   397  			},
   398  			ObjectMeta: metav1.ObjectMeta{
   399  				Namespace: "default",
   400  				Name:      "test",
   401  			},
   402  			Spec: clusterv1.MachineSpec{
   403  				Bootstrap: clusterv1.Bootstrap{
   404  					ConfigRef: &corev1.ObjectReference{
   405  						Kind:       "KubeadmConfig",
   406  						Namespace:  "default",
   407  						Name:       "test",
   408  						APIVersion: bootstrapv1.GroupVersion.String(),
   409  					},
   410  				},
   411  			},
   412  		}
   413  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   414  			m.Name: {
   415  				TypeMeta: metav1.TypeMeta{
   416  					Kind:       "KubeadmConfig",
   417  					APIVersion: bootstrapv1.GroupVersion.String(),
   418  				},
   419  				ObjectMeta: metav1.ObjectMeta{
   420  					Namespace: "default",
   421  					Name:      "test",
   422  				},
   423  				Spec: bootstrapv1.KubeadmConfigSpec{
   424  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   425  				},
   426  			},
   427  		}
   428  		g.Expect(matchInitOrJoinConfiguration(machineConfigs[m.Name], kcp)).To(BeFalse())
   429  	})
   430  	t.Run("returns true if JoinConfiguration is equal", func(t *testing.T) {
   431  		g := NewWithT(t)
   432  		kcp := &controlplanev1.KubeadmControlPlane{
   433  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   434  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   435  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   436  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   437  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   438  				},
   439  			},
   440  		}
   441  		m := &clusterv1.Machine{
   442  			TypeMeta: metav1.TypeMeta{
   443  				Kind:       "KubeadmConfig",
   444  				APIVersion: clusterv1.GroupVersion.String(),
   445  			},
   446  			ObjectMeta: metav1.ObjectMeta{
   447  				Namespace: "default",
   448  				Name:      "test",
   449  			},
   450  			Spec: clusterv1.MachineSpec{
   451  				Bootstrap: clusterv1.Bootstrap{
   452  					ConfigRef: &corev1.ObjectReference{
   453  						Kind:       "KubeadmConfig",
   454  						Namespace:  "default",
   455  						Name:       "test",
   456  						APIVersion: bootstrapv1.GroupVersion.String(),
   457  					},
   458  				},
   459  			},
   460  		}
   461  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   462  			m.Name: {
   463  				TypeMeta: metav1.TypeMeta{
   464  					Kind:       "KubeadmConfig",
   465  					APIVersion: bootstrapv1.GroupVersion.String(),
   466  				},
   467  				ObjectMeta: metav1.ObjectMeta{
   468  					Namespace: "default",
   469  					Name:      "test",
   470  				},
   471  				Spec: bootstrapv1.KubeadmConfigSpec{
   472  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   473  				},
   474  			},
   475  		}
   476  		g.Expect(matchInitOrJoinConfiguration(machineConfigs[m.Name], kcp)).To(BeTrue())
   477  	})
   478  	t.Run("returns false if JoinConfiguration is NOT equal", func(t *testing.T) {
   479  		g := NewWithT(t)
   480  		kcp := &controlplanev1.KubeadmControlPlane{
   481  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   482  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   483  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   484  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   485  					JoinConfiguration: &bootstrapv1.JoinConfiguration{
   486  						NodeRegistration: bootstrapv1.NodeRegistrationOptions{
   487  							Name: "A new name", // This is a change
   488  						},
   489  					},
   490  				},
   491  			},
   492  		}
   493  		m := &clusterv1.Machine{
   494  			TypeMeta: metav1.TypeMeta{
   495  				Kind:       "KubeadmConfig",
   496  				APIVersion: clusterv1.GroupVersion.String(),
   497  			},
   498  			ObjectMeta: metav1.ObjectMeta{
   499  				Namespace: "default",
   500  				Name:      "test",
   501  			},
   502  			Spec: clusterv1.MachineSpec{
   503  				Bootstrap: clusterv1.Bootstrap{
   504  					ConfigRef: &corev1.ObjectReference{
   505  						Kind:       "KubeadmConfig",
   506  						Namespace:  "default",
   507  						Name:       "test",
   508  						APIVersion: bootstrapv1.GroupVersion.String(),
   509  					},
   510  				},
   511  			},
   512  		}
   513  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   514  			m.Name: {
   515  				TypeMeta: metav1.TypeMeta{
   516  					Kind:       "KubeadmConfig",
   517  					APIVersion: bootstrapv1.GroupVersion.String(),
   518  				},
   519  				ObjectMeta: metav1.ObjectMeta{
   520  					Namespace: "default",
   521  					Name:      "test",
   522  				},
   523  				Spec: bootstrapv1.KubeadmConfigSpec{
   524  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   525  				},
   526  			},
   527  		}
   528  		g.Expect(matchInitOrJoinConfiguration(machineConfigs[m.Name], kcp)).To(BeFalse())
   529  	})
   530  	t.Run("returns false if some other configurations are not equal", func(t *testing.T) {
   531  		g := NewWithT(t)
   532  		kcp := &controlplanev1.KubeadmControlPlane{
   533  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   534  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   535  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   536  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   537  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   538  					Files:                []bootstrapv1.File{}, // This is a change
   539  				},
   540  			},
   541  		}
   542  		m := &clusterv1.Machine{
   543  			TypeMeta: metav1.TypeMeta{
   544  				Kind:       "KubeadmConfig",
   545  				APIVersion: clusterv1.GroupVersion.String(),
   546  			},
   547  			ObjectMeta: metav1.ObjectMeta{
   548  				Namespace: "default",
   549  				Name:      "test",
   550  			},
   551  			Spec: clusterv1.MachineSpec{
   552  				Bootstrap: clusterv1.Bootstrap{
   553  					ConfigRef: &corev1.ObjectReference{
   554  						Kind:       "KubeadmConfig",
   555  						Namespace:  "default",
   556  						Name:       "test",
   557  						APIVersion: bootstrapv1.GroupVersion.String(),
   558  					},
   559  				},
   560  			},
   561  		}
   562  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   563  			m.Name: {
   564  				TypeMeta: metav1.TypeMeta{
   565  					Kind:       "KubeadmConfig",
   566  					APIVersion: bootstrapv1.GroupVersion.String(),
   567  				},
   568  				ObjectMeta: metav1.ObjectMeta{
   569  					Namespace: "default",
   570  					Name:      "test",
   571  				},
   572  				Spec: bootstrapv1.KubeadmConfigSpec{
   573  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   574  				},
   575  			},
   576  		}
   577  		g.Expect(matchInitOrJoinConfiguration(machineConfigs[m.Name], kcp)).To(BeFalse())
   578  	})
   579  }
   580  
   581  func TestMatchesKubeadmBootstrapConfig(t *testing.T) {
   582  	t.Run("returns true if ClusterConfiguration is equal", func(t *testing.T) {
   583  		g := NewWithT(t)
   584  		kcp := &controlplanev1.KubeadmControlPlane{
   585  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   586  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   587  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{
   588  						ClusterName: "foo",
   589  					},
   590  				},
   591  			},
   592  		}
   593  		m := &clusterv1.Machine{
   594  			ObjectMeta: metav1.ObjectMeta{
   595  				Annotations: map[string]string{
   596  					controlplanev1.KubeadmClusterConfigurationAnnotation: "{\n  \"clusterName\": \"foo\"\n}",
   597  				},
   598  			},
   599  		}
   600  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   601  			m.Name: {},
   602  		}
   603  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   604  		g.Expect(match).To(BeTrue())
   605  		g.Expect(reason).To(BeEmpty())
   606  	})
   607  	t.Run("returns false if ClusterConfiguration is NOT equal", func(t *testing.T) {
   608  		g := NewWithT(t)
   609  		kcp := &controlplanev1.KubeadmControlPlane{
   610  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   611  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   612  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{
   613  						ClusterName: "foo",
   614  					},
   615  				},
   616  			},
   617  		}
   618  		m := &clusterv1.Machine{
   619  			ObjectMeta: metav1.ObjectMeta{
   620  				Annotations: map[string]string{
   621  					controlplanev1.KubeadmClusterConfigurationAnnotation: "{\n  \"clusterName\": \"bar\"\n}",
   622  				},
   623  			},
   624  		}
   625  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   626  			m.Name: {},
   627  		}
   628  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   629  		g.Expect(match).To(BeFalse())
   630  		g.Expect(reason).To(Equal("Machine ClusterConfiguration is outdated"))
   631  	})
   632  	t.Run("returns true if InitConfiguration is equal", func(t *testing.T) {
   633  		g := NewWithT(t)
   634  		kcp := &controlplanev1.KubeadmControlPlane{
   635  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   636  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   637  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   638  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   639  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   640  				},
   641  			},
   642  		}
   643  		m := &clusterv1.Machine{
   644  			TypeMeta: metav1.TypeMeta{
   645  				Kind:       "KubeadmConfig",
   646  				APIVersion: clusterv1.GroupVersion.String(),
   647  			},
   648  			ObjectMeta: metav1.ObjectMeta{
   649  				Namespace: "default",
   650  				Name:      "test",
   651  			},
   652  			Spec: clusterv1.MachineSpec{
   653  				Bootstrap: clusterv1.Bootstrap{
   654  					ConfigRef: &corev1.ObjectReference{
   655  						Kind:       "KubeadmConfig",
   656  						Namespace:  "default",
   657  						Name:       "test",
   658  						APIVersion: bootstrapv1.GroupVersion.String(),
   659  					},
   660  				},
   661  			},
   662  		}
   663  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   664  			m.Name: {
   665  				TypeMeta: metav1.TypeMeta{
   666  					Kind:       "KubeadmConfig",
   667  					APIVersion: bootstrapv1.GroupVersion.String(),
   668  				},
   669  				ObjectMeta: metav1.ObjectMeta{
   670  					Namespace: "default",
   671  					Name:      "test",
   672  				},
   673  				Spec: bootstrapv1.KubeadmConfigSpec{
   674  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   675  				},
   676  			},
   677  		}
   678  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   679  		g.Expect(match).To(BeTrue())
   680  		g.Expect(reason).To(BeEmpty())
   681  	})
   682  	t.Run("returns false if InitConfiguration is NOT equal", func(t *testing.T) {
   683  		g := NewWithT(t)
   684  		kcp := &controlplanev1.KubeadmControlPlane{
   685  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   686  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   687  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   688  					InitConfiguration: &bootstrapv1.InitConfiguration{
   689  						NodeRegistration: bootstrapv1.NodeRegistrationOptions{
   690  							Name: "foo", // This is a change
   691  						},
   692  					},
   693  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   694  				},
   695  			},
   696  		}
   697  		m := &clusterv1.Machine{
   698  			TypeMeta: metav1.TypeMeta{
   699  				Kind:       "KubeadmConfig",
   700  				APIVersion: clusterv1.GroupVersion.String(),
   701  			},
   702  			ObjectMeta: metav1.ObjectMeta{
   703  				Namespace: "default",
   704  				Name:      "test",
   705  			},
   706  			Spec: clusterv1.MachineSpec{
   707  				Bootstrap: clusterv1.Bootstrap{
   708  					ConfigRef: &corev1.ObjectReference{
   709  						Kind:       "KubeadmConfig",
   710  						Namespace:  "default",
   711  						Name:       "test",
   712  						APIVersion: bootstrapv1.GroupVersion.String(),
   713  					},
   714  				},
   715  			},
   716  		}
   717  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   718  			m.Name: {
   719  				TypeMeta: metav1.TypeMeta{
   720  					Kind:       "KubeadmConfig",
   721  					APIVersion: bootstrapv1.GroupVersion.String(),
   722  				},
   723  				ObjectMeta: metav1.ObjectMeta{
   724  					Namespace: "default",
   725  					Name:      "test",
   726  				},
   727  				Spec: bootstrapv1.KubeadmConfigSpec{
   728  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   729  				},
   730  			},
   731  		}
   732  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   733  		g.Expect(match).To(BeFalse())
   734  		g.Expect(reason).To(Equal("Machine InitConfiguration or JoinConfiguration are outdated"))
   735  	})
   736  	t.Run("returns true if JoinConfiguration is equal", func(t *testing.T) {
   737  		g := NewWithT(t)
   738  		kcp := &controlplanev1.KubeadmControlPlane{
   739  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   740  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   741  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   742  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   743  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   744  				},
   745  			},
   746  		}
   747  		m := &clusterv1.Machine{
   748  			TypeMeta: metav1.TypeMeta{
   749  				Kind:       "KubeadmConfig",
   750  				APIVersion: clusterv1.GroupVersion.String(),
   751  			},
   752  			ObjectMeta: metav1.ObjectMeta{
   753  				Namespace: "default",
   754  				Name:      "test",
   755  			},
   756  			Spec: clusterv1.MachineSpec{
   757  				Bootstrap: clusterv1.Bootstrap{
   758  					ConfigRef: &corev1.ObjectReference{
   759  						Kind:       "KubeadmConfig",
   760  						Namespace:  "default",
   761  						Name:       "test",
   762  						APIVersion: bootstrapv1.GroupVersion.String(),
   763  					},
   764  				},
   765  			},
   766  		}
   767  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   768  			m.Name: {
   769  				TypeMeta: metav1.TypeMeta{
   770  					Kind:       "KubeadmConfig",
   771  					APIVersion: bootstrapv1.GroupVersion.String(),
   772  				},
   773  				ObjectMeta: metav1.ObjectMeta{
   774  					Namespace: "default",
   775  					Name:      "test",
   776  				},
   777  				Spec: bootstrapv1.KubeadmConfigSpec{
   778  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   779  				},
   780  			},
   781  		}
   782  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   783  		g.Expect(match).To(BeTrue())
   784  		g.Expect(reason).To(BeEmpty())
   785  	})
   786  	t.Run("returns false if JoinConfiguration is NOT equal", func(t *testing.T) {
   787  		g := NewWithT(t)
   788  		kcp := &controlplanev1.KubeadmControlPlane{
   789  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   790  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   791  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   792  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   793  					JoinConfiguration: &bootstrapv1.JoinConfiguration{
   794  						NodeRegistration: bootstrapv1.NodeRegistrationOptions{
   795  							Name: "foo", // This is a change
   796  						},
   797  					},
   798  				},
   799  			},
   800  		}
   801  		m := &clusterv1.Machine{
   802  			TypeMeta: metav1.TypeMeta{
   803  				Kind:       "KubeadmConfig",
   804  				APIVersion: clusterv1.GroupVersion.String(),
   805  			},
   806  			ObjectMeta: metav1.ObjectMeta{
   807  				Namespace: "default",
   808  				Name:      "test",
   809  			},
   810  			Spec: clusterv1.MachineSpec{
   811  				Bootstrap: clusterv1.Bootstrap{
   812  					ConfigRef: &corev1.ObjectReference{
   813  						Kind:       "KubeadmConfig",
   814  						Namespace:  "default",
   815  						Name:       "test",
   816  						APIVersion: bootstrapv1.GroupVersion.String(),
   817  					},
   818  				},
   819  			},
   820  		}
   821  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   822  			m.Name: {
   823  				TypeMeta: metav1.TypeMeta{
   824  					Kind:       "KubeadmConfig",
   825  					APIVersion: bootstrapv1.GroupVersion.String(),
   826  				},
   827  				ObjectMeta: metav1.ObjectMeta{
   828  					Namespace: "default",
   829  					Name:      "test",
   830  				},
   831  				Spec: bootstrapv1.KubeadmConfigSpec{
   832  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   833  				},
   834  			},
   835  		}
   836  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   837  		g.Expect(match).To(BeFalse())
   838  		g.Expect(reason).To(Equal("Machine InitConfiguration or JoinConfiguration are outdated"))
   839  	})
   840  	t.Run("returns false if some other configurations are not equal", func(t *testing.T) {
   841  		g := NewWithT(t)
   842  		kcp := &controlplanev1.KubeadmControlPlane{
   843  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   844  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   845  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   846  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   847  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   848  					Files:                []bootstrapv1.File{}, // This is a change
   849  				},
   850  			},
   851  		}
   852  		m := &clusterv1.Machine{
   853  			TypeMeta: metav1.TypeMeta{
   854  				Kind:       "KubeadmConfig",
   855  				APIVersion: clusterv1.GroupVersion.String(),
   856  			},
   857  			ObjectMeta: metav1.ObjectMeta{
   858  				Namespace: "default",
   859  				Name:      "test",
   860  			},
   861  			Spec: clusterv1.MachineSpec{
   862  				Bootstrap: clusterv1.Bootstrap{
   863  					ConfigRef: &corev1.ObjectReference{
   864  						Kind:       "KubeadmConfig",
   865  						Namespace:  "default",
   866  						Name:       "test",
   867  						APIVersion: bootstrapv1.GroupVersion.String(),
   868  					},
   869  				},
   870  			},
   871  		}
   872  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   873  			m.Name: {
   874  				TypeMeta: metav1.TypeMeta{
   875  					Kind:       "KubeadmConfig",
   876  					APIVersion: bootstrapv1.GroupVersion.String(),
   877  				},
   878  				ObjectMeta: metav1.ObjectMeta{
   879  					Namespace: "default",
   880  					Name:      "test",
   881  				},
   882  				Spec: bootstrapv1.KubeadmConfigSpec{
   883  					InitConfiguration: &bootstrapv1.InitConfiguration{},
   884  				},
   885  			},
   886  		}
   887  		reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   888  		g.Expect(match).To(BeFalse())
   889  		g.Expect(reason).To(Equal("Machine InitConfiguration or JoinConfiguration are outdated"))
   890  	})
   891  	t.Run("should match on labels and annotations", func(t *testing.T) {
   892  		kcp := &controlplanev1.KubeadmControlPlane{
   893  			Spec: controlplanev1.KubeadmControlPlaneSpec{
   894  				MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
   895  					ObjectMeta: clusterv1.ObjectMeta{
   896  						Annotations: map[string]string{
   897  							"test": "annotation",
   898  						},
   899  						Labels: map[string]string{
   900  							"test": "labels",
   901  						},
   902  					},
   903  				},
   904  				KubeadmConfigSpec: bootstrapv1.KubeadmConfigSpec{
   905  					ClusterConfiguration: &bootstrapv1.ClusterConfiguration{},
   906  					InitConfiguration:    &bootstrapv1.InitConfiguration{},
   907  					JoinConfiguration:    &bootstrapv1.JoinConfiguration{},
   908  				},
   909  			},
   910  		}
   911  		m := &clusterv1.Machine{
   912  			TypeMeta: metav1.TypeMeta{
   913  				Kind:       "KubeadmConfig",
   914  				APIVersion: clusterv1.GroupVersion.String(),
   915  			},
   916  			ObjectMeta: metav1.ObjectMeta{
   917  				Namespace: "default",
   918  				Name:      "test",
   919  			},
   920  			Spec: clusterv1.MachineSpec{
   921  				Bootstrap: clusterv1.Bootstrap{
   922  					ConfigRef: &corev1.ObjectReference{
   923  						Kind:       "KubeadmConfig",
   924  						Namespace:  "default",
   925  						Name:       "test",
   926  						APIVersion: bootstrapv1.GroupVersion.String(),
   927  					},
   928  				},
   929  			},
   930  		}
   931  		machineConfigs := map[string]*bootstrapv1.KubeadmConfig{
   932  			m.Name: {
   933  				TypeMeta: metav1.TypeMeta{
   934  					Kind:       "KubeadmConfig",
   935  					APIVersion: bootstrapv1.GroupVersion.String(),
   936  				},
   937  				ObjectMeta: metav1.ObjectMeta{
   938  					Namespace: "default",
   939  					Name:      "test",
   940  				},
   941  				Spec: bootstrapv1.KubeadmConfigSpec{
   942  					JoinConfiguration: &bootstrapv1.JoinConfiguration{},
   943  				},
   944  			},
   945  		}
   946  
   947  		t.Run("by returning true if neither labels or annotations match", func(t *testing.T) {
   948  			g := NewWithT(t)
   949  			machineConfigs[m.Name].Annotations = nil
   950  			machineConfigs[m.Name].Labels = nil
   951  			reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   952  			g.Expect(match).To(BeTrue())
   953  			g.Expect(reason).To(BeEmpty())
   954  		})
   955  
   956  		t.Run("by returning true if only labels don't match", func(t *testing.T) {
   957  			g := NewWithT(t)
   958  			machineConfigs[m.Name].Annotations = kcp.Spec.MachineTemplate.ObjectMeta.Annotations
   959  			machineConfigs[m.Name].Labels = nil
   960  			reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   961  			g.Expect(match).To(BeTrue())
   962  			g.Expect(reason).To(BeEmpty())
   963  		})
   964  
   965  		t.Run("by returning true if only annotations don't match", func(t *testing.T) {
   966  			g := NewWithT(t)
   967  			machineConfigs[m.Name].Annotations = nil
   968  			machineConfigs[m.Name].Labels = kcp.Spec.MachineTemplate.ObjectMeta.Labels
   969  			reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   970  			g.Expect(match).To(BeTrue())
   971  			g.Expect(reason).To(BeEmpty())
   972  		})
   973  
   974  		t.Run("by returning true if both labels and annotations match", func(t *testing.T) {
   975  			g := NewWithT(t)
   976  			machineConfigs[m.Name].Labels = kcp.Spec.MachineTemplate.ObjectMeta.Labels
   977  			machineConfigs[m.Name].Annotations = kcp.Spec.MachineTemplate.ObjectMeta.Annotations
   978  			reason, match := matchesKubeadmBootstrapConfig(machineConfigs, kcp, m)
   979  			g.Expect(match).To(BeTrue())
   980  			g.Expect(reason).To(BeEmpty())
   981  		})
   982  	})
   983  }
   984  
   985  func TestMatchesTemplateClonedFrom(t *testing.T) {
   986  	t.Run("nil machine returns false", func(t *testing.T) {
   987  		g := NewWithT(t)
   988  		reason, match := matchesTemplateClonedFrom(nil, nil, nil)
   989  		g.Expect(match).To(BeFalse())
   990  		g.Expect(reason).To(Equal("Machine cannot be compared with KCP.spec.machineTemplate.infrastructureRef: Machine is nil"))
   991  	})
   992  
   993  	t.Run("returns true if machine not found", func(t *testing.T) {
   994  		g := NewWithT(t)
   995  		kcp := &controlplanev1.KubeadmControlPlane{}
   996  		machine := &clusterv1.Machine{
   997  			Spec: clusterv1.MachineSpec{
   998  				InfrastructureRef: corev1.ObjectReference{
   999  					Kind:       "KubeadmConfig",
  1000  					Namespace:  "default",
  1001  					Name:       "test",
  1002  					APIVersion: bootstrapv1.GroupVersion.String(),
  1003  				},
  1004  			},
  1005  		}
  1006  		reason, match := matchesTemplateClonedFrom(map[string]*unstructured.Unstructured{}, kcp, machine)
  1007  		g.Expect(match).To(BeTrue())
  1008  		g.Expect(reason).To(BeEmpty())
  1009  	})
  1010  
  1011  	t.Run("matches labels or annotations", func(t *testing.T) {
  1012  		kcp := &controlplanev1.KubeadmControlPlane{
  1013  			ObjectMeta: metav1.ObjectMeta{
  1014  				Namespace: "default",
  1015  			},
  1016  			Spec: controlplanev1.KubeadmControlPlaneSpec{
  1017  				MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
  1018  					ObjectMeta: clusterv1.ObjectMeta{
  1019  						Annotations: map[string]string{
  1020  							"test": "annotation",
  1021  						},
  1022  						Labels: map[string]string{
  1023  							"test": "labels",
  1024  						},
  1025  					},
  1026  					InfrastructureRef: corev1.ObjectReference{
  1027  						Kind:       "GenericMachineTemplate",
  1028  						Namespace:  "default",
  1029  						Name:       "infra-foo",
  1030  						APIVersion: "generic.io/v1",
  1031  					},
  1032  				},
  1033  			},
  1034  		}
  1035  		m := &clusterv1.Machine{
  1036  			Spec: clusterv1.MachineSpec{
  1037  				InfrastructureRef: corev1.ObjectReference{
  1038  					Kind:       "GenericMachine",
  1039  					Namespace:  "default",
  1040  					Name:       "infra-foo",
  1041  					APIVersion: "generic.io/v1",
  1042  				},
  1043  			},
  1044  		}
  1045  
  1046  		infraConfigs := map[string]*unstructured.Unstructured{
  1047  			m.Name: {
  1048  				Object: map[string]interface{}{
  1049  					"kind":       "InfrastructureMachine",
  1050  					"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1051  					"metadata": map[string]interface{}{
  1052  						"name":      "infra-config1",
  1053  						"namespace": "default",
  1054  					},
  1055  				},
  1056  			},
  1057  		}
  1058  
  1059  		t.Run("by returning true if neither labels or annotations match", func(t *testing.T) {
  1060  			g := NewWithT(t)
  1061  			infraConfigs[m.Name].SetAnnotations(map[string]string{
  1062  				clusterv1.TemplateClonedFromNameAnnotation:      "infra-foo",
  1063  				clusterv1.TemplateClonedFromGroupKindAnnotation: "GenericMachineTemplate.generic.io",
  1064  			})
  1065  			infraConfigs[m.Name].SetLabels(nil)
  1066  			reason, match := matchesTemplateClonedFrom(infraConfigs, kcp, m)
  1067  			g.Expect(match).To(BeTrue())
  1068  			g.Expect(reason).To(BeEmpty())
  1069  		})
  1070  
  1071  		t.Run("by returning true if only labels don't match", func(t *testing.T) {
  1072  			g := NewWithT(t)
  1073  			infraConfigs[m.Name].SetAnnotations(map[string]string{
  1074  				clusterv1.TemplateClonedFromNameAnnotation:      "infra-foo",
  1075  				clusterv1.TemplateClonedFromGroupKindAnnotation: "GenericMachineTemplate.generic.io",
  1076  				"test": "annotation",
  1077  			})
  1078  			infraConfigs[m.Name].SetLabels(nil)
  1079  			reason, match := matchesTemplateClonedFrom(infraConfigs, kcp, m)
  1080  			g.Expect(match).To(BeTrue())
  1081  			g.Expect(reason).To(BeEmpty())
  1082  		})
  1083  
  1084  		t.Run("by returning true if only annotations don't match", func(t *testing.T) {
  1085  			g := NewWithT(t)
  1086  			infraConfigs[m.Name].SetAnnotations(map[string]string{
  1087  				clusterv1.TemplateClonedFromNameAnnotation:      "infra-foo",
  1088  				clusterv1.TemplateClonedFromGroupKindAnnotation: "GenericMachineTemplate.generic.io",
  1089  			})
  1090  			infraConfigs[m.Name].SetLabels(kcp.Spec.MachineTemplate.ObjectMeta.Labels)
  1091  			reason, match := matchesTemplateClonedFrom(infraConfigs, kcp, m)
  1092  			g.Expect(match).To(BeTrue())
  1093  			g.Expect(reason).To(BeEmpty())
  1094  		})
  1095  
  1096  		t.Run("by returning true if both labels and annotations match", func(t *testing.T) {
  1097  			g := NewWithT(t)
  1098  			infraConfigs[m.Name].SetAnnotations(map[string]string{
  1099  				clusterv1.TemplateClonedFromNameAnnotation:      "infra-foo",
  1100  				clusterv1.TemplateClonedFromGroupKindAnnotation: "GenericMachineTemplate.generic.io",
  1101  				"test": "annotation",
  1102  			})
  1103  			infraConfigs[m.Name].SetLabels(kcp.Spec.MachineTemplate.ObjectMeta.Labels)
  1104  			reason, match := matchesTemplateClonedFrom(infraConfigs, kcp, m)
  1105  			g.Expect(match).To(BeTrue())
  1106  			g.Expect(reason).To(BeEmpty())
  1107  		})
  1108  	})
  1109  }
  1110  
  1111  func TestMatchesTemplateClonedFrom_WithClonedFromAnnotations(t *testing.T) {
  1112  	kcp := &controlplanev1.KubeadmControlPlane{
  1113  		ObjectMeta: metav1.ObjectMeta{
  1114  			Namespace: "default",
  1115  		},
  1116  		Spec: controlplanev1.KubeadmControlPlaneSpec{
  1117  			MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
  1118  				InfrastructureRef: corev1.ObjectReference{
  1119  					Kind:       "GenericMachineTemplate",
  1120  					Namespace:  "default",
  1121  					Name:       "infra-foo",
  1122  					APIVersion: "generic.io/v1",
  1123  				},
  1124  			},
  1125  		},
  1126  	}
  1127  	machine := &clusterv1.Machine{
  1128  		Spec: clusterv1.MachineSpec{
  1129  			InfrastructureRef: corev1.ObjectReference{
  1130  				APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
  1131  				Kind:       "InfrastructureMachine",
  1132  				Name:       "infra-config1",
  1133  				Namespace:  "default",
  1134  			},
  1135  		},
  1136  	}
  1137  	tests := []struct {
  1138  		name         string
  1139  		annotations  map[string]interface{}
  1140  		expectMatch  bool
  1141  		expectReason string
  1142  	}{
  1143  		{
  1144  			name:        "returns true if annotations don't exist",
  1145  			annotations: map[string]interface{}{},
  1146  			expectMatch: true,
  1147  		},
  1148  		{
  1149  			name: "returns false if annotations don't match anything",
  1150  			annotations: map[string]interface{}{
  1151  				clusterv1.TemplateClonedFromNameAnnotation:      "barfoo1",
  1152  				clusterv1.TemplateClonedFromGroupKindAnnotation: "barfoo2",
  1153  			},
  1154  			expectMatch:  false,
  1155  			expectReason: "Infrastructure template on KCP rotated from barfoo2 barfoo1 to GenericMachineTemplate.generic.io infra-foo",
  1156  		},
  1157  		{
  1158  			name: "returns false if TemplateClonedFromNameAnnotation matches but TemplateClonedFromGroupKindAnnotation doesn't",
  1159  			annotations: map[string]interface{}{
  1160  				clusterv1.TemplateClonedFromNameAnnotation:      "infra-foo",
  1161  				clusterv1.TemplateClonedFromGroupKindAnnotation: "barfoo2",
  1162  			},
  1163  			expectMatch:  false,
  1164  			expectReason: "Infrastructure template on KCP rotated from barfoo2 infra-foo to GenericMachineTemplate.generic.io infra-foo",
  1165  		},
  1166  		{
  1167  			name: "returns true if both annotations match",
  1168  			annotations: map[string]interface{}{
  1169  				clusterv1.TemplateClonedFromNameAnnotation:      "infra-foo",
  1170  				clusterv1.TemplateClonedFromGroupKindAnnotation: "GenericMachineTemplate.generic.io",
  1171  			},
  1172  			expectMatch: true,
  1173  		},
  1174  	}
  1175  
  1176  	for _, tt := range tests {
  1177  		t.Run(tt.name, func(t *testing.T) {
  1178  			g := NewWithT(t)
  1179  			infraConfigs := map[string]*unstructured.Unstructured{
  1180  				machine.Name: {
  1181  					Object: map[string]interface{}{
  1182  						"kind":       "InfrastructureMachine",
  1183  						"apiVersion": "infrastructure.cluster.x-k8s.io/v1beta1",
  1184  						"metadata": map[string]interface{}{
  1185  							"name":        "infra-config1",
  1186  							"namespace":   "default",
  1187  							"annotations": tt.annotations,
  1188  						},
  1189  					},
  1190  				},
  1191  			}
  1192  			reason, match := matchesTemplateClonedFrom(infraConfigs, kcp, machine)
  1193  			g.Expect(match).To(Equal(tt.expectMatch))
  1194  			g.Expect(reason).To(Equal(tt.expectReason))
  1195  		})
  1196  	}
  1197  }