Skip to content

Commit 6b66d1c

Browse files
committed
Add e2e test
1 parent c237f47 commit 6b66d1c

1 file changed

Lines changed: 327 additions & 0 deletions

File tree

test/e2e/func_middleware_update_test.go

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import (
3030
. "github.com/onsi/ginkgo/v2"
3131
. "github.com/onsi/gomega"
3232
"gopkg.in/yaml.v3"
33+
v1 "k8s.io/api/core/v1"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
"k8s.io/apimachinery/pkg/types"
36+
"k8s.io/utils/ptr"
3437
funcfn "knative.dev/func/pkg/functions"
3538
)
3639

@@ -241,4 +244,328 @@ var _ = Describe("Middleware Update", func() {
241244
Eventually(functionMiddlewareUpToDate(functionName, functionNamespace), 2*time.Minute).Should(Succeed())
242245
})
243246
})
247+
248+
Context("when ConfigMap autoUpdateMiddleware setting changes", func() {
249+
Skip("Skip for now, as the old used CLI for this test (1.20.1), does not have " +
250+
"https://github.com/knative/func/pull/3490 yet")
251+
252+
const (
253+
operatorNamespace = "func-operator-system"
254+
controllerConfigName = "func-operator-controller-config"
255+
)
256+
257+
var repoURL string
258+
var repoDir string
259+
var functionName, functionNamespace string
260+
var originalConfigMapData map[string]string
261+
262+
BeforeEach(func() {
263+
var err error
264+
265+
// Save original ConfigMap data to restore later
266+
cm := &v1.ConfigMap{}
267+
err = k8sClient.Get(ctx, types.NamespacedName{
268+
Name: controllerConfigName,
269+
Namespace: operatorNamespace,
270+
}, cm)
271+
Expect(err).NotTo(HaveOccurred())
272+
originalConfigMapData = make(map[string]string)
273+
for k, v := range cm.Data {
274+
originalConfigMapData[k] = v
275+
}
276+
277+
// Restore original ConfigMap data on cleanup
278+
DeferCleanup(func() {
279+
By("Restoring original ConfigMap data")
280+
cm := &v1.ConfigMap{}
281+
err := k8sClient.Get(ctx, types.NamespacedName{
282+
Name: controllerConfigName,
283+
Namespace: operatorNamespace,
284+
}, cm)
285+
Expect(err).NotTo(HaveOccurred())
286+
287+
cm.Data = originalConfigMapData
288+
err = k8sClient.Update(ctx, cm)
289+
Expect(err).NotTo(HaveOccurred())
290+
})
291+
292+
// Create repository provider resources with automatic cleanup
293+
username, password, _, cleanup, err := repoProvider.CreateRandomUser()
294+
Expect(err).NotTo(HaveOccurred())
295+
DeferCleanup(cleanup)
296+
297+
_, repoURL, cleanup, err = repoProvider.CreateRandomRepo(username, false)
298+
Expect(err).NotTo(HaveOccurred())
299+
DeferCleanup(cleanup)
300+
301+
functionNamespace, err = utils.GetTestNamespace()
302+
Expect(err).NotTo(HaveOccurred())
303+
DeferCleanup(cleanupNamespaces, functionNamespace)
304+
305+
// Initialize repository with function code using OLD func CLI version
306+
// to ensure middleware will be outdated
307+
oldFuncVersion := "v1.20.1"
308+
repoDir, err = utils.InitializeRepoWithFunction(
309+
repoURL,
310+
username,
311+
password,
312+
"go",
313+
utils.WithCliVersion(oldFuncVersion))
314+
Expect(err).NotTo(HaveOccurred())
315+
DeferCleanup(os.RemoveAll, repoDir)
316+
317+
// Deploy function using the same OLD func CLI version
318+
out, err := utils.RunFuncDeploy(repoDir,
319+
utils.WithNamespace(functionNamespace),
320+
utils.WithDeployCliVersion(oldFuncVersion))
321+
Expect(err).NotTo(HaveOccurred())
322+
_, _ = fmt.Fprint(GinkgoWriter, out)
323+
324+
// Cleanup func deployment
325+
DeferCleanup(func() {
326+
_, _ = utils.RunFunc("delete", "--path", repoDir, "--namespace", functionNamespace)
327+
})
328+
329+
// Commit func.yaml changes
330+
err = utils.CommitAndPush(repoDir, "Update func.yaml after deploy", "func.yaml")
331+
Expect(err).NotTo(HaveOccurred())
332+
})
333+
334+
AfterEach(func() {
335+
logFailedTestDetails(functionName, functionNamespace)
336+
337+
// Cleanup function resource
338+
if functionName != "" {
339+
cmd := exec.Command("kubectl", "delete", "function", functionName, "-n", functionNamespace, "--ignore-not-found")
340+
_, err := utils.Run(cmd)
341+
Expect(err).NotTo(HaveOccurred())
342+
}
343+
})
344+
345+
It("should reconcile functions without explicit autoUpdateMiddleware when ConfigMap changes", func() {
346+
// Set ConfigMap to disable middleware updates
347+
By("Disabling middleware updates in ConfigMap")
348+
cm := &v1.ConfigMap{}
349+
err := k8sClient.Get(ctx, types.NamespacedName{
350+
Name: controllerConfigName,
351+
Namespace: operatorNamespace,
352+
}, cm)
353+
Expect(err).NotTo(HaveOccurred())
354+
355+
cm.Data["autoUpdateMiddleware"] = "false"
356+
err = k8sClient.Update(ctx, cm)
357+
Expect(err).NotTo(HaveOccurred())
358+
359+
// Wait for the ConfigMap update to propagate
360+
time.Sleep(2 * time.Second)
361+
362+
// Create a Function resource WITHOUT autoUpdateMiddleware setting
363+
// (so it will use the operator default)
364+
fn := &functionsdevv1alpha1.Function{
365+
ObjectMeta: metav1.ObjectMeta{
366+
GenerateName: "my-function-",
367+
Namespace: functionNamespace,
368+
},
369+
Spec: functionsdevv1alpha1.FunctionSpec{
370+
Repository: functionsdevv1alpha1.FunctionSpecRepository{
371+
URL: repoURL,
372+
},
373+
// NOTE: autoUpdateMiddleware is intentionally not set,
374+
// so it will use the operator default (currently "false")
375+
},
376+
}
377+
378+
err = k8sClient.Create(ctx, fn)
379+
Expect(err).NotTo(HaveOccurred())
380+
functionName = fn.Name
381+
382+
By("Waiting for Function to become ready with middleware updates disabled")
383+
Eventually(functionBecomesReady(functionName, functionNamespace)).Should(Succeed())
384+
385+
// Verify the MiddlewareUpToDate condition reflects that updates are disabled
386+
By("Verifying middleware update is skipped due to ConfigMap setting")
387+
Eventually(func(g Gomega) {
388+
fn := &functionsdevv1alpha1.Function{}
389+
err := k8sClient.Get(ctx, types.NamespacedName{
390+
Name: functionName,
391+
Namespace: functionNamespace,
392+
}, fn)
393+
g.Expect(err).NotTo(HaveOccurred())
394+
395+
// Find the MiddlewareUpToDate condition
396+
for _, cond := range fn.Status.Conditions {
397+
if cond.Type == functionsdevv1alpha1.TypeMiddlewareUpToDate {
398+
// When middleware updates are disabled, the condition should be True
399+
// with reason "SkipMiddlewareUpdate"
400+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
401+
g.Expect(cond.Reason).To(Equal("SkipMiddlewareUpdate"))
402+
g.Expect(cond.Message).To(ContainSubstring("operator"))
403+
return
404+
}
405+
}
406+
g.Expect(false).To(BeTrue(), "MiddlewareUpToDate condition not found")
407+
}, 1*time.Minute).Should(Succeed())
408+
409+
// Get the initial image to compare later
410+
fnBefore := &functionsdevv1alpha1.Function{}
411+
err = k8sClient.Get(ctx, types.NamespacedName{
412+
Name: functionName,
413+
Namespace: functionNamespace,
414+
}, fnBefore)
415+
Expect(err).NotTo(HaveOccurred())
416+
imageBefore := fnBefore.Status.Deployment.Image
417+
418+
// Now enable middleware updates in the ConfigMap
419+
By("Enabling middleware updates in ConfigMap")
420+
err = k8sClient.Get(ctx, types.NamespacedName{
421+
Name: controllerConfigName,
422+
Namespace: operatorNamespace,
423+
}, cm)
424+
Expect(err).NotTo(HaveOccurred())
425+
426+
cm.Data["autoUpdateMiddleware"] = "true"
427+
err = k8sClient.Update(ctx, cm)
428+
Expect(err).NotTo(HaveOccurred())
429+
430+
// The Function should be automatically reconciled and middleware should be updated
431+
By("Waiting for Function to be reconciled and middleware updated after ConfigMap change")
432+
Eventually(func(g Gomega) {
433+
fn := &functionsdevv1alpha1.Function{}
434+
err := k8sClient.Get(ctx, types.NamespacedName{
435+
Name: functionName,
436+
Namespace: functionNamespace,
437+
}, fn)
438+
g.Expect(err).NotTo(HaveOccurred())
439+
440+
// The image should have changed (middleware was updated)
441+
g.Expect(fn.Status.Deployment.Image).NotTo(BeEmpty())
442+
g.Expect(fn.Status.Deployment.Image).NotTo(Equal(imageBefore),
443+
"Image should have changed after middleware update")
444+
445+
// The MiddlewareUpToDate condition should now indicate middleware is up to date
446+
for _, cond := range fn.Status.Conditions {
447+
if cond.Type == functionsdevv1alpha1.TypeMiddlewareUpToDate {
448+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
449+
g.Expect(cond.Reason).To(Equal("MiddlewareUpToDate"))
450+
return
451+
}
452+
}
453+
g.Expect(false).To(BeTrue(), "MiddlewareUpToDate condition not found")
454+
}, 5*time.Minute).Should(Succeed())
455+
456+
Eventually(functionBecomesReady(functionName, functionNamespace)).Should(Succeed())
457+
})
458+
459+
It("should not reconcile functions with explicit autoUpdateMiddleware when ConfigMap changes", func() {
460+
// Set ConfigMap to enable middleware updates
461+
By("Setting ConfigMap to enable middleware updates")
462+
cm := &v1.ConfigMap{}
463+
err := k8sClient.Get(ctx, types.NamespacedName{
464+
Name: controllerConfigName,
465+
Namespace: operatorNamespace,
466+
}, cm)
467+
Expect(err).NotTo(HaveOccurred())
468+
469+
cm.Data["autoUpdateMiddleware"] = "true"
470+
err = k8sClient.Update(ctx, cm)
471+
Expect(err).NotTo(HaveOccurred())
472+
473+
time.Sleep(2 * time.Second)
474+
475+
// Create a Function resource WITH explicit autoUpdateMiddleware=false
476+
// This should NOT be affected by ConfigMap changes
477+
fn := &functionsdevv1alpha1.Function{
478+
ObjectMeta: metav1.ObjectMeta{
479+
GenerateName: "my-function-explicit-",
480+
Namespace: functionNamespace,
481+
},
482+
Spec: functionsdevv1alpha1.FunctionSpec{
483+
Repository: functionsdevv1alpha1.FunctionSpecRepository{
484+
URL: repoURL,
485+
},
486+
// Explicitly set autoUpdateMiddleware
487+
AutoUpdateMiddleware: ptr.To(false),
488+
},
489+
}
490+
491+
err = k8sClient.Create(ctx, fn)
492+
Expect(err).NotTo(HaveOccurred())
493+
functionName = fn.Name
494+
495+
By("Waiting for Function to become ready")
496+
Eventually(functionBecomesReady(functionName, functionNamespace)).Should(Succeed())
497+
498+
// Verify the MiddlewareUpToDate condition shows updates are skipped
499+
By("Verifying middleware update is skipped due to function setting")
500+
Eventually(func(g Gomega) {
501+
fn := &functionsdevv1alpha1.Function{}
502+
err := k8sClient.Get(ctx, types.NamespacedName{
503+
Name: functionName,
504+
Namespace: functionNamespace,
505+
}, fn)
506+
g.Expect(err).NotTo(HaveOccurred())
507+
508+
for _, cond := range fn.Status.Conditions {
509+
if cond.Type == functionsdevv1alpha1.TypeMiddlewareUpToDate {
510+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
511+
g.Expect(cond.Reason).To(Equal("SkipMiddlewareUpdate"))
512+
// Message should indicate the source is the function spec
513+
g.Expect(cond.Message).To(ContainSubstring("function"))
514+
return
515+
}
516+
}
517+
g.Expect(false).To(BeTrue(), "MiddlewareUpToDate condition not found")
518+
}, 1*time.Minute).Should(Succeed())
519+
520+
// Get the current image
521+
fnBefore := &functionsdevv1alpha1.Function{}
522+
err = k8sClient.Get(ctx, types.NamespacedName{
523+
Name: functionName,
524+
Namespace: functionNamespace,
525+
}, fnBefore)
526+
Expect(err).NotTo(HaveOccurred())
527+
imageBefore := fnBefore.Status.Deployment.Image
528+
529+
// Update ConfigMap to disable middleware updates
530+
// (opposite of the function's explicit setting, but should have no effect)
531+
By("Changing ConfigMap to disable middleware updates")
532+
err = k8sClient.Get(ctx, types.NamespacedName{
533+
Name: controllerConfigName,
534+
Namespace: operatorNamespace,
535+
}, cm)
536+
Expect(err).NotTo(HaveOccurred())
537+
538+
cm.Data["autoUpdateMiddleware"] = "false"
539+
err = k8sClient.Update(ctx, cm)
540+
Expect(err).NotTo(HaveOccurred())
541+
542+
// Wait a bit to ensure controller has time to process ConfigMap change
543+
time.Sleep(10 * time.Second)
544+
545+
// The Function should NOT be reconciled and image should not change
546+
By("Verifying Function was not reconciled after ConfigMap change")
547+
Consistently(func(g Gomega) {
548+
fn := &functionsdevv1alpha1.Function{}
549+
err := k8sClient.Get(ctx, types.NamespacedName{
550+
Name: functionName,
551+
Namespace: functionNamespace,
552+
}, fn)
553+
g.Expect(err).NotTo(HaveOccurred())
554+
555+
// Image should not change
556+
g.Expect(fn.Status.Deployment.Image).To(Equal(imageBefore))
557+
558+
// Condition should still show updates are skipped with "function" source
559+
for _, cond := range fn.Status.Conditions {
560+
if cond.Type == functionsdevv1alpha1.TypeMiddlewareUpToDate {
561+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
562+
g.Expect(cond.Reason).To(Equal("SkipMiddlewareUpdate"))
563+
g.Expect(cond.Message).To(ContainSubstring("function"))
564+
return
565+
}
566+
}
567+
g.Expect(false).To(BeTrue(), "MiddlewareUpToDate condition not found")
568+
}, 30*time.Second, 5*time.Second).Should(Succeed())
569+
})
570+
})
244571
})

0 commit comments

Comments
 (0)