Skip to content

Commit e3b5e12

Browse files
committed
feat(controllers): CachedImage events
closes #28
1 parent db9b3ee commit e3b5e12

File tree

6 files changed

+78
-41
lines changed

6 files changed

+78
-41
lines changed

cmd/cache/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ func main() {
7777
}
7878

7979
if err = (&controllers.CachedImageReconciler{
80-
Client: mgr.GetClient(),
81-
Scheme: mgr.GetScheme(),
80+
Client: mgr.GetClient(),
81+
Scheme: mgr.GetScheme(),
82+
Recorder: mgr.GetEventRecorderFor("cachedimage-controller"),
8283
}).SetupWithManager(mgr); err != nil {
8384
setupLog.Error(err, "unable to create controller", "controller", "CachedImage")
8485
os.Exit(1)

config/rbac/role.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ metadata:
66
creationTimestamp: null
77
name: manager-role
88
rules:
9+
- apiGroups:
10+
- ""
11+
resources:
12+
- events
13+
verbs:
14+
- create
15+
- patch
916
- apiGroups:
1017
- ""
1118
resources:

controllers/cachedimage_controller.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
corev1 "k8s.io/api/core/v1"
2626
"k8s.io/apimachinery/pkg/api/errors"
2727
"k8s.io/apimachinery/pkg/runtime"
28+
"k8s.io/client-go/tools/record"
2829
"k8s.io/klog/v2"
2930
ctrl "sigs.k8s.io/controller-runtime"
3031
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -39,13 +40,15 @@ import (
3940
// CachedImageReconciler reconciles a CachedImage object
4041
type CachedImageReconciler struct {
4142
client.Client
42-
Scheme *runtime.Scheme
43+
Scheme *runtime.Scheme
44+
Recorder record.EventRecorder
4345
}
4446

4547
//+kubebuilder:rbac:groups=dcr.enix.io,resources=cachedimages,verbs=get;list;watch;create;update;patch;delete
4648
//+kubebuilder:rbac:groups=dcr.enix.io,resources=cachedimages/status,verbs=get;update;patch
4749
//+kubebuilder:rbac:groups=dcr.enix.io,resources=cachedimages/finalizers,verbs=update
4850
//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list
51+
//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch
4952

5053
// Reconcile is part of the main kubernetes reconciliation loop which aims to
5154
// move the current state of the cluster closer to the desired state.
@@ -72,10 +75,13 @@ func (r *CachedImageReconciler) Reconcile(ctx context.Context, req ctrl.Request)
7275
// Remove image from registry when CachedImage is beeing deleted, finalizer is removed after it
7376
if !cachedImage.ObjectMeta.DeletionTimestamp.IsZero() {
7477
if containsString(cachedImage.GetFinalizers(), finalizerName) {
75-
log.Info("deleting image cache")
78+
log.Info("deleting image from cache")
79+
r.Recorder.Eventf(&cachedImage, "Normal", "CleaningUp", "Removing image %s from cache", cachedImage.Spec.SourceImage)
7680
if err := registry.DeleteImage(cachedImage.Spec.SourceImage); err != nil {
81+
r.Recorder.Eventf(&cachedImage, "Warning", "CleanupFailed", "Image %s could not be removed from cache: %s", cachedImage.Spec.SourceImage, err)
7782
return ctrl.Result{}, err
7883
}
84+
r.Recorder.Eventf(&cachedImage, "Normal", "CleanedUp", "Image %s successfully removed from cache", cachedImage.Spec.SourceImage)
7985

8086
log.Info("removing finalizer")
8187
controllerutil.RemoveFinalizer(&cachedImage, finalizerName)
@@ -127,10 +133,13 @@ func (r *CachedImageReconciler) Reconcile(ctx context.Context, req ctrl.Request)
127133
if !expiresAt.IsZero() {
128134
if time.Now().After(expiresAt.Time) {
129135
log.Info("cachedimage expired, deleting it", "now", time.Now(), "expiresAt", expiresAt)
136+
r.Recorder.Eventf(&cachedImage, "Normal", "Expiring", "Image %s has expired, deleting it", cachedImage.Spec.SourceImage)
130137
err := r.Delete(ctx, &cachedImage)
131138
if err != nil {
139+
r.Recorder.Eventf(&cachedImage, "Warning", "ExpiringFailed", "Image %s could not expire: %s", cachedImage.Spec.SourceImage, err)
132140
return ctrl.Result{}, err
133141
}
142+
r.Recorder.Eventf(&cachedImage, "Normal", "Expired", "Image %s successfully expired", cachedImage.Spec.SourceImage)
134143
return ctrl.Result{}, nil
135144
} else {
136145
return ctrl.Result{RequeueAfter: expiresAt.Sub(time.Now())}, nil
@@ -139,17 +148,28 @@ func (r *CachedImageReconciler) Reconcile(ctx context.Context, req ctrl.Request)
139148

140149
// Adding image to registry
141150
log.Info("caching image")
142-
keychain := registry.NewKubernetesKeychain(r.Client, cachedImage.Spec.PullSecretsNamespace, cachedImage.Spec.PullSecretNames)
143-
if cacheUpdated, err := registry.CacheImage(cachedImage.Spec.SourceImage, keychain); err != nil {
144-
log.Error(err, "failed to cache image")
151+
isCached, err := registry.ImageIsCached(cachedImage.Spec.SourceImage)
152+
if err != nil {
153+
log.Error(err, "could not determine if the image present in cache")
145154
return ctrl.Result{}, err
146-
} else if cacheUpdated {
147-
log.Info("image cached")
148-
if err := r.Get(ctx, req.NamespacedName, &cachedImage); err != nil {
149-
return ctrl.Result{}, client.IgnoreNotFound(err)
155+
}
156+
157+
if !isCached {
158+
r.Recorder.Eventf(&cachedImage, "Normal", "Caching", "Start caching image %s", cachedImage.Spec.SourceImage)
159+
keychain := registry.NewKubernetesKeychain(r.Client, cachedImage.Spec.PullSecretsNamespace, cachedImage.Spec.PullSecretNames)
160+
if err := registry.CacheImage(cachedImage.Spec.SourceImage, keychain); err != nil {
161+
log.Error(err, "failed to cache image")
162+
r.Recorder.Eventf(&cachedImage, "Warning", "CacheFailed", "Failed to cache image %s, reason: %s", cachedImage.Spec.SourceImage, err)
163+
return ctrl.Result{}, err
164+
} else {
165+
log.Info("image cached")
166+
r.Recorder.Eventf(&cachedImage, "Normal", "Cached", "Successfully cached image %s", cachedImage.Spec.SourceImage)
167+
if err := r.Get(ctx, req.NamespacedName, &cachedImage); err != nil {
168+
return ctrl.Result{}, client.IgnoreNotFound(err)
169+
}
150170
}
151171
} else {
152-
log.Info("image already cached, cache not updated")
172+
log.Info("image already present in cache, ignoring")
153173
}
154174

155175
// Update CachedImage IsCached status

controllers/suite_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ var _ = BeforeSuite(func(done Done) {
147147
Expect(err).ToNot(HaveOccurred())
148148

149149
err = (&CachedImageReconciler{
150-
Client: k8sManager.GetClient(),
151-
Scheme: k8sManager.GetScheme(),
150+
Client: k8sManager.GetClient(),
151+
Scheme: k8sManager.GetScheme(),
152+
Recorder: k8sManager.GetEventRecorderFor("cachedimage-controller"),
152153
}).SetupWithManager(k8sManager)
153154
Expect(err).ToNot(HaveOccurred())
154155

helm/cache-registry/templates/clusterrole.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ apiVersion: rbac.authorization.k8s.io/v1
44
metadata:
55
name: {{ default (include "cache-registry.fullname" .) .Values.serviceAccount.name }}
66
rules:
7+
- apiGroups:
8+
- ""
9+
resources:
10+
- events
11+
verbs:
12+
- create
13+
- patch
714
- apiGroups:
815
- ""
916
resources:

internal/registry/registry.go

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,25 @@ func getDestinationName(sourceName string) (string, error) {
3838
return Endpoint + "/" + fullname, nil
3939
}
4040

41-
func DeleteImage(imageName string) error {
41+
func parseLocalReference(imageName string) (name.Reference, error) {
4242
destName, err := getDestinationName(imageName)
4343
if err != nil {
44-
return err
44+
return nil, err
4545
}
46-
ref, err := name.ParseReference(destName, name.Insecure)
46+
return name.ParseReference(destName, name.Insecure)
47+
}
48+
49+
func ImageIsCached(imageName string) (bool, error) {
50+
reference, err := parseLocalReference(imageName)
51+
if err != nil {
52+
return false, err
53+
}
54+
55+
return imageExists(reference)
56+
}
57+
58+
func DeleteImage(imageName string) error {
59+
ref, err := parseLocalReference(imageName)
4760
if err != nil {
4861
return err
4962
}
@@ -57,7 +70,7 @@ func DeleteImage(imageName string) error {
5770
return err
5871
}
5972

60-
digest, err := name.NewDigest(destName+"@"+descriptor.Digest.String(), name.Insecure)
73+
digest, err := name.NewDigest(ref.Name()+"@"+descriptor.Digest.String(), name.Insecure)
6174

6275
if err != nil {
6376
return err
@@ -66,51 +79,39 @@ func DeleteImage(imageName string) error {
6679
return remote.Delete(digest)
6780
}
6881

69-
func CacheImage(imageName string, keychain authn.Keychain) (bool, error) {
70-
destName, err := getDestinationName(imageName)
71-
if err != nil {
72-
return false, err
73-
}
74-
destRef, err := name.ParseReference(destName, name.Insecure)
82+
func CacheImage(imageName string, keychain authn.Keychain) error {
83+
destRef, err := parseLocalReference(imageName)
7584
if err != nil {
76-
return false, err
85+
return err
7786
}
7887
sourceRef, err := name.ParseReference(imageName, name.Insecure)
7988
if err != nil {
80-
return false, err
81-
}
82-
83-
exists, err := imageExists(destRef)
84-
if err != nil {
85-
return false, err
86-
}
87-
if exists {
88-
return false, nil
89+
return err
8990
}
9091

9192
auth := remote.WithAuthFromKeychain(keychain)
92-
exists, err = imageExists(sourceRef, auth)
93+
exists, err := imageExists(sourceRef, auth)
9394
if err != nil {
94-
return false, err
95+
return err
9596
}
9697
if !exists {
97-
return false, errors.New("could not find source image")
98+
return errors.New("could not find source image")
9899
}
99100

100101
image, err := remote.Image(sourceRef, auth)
101102
if err != nil {
102-
return false, err
103+
return err
103104
}
104105

105106
if err := remote.Write(destRef, image); err != nil {
106-
return false, err
107+
return err
107108
}
108109

109110
if err := remote.Put(destRef, image); err != nil {
110-
return false, err
111+
return err
111112
}
112113

113-
return true, nil
114+
return nil
114115
}
115116

116117
func SanitizeName(image string) string {

0 commit comments

Comments
 (0)