-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(controllers): add a finalizer on Repositories
- Loading branch information
Showing
6 changed files
with
349 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,29 @@ package controllers | |
import ( | ||
"context" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/api/meta" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/types" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/builder" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" | ||
"sigs.k8s.io/controller-runtime/pkg/event" | ||
"sigs.k8s.io/controller-runtime/pkg/handler" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
"sigs.k8s.io/controller-runtime/pkg/predicate" | ||
"sigs.k8s.io/controller-runtime/pkg/source" | ||
|
||
kuikv1alpha1 "github.com/enix/kube-image-keeper/api/v1alpha1" | ||
) | ||
|
||
const ( | ||
repositoryFinalizerName = "repository.kuik.enix.io/finalizer" | ||
typeReadyRepository = "Ready" | ||
) | ||
|
||
// RepositoryReconciler reconciles a Repository object | ||
type RepositoryReconciler struct { | ||
client.Client | ||
|
@@ -31,16 +46,138 @@ type RepositoryReconciler struct { | |
// For more details, check Reconcile and its Result here: | ||
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile | ||
func (r *RepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
_ = log.FromContext(ctx) | ||
log := log.FromContext(ctx) | ||
|
||
var repository kuikv1alpha1.Repository | ||
if err := r.Get(ctx, req.NamespacedName, &repository); err != nil { | ||
return ctrl.Result{}, client.IgnoreNotFound(err) | ||
} | ||
|
||
log.Info("reconciling repository") | ||
|
||
if !repository.ObjectMeta.DeletionTimestamp.IsZero() { | ||
r.UpdateStatus(ctx, &repository, []metav1.Condition{{ | ||
Type: typeReadyRepository, | ||
Status: metav1.ConditionFalse, | ||
Reason: "AskedForDeletion", | ||
Message: "Repository has been asked to be deleted", | ||
}}) | ||
|
||
if controllerutil.ContainsFinalizer(&repository, repositoryFinalizerName) { | ||
var cachedImageList kuikv1alpha1.CachedImageList | ||
if err := r.List(ctx, &cachedImageList, client.MatchingFields{repositoryOwnerKey: repository.Name}); err != nil && !apierrors.IsNotFound(err) { | ||
return ctrl.Result{}, err | ||
} | ||
|
||
log.Info("repository is deleting", "cachedImages", len(cachedImageList.Items)) | ||
if len(cachedImageList.Items) > 0 { | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
// TODO(user): your logic here | ||
log.Info("removing finalizer") | ||
controllerutil.RemoveFinalizer(&repository, repositoryFinalizerName) | ||
if err := r.Update(ctx, &repository); err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
} | ||
|
||
return ctrl.Result{}, nil | ||
} | ||
|
||
err := r.UpdateStatus(ctx, &repository, []metav1.Condition{{ | ||
Type: typeReadyRepository, | ||
Status: metav1.ConditionTrue, | ||
Reason: "Created", | ||
Message: "Repository is ready", | ||
}}) | ||
if err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
|
||
// Add finalizer to keep the Repository during image removal from registry on deletion | ||
if !controllerutil.ContainsFinalizer(&repository, repositoryFinalizerName) { | ||
log.Info("adding finalizer") | ||
controllerutil.AddFinalizer(&repository, repositoryFinalizerName) | ||
if err := r.Update(ctx, &repository); err != nil { | ||
return ctrl.Result{}, err | ||
} | ||
} | ||
|
||
return ctrl.Result{}, nil | ||
} | ||
|
||
func (r *RepositoryReconciler) UpdateStatus(ctx context.Context, repository *kuikv1alpha1.Repository, conditions []metav1.Condition) error { | ||
log := log.FromContext(ctx) | ||
|
||
for _, condition := range conditions { | ||
meta.SetStatusCondition(&repository.Status.Conditions, condition) | ||
} | ||
|
||
conditionReady := meta.FindStatusCondition(repository.Status.Conditions, typeReadyRepository) | ||
if conditionReady.Status == metav1.ConditionTrue { | ||
repository.Status.Phase = "Ready" | ||
} else if conditionReady.Status == metav1.ConditionFalse { | ||
repository.Status.Phase = "Terminating" | ||
} else { | ||
repository.Status.Phase = "" | ||
} | ||
|
||
if err := r.Status().Update(ctx, repository); err != nil { | ||
log.Error(err, "Failed to update Repository status") | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// SetupWithManager sets up the controller with the Manager. | ||
func (r *RepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
p := predicate.Funcs{ | ||
DeleteFunc: func(e event.DeleteEvent) bool { | ||
return true | ||
}, | ||
} | ||
|
||
// Create an index to list CachedImage by Repository | ||
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &kuikv1alpha1.CachedImage{}, repositoryOwnerKey, func(rawObj client.Object) []string { | ||
cachedImage := rawObj.(*kuikv1alpha1.CachedImage) | ||
|
||
owners := cachedImage.GetOwnerReferences() | ||
for _, owner := range owners { | ||
if owner.APIVersion != kuikv1alpha1.GroupVersion.String() || owner.Kind != "Repository" { | ||
return nil | ||
} | ||
|
||
return []string{owner.Name} | ||
} | ||
|
||
return []string{} | ||
}); err != nil { | ||
return err | ||
} | ||
|
||
return ctrl.NewControllerManagedBy(mgr). | ||
For(&kuikv1alpha1.Repository{}). | ||
Watches( | ||
&source.Kind{Type: &kuikv1alpha1.CachedImage{}}, | ||
handler.EnqueueRequestsFromMapFunc(r.repositoryWithDeletingCachedImages), | ||
builder.WithPredicates(p), | ||
). | ||
Complete(r) | ||
} | ||
|
||
func (r *RepositoryReconciler) repositoryWithDeletingCachedImages(obj client.Object) []ctrl.Request { | ||
cachedImage := obj.(*kuikv1alpha1.CachedImage) | ||
var currentCachedImage kuikv1alpha1.CachedImage | ||
// wait for the CachedImage to be really deleted | ||
if err := r.Get(context.Background(), client.ObjectKeyFromObject(cachedImage), ¤tCachedImage); err == nil || !apierrors.IsNotFound(err) { | ||
return nil | ||
} | ||
|
||
repositoryName, ok := cachedImage.Labels[kuikv1alpha1.RepositoryLabelName] | ||
if !ok { | ||
return nil | ||
} | ||
|
||
return []ctrl.Request{{NamespacedName: types.NamespacedName{Name: repositoryName}}} | ||
} |
Oops, something went wrong.