@@ -10,6 +10,7 @@ import (
1010
1111 . "github.com/onsi/gomega"
1212 corev1 "k8s.io/api/core/v1"
13+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314 "k8s.io/apiserver/pkg/storage/names"
1415 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1516 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -21,7 +22,7 @@ func TestReconcileExistingNamespaceWithUpdatedLabels(t *testing.T) {
2122 g := NewWithT (t )
2223 timeout := 5 * time .Second
2324
24- sourceClusterClassName , cleanup , err := createUniqueClusterClassAndTemplates (
25+ sourceClusterClassName , _ , cleanup , err := createUniqueClusterClassAndTemplates (
2526 sourceClusterClassNamespace ,
2627 )
2728 g .Expect (err ).ToNot (HaveOccurred ())
@@ -53,7 +54,7 @@ func TestReconcileNewNamespaces(t *testing.T) {
5354 g := NewWithT (t )
5455 timeout := 5 * time .Second
5556
56- sourceClusterClassName , cleanup , err := createUniqueClusterClassAndTemplates (
57+ sourceClusterClassName , _ , cleanup , err := createUniqueClusterClassAndTemplates (
5758 sourceClusterClassNamespace ,
5859 )
5960 g .Expect (err ).ToNot (HaveOccurred ())
@@ -84,7 +85,7 @@ func TestReconcileNewClusterClass(t *testing.T) {
8485 targetNamespaces , err := createTargetNamespaces (3 )
8586 g .Expect (err ).ToNot (HaveOccurred ())
8687
87- sourceClusterClassName , cleanup , err := createUniqueClusterClassAndTemplates (
88+ sourceClusterClassName , _ , cleanup , err := createUniqueClusterClassAndTemplates (
8889 sourceClusterClassNamespace ,
8990 )
9091 g .Expect (err ).ToNot (HaveOccurred ())
@@ -108,7 +109,7 @@ func TestReconcileNewClusterClass(t *testing.T) {
108109func TestSourceClusterClassNamespaceEmpty (t * testing.T ) {
109110 g := NewWithT (t )
110111
111- _ , cleanup , err := createUniqueClusterClassAndTemplates (
112+ _ , _ , cleanup , err := createUniqueClusterClassAndTemplates (
112113 sourceClusterClassNamespace ,
113114 )
114115 g .Expect (err ).ToNot (HaveOccurred ())
@@ -128,6 +129,60 @@ func TestSourceClusterClassNamespaceEmpty(t *testing.T) {
128129 g .Expect (ns ).To (BeEmpty ())
129130}
130131
132+ func TestReconcileAfterPartialFailureToCopy (t * testing.T ) {
133+ g := NewWithT (t )
134+ timeout := 5 * time .Second
135+ prefix := "partial-failure"
136+
137+ sourceClusterClassName , sourceTemplates , sourceCleanup , err := createClusterClassAndTemplates (
138+ prefix ,
139+ sourceClusterClassNamespace ,
140+ )
141+ g .Expect (err ).ToNot (HaveOccurred ())
142+ defer func () {
143+ g .Expect (sourceCleanup ()).To (Succeed ())
144+ }()
145+
146+ // Create a namespace with the same ClusterClass and templates.
147+ targetNamespace , err := env .CreateNamespace (ctx , "target" , map [string ]string {})
148+ g .Expect (err ).ToNot (HaveOccurred ())
149+ targetClusterClassName , targetTemplates , _ , err := createClusterClassAndTemplates (
150+ prefix ,
151+ targetNamespace .Name ,
152+ )
153+ g .Expect (err ).ToNot (HaveOccurred ())
154+
155+ // Verify that the target ClusterClass and templates match the source.
156+ g .Expect (targetClusterClassName ).To (Equal (sourceClusterClassName ))
157+ for i := range targetTemplates {
158+ g .Expect (targetTemplates [i ].GetName ()).To (Equal (sourceTemplates [i ].GetName ()))
159+ }
160+
161+ // Simulate a partial copy failure by removing the ClusterClass--the last object copied--from the target namespace.
162+ g .Expect (env .CleanupAndWait (ctx , & clusterv1.ClusterClass {
163+ ObjectMeta : metav1.ObjectMeta {
164+ Name : targetClusterClassName ,
165+ Namespace : targetNamespace .Name ,
166+ },
167+ })).To (Succeed ())
168+
169+ // Label the namespace so that it is considered a target namespace to trigger the reconcile loop.
170+ targetNamespace .Labels [targetNamespaceLabelKey ] = ""
171+ err = env .Update (ctx , targetNamespace )
172+ g .Expect (err ).ToNot (HaveOccurred ())
173+
174+ // Verify that the templates are copied.
175+ g .Eventually (func () error {
176+ return verifyClusterClassAndTemplates (
177+ env .Client ,
178+ sourceClusterClassName ,
179+ targetNamespace .Name ,
180+ )
181+ },
182+ timeout ,
183+ ).Should (Succeed ())
184+ }
185+
131186func verifyClusterClassAndTemplates (
132187 cli client.Reader ,
133188 name ,
@@ -151,6 +206,7 @@ func verifyClusterClassAndTemplates(
151206
152207func createUniqueClusterClassAndTemplates (namespace string ) (
153208 clusterClassName string ,
209+ templates []client.Object ,
154210 cleanup func () error ,
155211 err error ,
156212) {
@@ -165,6 +221,7 @@ func createClusterClassAndTemplates(
165221 namespace string ,
166222) (
167223 clusterClassName string ,
224+ templates []client.Object ,
168225 cleanup func () error ,
169226 err error ,
170227) {
@@ -206,7 +263,7 @@ func createClusterClassAndTemplates(
206263
207264 // Create the set of initObjects from the objects above to add to the API server when the test environment starts.
208265
209- templates : = []client.Object {
266+ templates = []client.Object {
210267 bootstrapTemplate ,
211268 infraMachineTemplateWorker ,
212269 infraMachineTemplateControlPlane ,
@@ -216,11 +273,11 @@ func createClusterClassAndTemplates(
216273
217274 for _ , obj := range templates {
218275 if err := env .CreateAndWait (ctx , obj ); err != nil {
219- return "" , nil , err
276+ return "" , nil , nil , err
220277 }
221278 }
222279 if err := env .CreateAndWait (ctx , clusterClass ); err != nil {
223- return "" , nil , err
280+ return "" , nil , nil , err
224281 }
225282
226283 cleanup = func () error {
@@ -232,7 +289,7 @@ func createClusterClassAndTemplates(
232289 return env .CleanupAndWait (ctx , clusterClass )
233290 }
234291
235- return clusterClass .Name , cleanup , nil
292+ return clusterClass .Name , templates , cleanup , nil
236293}
237294
238295func createTargetNamespaces (number int ) ([]* corev1.Namespace , error ) {
0 commit comments