Skip to content

Commit 0c2d58b

Browse files
committed
Retry policy resolution when no tags found in database
Signed-off-by: Stefan Prodan <[email protected]>
1 parent 8d34196 commit 0c2d58b

File tree

4 files changed

+67
-33
lines changed

4 files changed

+67
-33
lines changed

internal/controller/imagepolicy_controller.go

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import (
2828
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2929
"k8s.io/apimachinery/pkg/types"
3030
kerrors "k8s.io/apimachinery/pkg/util/errors"
31+
"k8s.io/apimachinery/pkg/util/wait"
3132
kuberecorder "k8s.io/client-go/tools/record"
33+
"k8s.io/client-go/util/retry"
3234
"k8s.io/client-go/util/workqueue"
3335
ctrl "sigs.k8s.io/controller-runtime"
3436
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -105,11 +107,12 @@ type ImagePolicyReconciler struct {
105107
kuberecorder.EventRecorder
106108
helper.Metrics
107109

108-
ControllerName string
109-
Database DatabaseReader
110-
ACLOptions acl.Options
111-
AuthOptionsGetter *registry.AuthOptionsGetter
112-
TokenCache *cache.TokenCache
110+
ControllerName string
111+
Database DatabaseReader
112+
ACLOptions acl.Options
113+
AuthOptionsGetter *registry.AuthOptionsGetter
114+
TokenCache *cache.TokenCache
115+
DependencyRequeueInterval time.Duration
113116

114117
patchOptions []patch.Option
115118
}
@@ -361,10 +364,12 @@ func (r *ImagePolicyReconciler) reconcile(ctx context.Context, sp *patch.SerialP
361364
return
362365
}
363366

364-
// If there's no tag in the database, mark not ready.
365-
if err == errNoTagsInDatabase {
366-
conditions.MarkFalse(obj, meta.ReadyCondition, imagev1.DependencyNotReadyReason, "%s", err)
367-
result, retErr = ctrl.Result{}, nil
367+
// If there's no tag in the database, mark not ready and
368+
// requeue according to --requeue-dependency flag.
369+
if errors.Is(err, errNoTagsInDatabase) {
370+
depsErr := fmt.Errorf("retrying in %s error: %w", r.DependencyRequeueInterval.Round(time.Second), err)
371+
conditions.MarkFalse(obj, meta.ReadyCondition, imagev1.DependencyNotReadyReason, "%s", depsErr)
372+
result, retErr = ctrl.Result{RequeueAfter: r.DependencyRequeueInterval}, nil
368373
return
369374
}
370375

@@ -510,15 +515,10 @@ func (r *ImagePolicyReconciler) applyPolicy(obj *imagev1.ImagePolicy, repo *imag
510515
return "", errInvalidPolicy{err: fmt.Errorf("invalid policy: %w", err)}
511516
}
512517

513-
// Read tags from database, apply and filter is configured and compute the
514-
// result.
515-
tags, err := r.Database.Tags(repo.Status.CanonicalImageName)
518+
// Read tags from database with a maximum of 3 retries.
519+
tags, err := r.listTagsWithBackoff(repo.Status.CanonicalImageName)
516520
if err != nil {
517-
return "", fmt.Errorf("failed to read tags from database: %w", err)
518-
}
519-
520-
if len(tags) == 0 {
521-
return "", errNoTagsInDatabase
521+
return "", err
522522
}
523523

524524
// Apply tag filter.
@@ -566,3 +566,32 @@ func (r *ImagePolicyReconciler) imagePoliciesForRepository(ctx context.Context,
566566
}
567567
return reqs
568568
}
569+
570+
// listTagsWithBackoff lists the tags of the given image from the
571+
// internal database with retries if there are no tags in the database.
572+
func (r *ImagePolicyReconciler) listTagsWithBackoff(canonicalImageName string) ([]string, error) {
573+
var backoff = wait.Backoff{
574+
Steps: 4,
575+
Duration: 1 * time.Second,
576+
Factor: 1.5,
577+
Jitter: 0.1,
578+
}
579+
580+
var tags []string
581+
582+
err := retry.OnError(backoff, func(err error) bool {
583+
return errors.Is(err, errNoTagsInDatabase)
584+
}, func() error {
585+
var err error
586+
tags, err = r.Database.Tags(canonicalImageName)
587+
if err != nil {
588+
return fmt.Errorf("failed to read tags from database: %w", err)
589+
}
590+
if len(tags) == 0 {
591+
return errNoTagsInDatabase
592+
}
593+
return nil
594+
})
595+
596+
return tags, err
597+
}

internal/controller/imagepolicy_controller_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ func TestImagePolicyReconciler_imageRepoHasNoTags(t *testing.T) {
9898
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imagePolicy), imagePolicy)
9999
return err == nil && !conditions.IsReady(imagePolicy) &&
100100
conditions.GetReason(imagePolicy, meta.ReadyCondition) == imagev1.DependencyNotReadyReason
101-
}).Should(BeTrue())
101+
}, 6*time.Second).Should(BeTrue())
102+
103+
g.Expect(conditions.GetMessage(imagePolicy, meta.ReadyCondition)).To(BeEquivalentTo("retrying in 30s error: no tags in database"))
102104
}
103105

104106
func TestImagePolicyReconciler_ignoresImageRepoNotReadyEvent(t *testing.T) {
@@ -527,10 +529,9 @@ func TestImagePolicyReconciler_objectLevelWorkloadIdentityFeatureGate(t *testing
527529

528530
g.Eventually(func() bool {
529531
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(imagePolicy), imagePolicy)
530-
logPolicyStatus(t, imagePolicy)
531532
return err == nil && !conditions.IsReady(imagePolicy) &&
532533
conditions.GetReason(imagePolicy, meta.ReadyCondition) == imagev1.DependencyNotReadyReason
533-
}).Should(BeTrue())
534+
}, 6*time.Second).Should(BeTrue())
534535
})
535536
}
536537

internal/controller/suite_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import (
4949

5050
// for Eventually
5151
const (
52-
timeout = time.Second * 30
52+
timeout = time.Second * 25
5353
contextTimeout = time.Second * 20
5454
interval = time.Second * 1
5555
reconciliationInterval = time.Second * 2
@@ -101,10 +101,11 @@ func TestMain(m *testing.M) {
101101
}
102102

103103
if err = (&ImagePolicyReconciler{
104-
Client: testEnv,
105-
Database: database.NewBadgerDatabase(testBadgerDB),
106-
EventRecorder: record.NewFakeRecorder(256),
107-
AuthOptionsGetter: optGetter,
104+
Client: testEnv,
105+
Database: database.NewBadgerDatabase(testBadgerDB),
106+
EventRecorder: record.NewFakeRecorder(256),
107+
AuthOptionsGetter: optGetter,
108+
DependencyRequeueInterval: 30 * time.Second,
108109
}).SetupWithManager(testEnv, ImagePolicyReconcilerOptions{
109110
RateLimiter: controller.GetDefaultRateLimiter(),
110111
}); err != nil {

main.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func main() {
9797
featureGates feathelper.FeatureGates
9898
tokenCacheOptions pkgcache.TokenFlags
9999
defaultServiceAccount string
100+
requeueDependency time.Duration
100101
)
101102

102103
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
@@ -106,6 +107,7 @@ func main() {
106107
flag.Int64Var(&storageValueLogFileSize, "storage-value-log-file-size", 1<<28, "Set the database's memory mapped value log file size in bytes. Effective memory usage is about two times this size.")
107108
flag.Uint16Var(&gcInterval, "gc-interval", 10, "The number of minutes to wait between garbage collections. 0 disables the garbage collector.")
108109
flag.IntVar(&concurrent, "concurrent", 4, "The number of concurrent resource reconciles.")
110+
flag.DurationVar(&requeueDependency, "requeue-dependency", 30*time.Second, "The interval at which failing dependencies are reevaluated.")
109111

110112
clientOptions.BindFlags(flag.CommandLine)
111113
logOptions.BindFlags(flag.CommandLine)
@@ -283,14 +285,15 @@ func main() {
283285
os.Exit(1)
284286
}
285287
if err := (&controller.ImagePolicyReconciler{
286-
Client: mgr.GetClient(),
287-
EventRecorder: eventRecorder,
288-
Metrics: metricsH,
289-
Database: db,
290-
ACLOptions: aclOptions,
291-
ControllerName: controllerName,
292-
AuthOptionsGetter: authOptionsGetter,
293-
TokenCache: tokenCache,
288+
Client: mgr.GetClient(),
289+
EventRecorder: eventRecorder,
290+
Metrics: metricsH,
291+
Database: db,
292+
ACLOptions: aclOptions,
293+
ControllerName: controllerName,
294+
AuthOptionsGetter: authOptionsGetter,
295+
TokenCache: tokenCache,
296+
DependencyRequeueInterval: requeueDependency,
294297
}).SetupWithManager(mgr, controller.ImagePolicyReconcilerOptions{
295298
RateLimiter: helper.GetRateLimiter(rateLimiterOptions),
296299
}); err != nil {

0 commit comments

Comments
 (0)