@@ -3,14 +3,29 @@ package controllers
33import (
44 "context"
55
6+ apierrors "k8s.io/apimachinery/pkg/api/errors"
7+ "k8s.io/apimachinery/pkg/api/meta"
8+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
69 "k8s.io/apimachinery/pkg/runtime"
10+ "k8s.io/apimachinery/pkg/types"
711 ctrl "sigs.k8s.io/controller-runtime"
12+ "sigs.k8s.io/controller-runtime/pkg/builder"
813 "sigs.k8s.io/controller-runtime/pkg/client"
14+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
15+ "sigs.k8s.io/controller-runtime/pkg/event"
16+ "sigs.k8s.io/controller-runtime/pkg/handler"
917 "sigs.k8s.io/controller-runtime/pkg/log"
18+ "sigs.k8s.io/controller-runtime/pkg/predicate"
19+ "sigs.k8s.io/controller-runtime/pkg/source"
1020
1121 kuikv1alpha1 "github.com/enix/kube-image-keeper/api/v1alpha1"
1222)
1323
24+ const (
25+ repositoryFinalizerName = "repository.kuik.enix.io/finalizer"
26+ typeReadyRepository = "Ready"
27+ )
28+
1429// RepositoryReconciler reconciles a Repository object
1530type RepositoryReconciler struct {
1631 client.Client
@@ -31,16 +46,138 @@ type RepositoryReconciler struct {
3146// For more details, check Reconcile and its Result here:
3247// - https://pkg.go.dev/sigs.k8s.io/[email protected] /pkg/reconcile 3348func (r * RepositoryReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
34- _ = log .FromContext (ctx )
49+ log := log .FromContext (ctx )
50+
51+ var repository kuikv1alpha1.Repository
52+ if err := r .Get (ctx , req .NamespacedName , & repository ); err != nil {
53+ return ctrl.Result {}, client .IgnoreNotFound (err )
54+ }
55+
56+ log .Info ("reconciling repository" )
57+
58+ if ! repository .ObjectMeta .DeletionTimestamp .IsZero () {
59+ r .UpdateStatus (ctx , & repository , []metav1.Condition {{
60+ Type : typeReadyRepository ,
61+ Status : metav1 .ConditionFalse ,
62+ Reason : "AskedForDeletion" ,
63+ Message : "Repository has been asked to be deleted" ,
64+ }})
65+
66+ if controllerutil .ContainsFinalizer (& repository , repositoryFinalizerName ) {
67+ var cachedImageList kuikv1alpha1.CachedImageList
68+ if err := r .List (ctx , & cachedImageList , client.MatchingFields {repositoryOwnerKey : repository .Name }); err != nil && ! apierrors .IsNotFound (err ) {
69+ return ctrl.Result {}, err
70+ }
71+
72+ log .Info ("repository is deleting" , "cachedImages" , len (cachedImageList .Items ))
73+ if len (cachedImageList .Items ) > 0 {
74+ return ctrl.Result {}, nil
75+ }
3576
36- // TODO(user): your logic here
77+ log .Info ("removing finalizer" )
78+ controllerutil .RemoveFinalizer (& repository , repositoryFinalizerName )
79+ if err := r .Update (ctx , & repository ); err != nil {
80+ return ctrl.Result {}, err
81+ }
82+ }
83+
84+ return ctrl.Result {}, nil
85+ }
86+
87+ err := r .UpdateStatus (ctx , & repository , []metav1.Condition {{
88+ Type : typeReadyRepository ,
89+ Status : metav1 .ConditionTrue ,
90+ Reason : "Created" ,
91+ Message : "Repository is ready" ,
92+ }})
93+ if err != nil {
94+ return ctrl.Result {}, err
95+ }
96+
97+ // Add finalizer to keep the Repository during image removal from registry on deletion
98+ if ! controllerutil .ContainsFinalizer (& repository , repositoryFinalizerName ) {
99+ log .Info ("adding finalizer" )
100+ controllerutil .AddFinalizer (& repository , repositoryFinalizerName )
101+ if err := r .Update (ctx , & repository ); err != nil {
102+ return ctrl.Result {}, err
103+ }
104+ }
37105
38106 return ctrl.Result {}, nil
39107}
40108
109+ func (r * RepositoryReconciler ) UpdateStatus (ctx context.Context , repository * kuikv1alpha1.Repository , conditions []metav1.Condition ) error {
110+ log := log .FromContext (ctx )
111+
112+ for _ , condition := range conditions {
113+ meta .SetStatusCondition (& repository .Status .Conditions , condition )
114+ }
115+
116+ conditionReady := meta .FindStatusCondition (repository .Status .Conditions , typeReadyRepository )
117+ if conditionReady .Status == metav1 .ConditionTrue {
118+ repository .Status .Phase = "Ready"
119+ } else if conditionReady .Status == metav1 .ConditionFalse {
120+ repository .Status .Phase = "Terminating"
121+ } else {
122+ repository .Status .Phase = ""
123+ }
124+
125+ if err := r .Status ().Update (ctx , repository ); err != nil {
126+ log .Error (err , "Failed to update Repository status" )
127+ return err
128+ }
129+
130+ return nil
131+ }
132+
41133// SetupWithManager sets up the controller with the Manager.
42134func (r * RepositoryReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
135+ p := predicate.Funcs {
136+ DeleteFunc : func (e event.DeleteEvent ) bool {
137+ return true
138+ },
139+ }
140+
141+ // Create an index to list CachedImage by Repository
142+ if err := mgr .GetFieldIndexer ().IndexField (context .Background (), & kuikv1alpha1.CachedImage {}, repositoryOwnerKey , func (rawObj client.Object ) []string {
143+ cachedImage := rawObj .(* kuikv1alpha1.CachedImage )
144+
145+ owners := cachedImage .GetOwnerReferences ()
146+ for _ , owner := range owners {
147+ if owner .APIVersion != kuikv1alpha1 .GroupVersion .String () || owner .Kind != "Repository" {
148+ return nil
149+ }
150+
151+ return []string {owner .Name }
152+ }
153+
154+ return []string {}
155+ }); err != nil {
156+ return err
157+ }
158+
43159 return ctrl .NewControllerManagedBy (mgr ).
44160 For (& kuikv1alpha1.Repository {}).
161+ Watches (
162+ & source.Kind {Type : & kuikv1alpha1.CachedImage {}},
163+ handler .EnqueueRequestsFromMapFunc (r .repositoryWithDeletingCachedImages ),
164+ builder .WithPredicates (p ),
165+ ).
45166 Complete (r )
46167}
168+
169+ func (r * RepositoryReconciler ) repositoryWithDeletingCachedImages (obj client.Object ) []ctrl.Request {
170+ cachedImage := obj .(* kuikv1alpha1.CachedImage )
171+ var currentCachedImage kuikv1alpha1.CachedImage
172+ // wait for the CachedImage to be really deleted
173+ if err := r .Get (context .Background (), client .ObjectKeyFromObject (cachedImage ), & currentCachedImage ); err == nil || ! apierrors .IsNotFound (err ) {
174+ return nil
175+ }
176+
177+ repositoryName , ok := cachedImage .Labels [kuikv1alpha1 .RepositoryLabelName ]
178+ if ! ok {
179+ return nil
180+ }
181+
182+ return []ctrl.Request {{NamespacedName : types.NamespacedName {Name : repositoryName }}}
183+ }
0 commit comments