Skip to content

Commit 6235745

Browse files
authored
Merge pull request #774 from fluxcd/fix-773
Fix ImagePolicy reconciler getting triggered when ImageRepository is not ready
2 parents b966bd7 + 8dcbf4e commit 6235745

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

internal/controller/imagepolicy_controller.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"sigs.k8s.io/controller-runtime/pkg/client"
3737
"sigs.k8s.io/controller-runtime/pkg/controller"
3838
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
39+
"sigs.k8s.io/controller-runtime/pkg/event"
3940
"sigs.k8s.io/controller-runtime/pkg/handler"
4041
"sigs.k8s.io/controller-runtime/pkg/predicate"
4142
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -143,13 +144,37 @@ func (r *ImagePolicyReconciler) SetupWithManager(mgr ctrl.Manager, opts ImagePol
143144
Watches(
144145
&imagev1.ImageRepository{},
145146
handler.EnqueueRequestsFromMapFunc(r.imagePoliciesForRepository),
147+
builder.WithPredicates(imageRepositoryPredicate{}),
146148
).
147149
WithOptions(controller.Options{
148150
RateLimiter: opts.RateLimiter,
149151
}).
150152
Complete(r)
151153
}
152154

155+
// imageRepositoryPredicate is used for watching changes to
156+
// ImageRepository objects that are referenced by ImagePolicy
157+
// objects.
158+
type imageRepositoryPredicate struct {
159+
predicate.Funcs
160+
}
161+
162+
func (imageRepositoryPredicate) Update(e event.UpdateEvent) bool {
163+
if e.ObjectOld == nil || e.ObjectNew == nil {
164+
return false
165+
}
166+
167+
// This is a temporary workaround to avoid reconciling ImagePolicy
168+
// when the ImageRepository is not ready. In the near future, we
169+
// will implement a digest for the scanned tags in the ImageRepository
170+
// and will only return true here if the digest has changed, which
171+
// covers not only skipping the reconciliation when the ImageRepository
172+
// is not ready, but also when the tags have not changed.
173+
repo := e.ObjectNew.(*imagev1.ImageRepository)
174+
return conditions.IsReady(repo) &&
175+
conditions.GetObservedGeneration(repo, meta.ReadyCondition) == repo.Generation
176+
}
177+
153178
func (r *ImagePolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) {
154179
start := time.Now()
155180

internal/controller/imagepolicy_controller_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,90 @@ func TestImagePolicyReconciler_imageRepoNotReady(t *testing.T) {
101101
}).Should(BeTrue())
102102
}
103103

104+
func TestImagePolicyReconciler_ignoresImageRepoNotReadyEvent(t *testing.T) {
105+
g := NewWithT(t)
106+
107+
namespaceName := "imagepolicy-" + randStringRunes(5)
108+
namespace := &corev1.Namespace{
109+
ObjectMeta: metav1.ObjectMeta{Name: namespaceName},
110+
}
111+
g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred())
112+
t.Cleanup(func() {
113+
g.Expect(k8sClient.Delete(ctx, namespace)).NotTo(HaveOccurred())
114+
})
115+
116+
imageRepo := &imagev1.ImageRepository{
117+
ObjectMeta: metav1.ObjectMeta{
118+
Namespace: namespaceName,
119+
Name: "repo",
120+
},
121+
Spec: imagev1.ImageRepositorySpec{
122+
Image: "ghcr.io/stefanprodan/podinfo",
123+
},
124+
}
125+
g.Expect(k8sClient.Create(ctx, imageRepo)).NotTo(HaveOccurred())
126+
t.Cleanup(func() {
127+
g.Expect(k8sClient.Delete(ctx, imageRepo)).NotTo(HaveOccurred())
128+
})
129+
130+
g.Eventually(func() bool {
131+
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imageRepo), imageRepo)
132+
return err == nil && conditions.IsReady(imageRepo)
133+
}, timeout).Should(BeTrue())
134+
135+
imagePolicy := &imagev1.ImagePolicy{
136+
ObjectMeta: metav1.ObjectMeta{
137+
Namespace: namespaceName,
138+
Name: "test-imagepolicy",
139+
},
140+
Spec: imagev1.ImagePolicySpec{
141+
ImageRepositoryRef: meta.NamespacedObjectReference{
142+
Name: imageRepo.Name,
143+
},
144+
Policy: imagev1.ImagePolicyChoice{
145+
Alphabetical: &imagev1.AlphabeticalPolicy{},
146+
},
147+
},
148+
}
149+
g.Expect(k8sClient.Create(ctx, imagePolicy)).NotTo(HaveOccurred())
150+
t.Cleanup(func() {
151+
g.Expect(k8sClient.Delete(ctx, imagePolicy)).NotTo(HaveOccurred())
152+
})
153+
154+
g.Eventually(func() bool {
155+
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imagePolicy), imagePolicy)
156+
return err == nil && conditions.IsReady(imagePolicy)
157+
}).Should(BeTrue())
158+
159+
// Now cause the ImageRepository to become not ready.
160+
g.Eventually(func() bool {
161+
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imageRepo), imageRepo)
162+
if err != nil {
163+
return false
164+
}
165+
p := patch.NewSerialPatcher(imageRepo, k8sClient)
166+
imageRepo.Spec.Image = "ghcr.io/stefanprodan/podinfo/foo:bar:zzz:qqq/aaa"
167+
return p.Patch(ctx, imageRepo) == nil
168+
}).Should(BeTrue())
169+
170+
// Wait for the ImageRepository to become not ready.
171+
g.Eventually(func() bool {
172+
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imageRepo), imageRepo)
173+
return err == nil && conditions.IsStalled(imageRepo)
174+
}).Should(BeTrue())
175+
176+
// Check that the ImagePolicy is still ready and does not get updated.
177+
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imagePolicy), imagePolicy)
178+
g.Expect(err).NotTo(HaveOccurred())
179+
g.Expect(conditions.IsReady(imagePolicy)).To(BeTrue())
180+
181+
// Wait a bit and check that the ImagePolicy remains ready.
182+
time.Sleep(time.Second)
183+
err = k8sClient.Get(ctx, client.ObjectKeyFromObject(imagePolicy), imagePolicy)
184+
g.Expect(err).NotTo(HaveOccurred())
185+
g.Expect(conditions.IsReady(imagePolicy)).To(BeTrue())
186+
}
187+
104188
func TestImagePolicyReconciler_invalidImage(t *testing.T) {
105189
g := NewWithT(t)
106190

0 commit comments

Comments
 (0)