Skip to content

Commit 66b51ff

Browse files
committed
Add integration tests for bundle create failure handling
1 parent d622f9d commit 66b51ff

File tree

1 file changed

+309
-0
lines changed

1 file changed

+309
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
package bundle
2+
3+
import (
4+
"time"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
9+
"github.com/rancher/fleet/integrationtests/utils"
10+
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
11+
12+
corev1 "k8s.io/api/core/v1"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/labels"
15+
"k8s.io/apimachinery/pkg/types"
16+
"sigs.k8s.io/controller-runtime/pkg/client"
17+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
18+
)
19+
20+
const (
21+
testFinalizer = "test.fleet.cattle.io/block-deletion"
22+
testFinalizerNS = "test.fleet.cattle.io/block-ns-deletion"
23+
)
24+
25+
var _ = Describe("Bundle controller error handling", Ordered, func() {
26+
var bundleNS string
27+
28+
createNamespace := func(name string) {
29+
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
30+
Expect(k8sClient.Create(ctx, ns)).ToNot(HaveOccurred())
31+
}
32+
33+
createCluster := func(name, namespace, statusNamespace string, labels map[string]string) *v1alpha1.Cluster {
34+
cluster := &v1alpha1.Cluster{
35+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace, Labels: labels},
36+
Spec: v1alpha1.ClusterSpec{Paused: false},
37+
}
38+
Expect(k8sClient.Create(ctx, cluster)).ToNot(HaveOccurred())
39+
40+
Eventually(func(g Gomega) {
41+
g.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, cluster)).To(Succeed())
42+
cluster.Status.Namespace = statusNamespace
43+
g.Expect(k8sClient.Status().Update(ctx, cluster)).To(Succeed())
44+
}).Should(Succeed())
45+
46+
return cluster
47+
}
48+
49+
createBundle := func(name, namespace, defaultNS, configMapName string, targets []v1alpha1.BundleTarget) *v1alpha1.Bundle {
50+
bundle := &v1alpha1.Bundle{
51+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
52+
Spec: v1alpha1.BundleSpec{
53+
BundleDeploymentOptions: v1alpha1.BundleDeploymentOptions{DefaultNamespace: defaultNS},
54+
Targets: targets,
55+
Resources: []v1alpha1.BundleResource{{
56+
Name: "test.yaml",
57+
Content: "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: " + configMapName + "\ndata:\n key: value",
58+
}},
59+
},
60+
}
61+
Expect(k8sClient.Create(ctx, bundle)).ToNot(HaveOccurred())
62+
return bundle
63+
}
64+
65+
getBundleDeployments := func(bundleName, bundleNS string) *v1alpha1.BundleDeploymentList {
66+
bdList := &v1alpha1.BundleDeploymentList{}
67+
Expect(k8sClient.List(ctx, bdList, client.MatchingLabelsSelector{
68+
Selector: labels.SelectorFromSet(map[string]string{
69+
"fleet.cattle.io/bundle-name": bundleName,
70+
"fleet.cattle.io/bundle-namespace": bundleNS,
71+
}),
72+
})).To(Succeed())
73+
return bdList
74+
}
75+
76+
addFinalizer := func(obj client.Object, finalizer string) {
77+
Eventually(func(g Gomega) {
78+
key := client.ObjectKeyFromObject(obj)
79+
g.Expect(k8sClient.Get(ctx, key, obj)).To(Succeed())
80+
if !controllerutil.ContainsFinalizer(obj, finalizer) {
81+
controllerutil.AddFinalizer(obj, finalizer)
82+
g.Expect(k8sClient.Update(ctx, obj)).To(Succeed())
83+
}
84+
}).Should(Succeed())
85+
}
86+
87+
removeFinalizer := func(obj client.Object, finalizer string) {
88+
Eventually(func(g Gomega) {
89+
key := client.ObjectKeyFromObject(obj)
90+
g.Expect(k8sClient.Get(ctx, key, obj)).To(Succeed())
91+
controllerutil.RemoveFinalizer(obj, finalizer)
92+
g.Expect(k8sClient.Update(ctx, obj)).To(Succeed())
93+
}).Should(Succeed())
94+
}
95+
96+
updateBundleDefaultNamespace := func(bundle *v1alpha1.Bundle, newNS string) {
97+
Eventually(func(g Gomega) {
98+
latestBundle := &v1alpha1.Bundle{}
99+
key := client.ObjectKeyFromObject(bundle)
100+
g.Expect(k8sClient.Get(ctx, key, latestBundle)).To(Succeed())
101+
latestBundle.Spec.BundleDeploymentOptions.DefaultNamespace = newNS
102+
g.Expect(k8sClient.Update(ctx, latestBundle)).To(Succeed())
103+
}).Should(Succeed())
104+
}
105+
106+
generateTestID := func() string {
107+
testID, err := utils.NewNamespaceName()
108+
Expect(err).ToNot(HaveOccurred())
109+
if len(testID) > 5 {
110+
testID = testID[5:]
111+
}
112+
return testID
113+
}
114+
115+
setupClusters := func(cluster1Name, cluster2Name, testID string, clusterLabels []map[string]string) (*v1alpha1.Cluster, *v1alpha1.Cluster) {
116+
cluster1NS := "cluster1-ns-" + testID
117+
cluster2NS := "cluster2-ns-" + testID
118+
119+
createNamespace(cluster1NS)
120+
createNamespace(cluster2NS)
121+
122+
cluster1 := createCluster(cluster1Name, bundleNS, cluster1NS, clusterLabels[0])
123+
cluster2 := createCluster(cluster2Name, bundleNS, cluster2NS, clusterLabels[1])
124+
125+
return cluster1, cluster2
126+
}
127+
128+
cleanupResources := func(bundle *v1alpha1.Bundle, bundleName, testID string, resources ...client.Object) {
129+
_ = k8sClient.Delete(ctx, bundle)
130+
Eventually(func() int {
131+
return len(getBundleDeployments(bundleName, bundleNS).Items)
132+
}).Should(Equal(0))
133+
134+
for _, resource := range resources {
135+
_ = k8sClient.Delete(ctx, resource)
136+
}
137+
138+
_ = k8sClient.Delete(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "cluster1-ns-" + testID}})
139+
_ = k8sClient.Delete(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "cluster2-ns-" + testID}})
140+
}
141+
142+
BeforeAll(func() {
143+
var err error
144+
bundleNS, err = utils.NewNamespaceName()
145+
Expect(err).ToNot(HaveOccurred())
146+
createNamespace(bundleNS)
147+
DeferCleanup(func() {
148+
Expect(k8sClient.Delete(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: bundleNS}})).ToNot(HaveOccurred())
149+
})
150+
})
151+
152+
Context("Issue #4144 - UID tracking prevents incorrect deletion", func() {
153+
var (
154+
bundle *v1alpha1.Bundle
155+
cluster1 *v1alpha1.Cluster
156+
cluster2 *v1alpha1.Cluster
157+
content *v1alpha1.Content
158+
bundleName string
159+
testID string
160+
)
161+
162+
BeforeEach(func() {
163+
bundleName = "test-bundle-uid"
164+
testID = generateTestID()
165+
cluster1, cluster2 = setupClusters("cluster1", "cluster2", testID, []map[string]string{
166+
{"env": "test"},
167+
{"env": "test"},
168+
})
169+
170+
content = &v1alpha1.Content{
171+
ObjectMeta: metav1.ObjectMeta{Name: "test-content-" + testID},
172+
Content: []byte("test content"),
173+
}
174+
Expect(k8sClient.Create(ctx, content)).ToNot(HaveOccurred())
175+
176+
bundle = createBundle(bundleName, bundleNS, "default", "test", []v1alpha1.BundleTarget{{
177+
ClusterSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"env": "test"}},
178+
}})
179+
})
180+
181+
AfterEach(func() {
182+
cleanupResources(bundle, bundleName, testID, content, cluster1, cluster2)
183+
})
184+
185+
It("should not delete existing bundledeployments when content resource fails", func() {
186+
var uid1, uid2 types.UID
187+
Eventually(func(g Gomega) {
188+
bdList := getBundleDeployments(bundleName, bundleNS)
189+
g.Expect(bdList.Items).To(HaveLen(2))
190+
uid1 = bdList.Items[0].UID
191+
uid2 = bdList.Items[1].UID
192+
}).Should(Succeed())
193+
194+
addFinalizer(content, testFinalizer)
195+
Expect(k8sClient.Delete(ctx, content)).To(Succeed())
196+
197+
Eventually(func(g Gomega) {
198+
c := &v1alpha1.Content{}
199+
g.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: content.Name}, c)).To(Succeed())
200+
g.Expect(c.DeletionTimestamp).NotTo(BeNil())
201+
}).Should(Succeed())
202+
203+
updateBundleDefaultNamespace(bundle, "kube-system")
204+
205+
Consistently(func(g Gomega) {
206+
bdList := getBundleDeployments(bundleName, bundleNS)
207+
g.Expect(bdList.Items).To(HaveLen(2))
208+
currentUIDs := make(map[types.UID]bool)
209+
for _, bd := range bdList.Items {
210+
currentUIDs[bd.UID] = true
211+
}
212+
g.Expect(currentUIDs).To(HaveKey(uid1))
213+
g.Expect(currentUIDs).To(HaveKey(uid2))
214+
}, 5*time.Second, time.Second).Should(Succeed())
215+
216+
removeFinalizer(content, testFinalizer)
217+
218+
Eventually(func(g Gomega) {
219+
err := k8sClient.Get(ctx, types.NamespacedName{Name: content.Name}, &v1alpha1.Content{})
220+
g.Expect(err).To(HaveOccurred())
221+
}).Should(Succeed())
222+
})
223+
})
224+
225+
Context("Issue #4028 - Continue processing all bundledeployments on error", func() {
226+
var (
227+
bundle *v1alpha1.Bundle
228+
cluster1 *v1alpha1.Cluster
229+
cluster2 *v1alpha1.Cluster
230+
bundleName string
231+
testID string
232+
cluster1NS string
233+
cluster2NS string
234+
)
235+
236+
BeforeEach(func() {
237+
bundleName = "test-bundle-continue"
238+
testID = generateTestID()
239+
cluster1, cluster2 = setupClusters("cluster1-continue", "cluster2-continue", testID, []map[string]string{
240+
{"env": "test", "order": "1"},
241+
{"env": "test", "order": "2"},
242+
})
243+
cluster1NS = cluster1.Status.Namespace
244+
cluster2NS = cluster2.Status.Namespace
245+
246+
bundle = createBundle(bundleName, bundleNS, "default", "test-continue", []v1alpha1.BundleTarget{{
247+
ClusterSelector: &metav1.LabelSelector{
248+
MatchLabels: map[string]string{"env": "test"},
249+
MatchExpressions: []metav1.LabelSelectorRequirement{{
250+
Key: "order",
251+
Operator: metav1.LabelSelectorOpIn,
252+
Values: []string{"1", "2"},
253+
}},
254+
},
255+
}})
256+
})
257+
258+
AfterEach(func() {
259+
cleanupResources(bundle, bundleName, testID, cluster1, cluster2)
260+
})
261+
262+
It("should continue processing second bundledeployment when first fails", func() {
263+
var bd1UID, bd2UID types.UID
264+
Eventually(func(g Gomega) {
265+
bdList := getBundleDeployments(bundleName, bundleNS)
266+
g.Expect(bdList.Items).To(HaveLen(2))
267+
for _, bd := range bdList.Items {
268+
switch bd.Namespace {
269+
case cluster1NS:
270+
bd1UID = bd.UID
271+
case cluster2NS:
272+
bd2UID = bd.UID
273+
}
274+
}
275+
g.Expect(bd1UID).NotTo(BeEmpty())
276+
g.Expect(bd2UID).NotTo(BeEmpty())
277+
}).Should(Succeed())
278+
279+
cluster1Namespace := &corev1.Namespace{}
280+
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: cluster1NS}, cluster1Namespace)).To(Succeed())
281+
addFinalizer(cluster1Namespace, testFinalizerNS)
282+
Expect(k8sClient.Delete(ctx, cluster1Namespace)).To(Succeed())
283+
284+
Eventually(func(g Gomega) {
285+
g.Expect(k8sClient.Get(ctx, types.NamespacedName{Name: cluster1NS}, cluster1Namespace)).To(Succeed())
286+
g.Expect(cluster1Namespace.DeletionTimestamp).NotTo(BeNil())
287+
}).Should(Succeed())
288+
289+
updateBundleDefaultNamespace(bundle, "kube-system")
290+
291+
Eventually(func(g Gomega) {
292+
bdList := &v1alpha1.BundleDeploymentList{}
293+
g.Expect(k8sClient.List(ctx, bdList, client.InNamespace(cluster2NS))).To(Succeed())
294+
295+
var bd2 *v1alpha1.BundleDeployment
296+
for _, bd := range bdList.Items {
297+
if bd.Labels["fleet.cattle.io/bundle-name"] == bundleName {
298+
bd2 = &bd
299+
break
300+
}
301+
}
302+
g.Expect(bd2).NotTo(BeNil())
303+
g.Expect(bd2.Spec.Options.DefaultNamespace).To(Equal("kube-system"))
304+
}).Should(Succeed())
305+
306+
removeFinalizer(cluster1Namespace, testFinalizerNS)
307+
})
308+
})
309+
})

0 commit comments

Comments
 (0)