diff --git a/api/v1alpha1/cachedimage_types.go b/api/v1alpha1/cachedimage_types.go index d854fb3a..b2edee75 100644 --- a/api/v1alpha1/cachedimage_types.go +++ b/api/v1alpha1/cachedimage_types.go @@ -10,9 +10,11 @@ var RepositoryLabelName = "kuik.enix.io/repository" type CachedImageSpec struct { SourceImage string `json:"sourceImage"` // +optional - ExpiresAt *metav1.Time `json:"expiresAt,omitempty"` - PullSecretNames []string `json:"pullSecretNames,omitempty"` - PullSecretsNamespace string `json:"pullSecretsNamespace,omitempty"` + ExpiresAt *metav1.Time `json:"expiresAt,omitempty"` + // +optional + Retain bool `json:"retain,omitempty"` + PullSecretNames []string `json:"pullSecretNames,omitempty"` + PullSecretsNamespace string `json:"pullSecretsNamespace,omitempty"` } type PodReference struct { @@ -36,6 +38,7 @@ type CachedImageStatus struct { //+kubebuilder:subresource:status //+kubebuilder:resource:scope=Cluster,shortName=ci //+kubebuilder:printcolumn:name="Cached",type="boolean",JSONPath=".status.isCached" +//+kubebuilder:printcolumn:name="Retain",type="boolean",JSONPath=".spec.retain" //+kubebuilder:printcolumn:name="Expires at",type="string",JSONPath=".spec.expiresAt" //+kubebuilder:printcolumn:name="Pods count",type="integer",JSONPath=".status.usedBy.count" //+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" diff --git a/config/crd/bases/kuik.enix.io_cachedimages.yaml b/config/crd/bases/kuik.enix.io_cachedimages.yaml index c2676ac1..5e94b193 100644 --- a/config/crd/bases/kuik.enix.io_cachedimages.yaml +++ b/config/crd/bases/kuik.enix.io_cachedimages.yaml @@ -21,6 +21,9 @@ spec: - jsonPath: .status.isCached name: Cached type: boolean + - jsonPath: .spec.retain + name: Retain + type: boolean - jsonPath: .spec.expiresAt name: Expires at type: string @@ -59,6 +62,8 @@ spec: type: array pullSecretsNamespace: type: string + retain: + type: boolean sourceImage: type: string required: diff --git a/controllers/cachedimage_controller.go b/controllers/cachedimage_controller.go index 0ff12955..b06793f7 100644 --- a/controllers/cachedimage_controller.go +++ b/controllers/cachedimage_controller.go @@ -100,8 +100,35 @@ func (r *CachedImageReconciler) Reconcile(ctx context.Context, req ctrl.Request) log = log.WithValues("sourceImage", cachedImage.Spec.SourceImage) - // Delete expired CachedImage and schedule deletion for expiring ones + // Update CachedImage UsedBy status + if requeue, err := r.updatePodCount(ctx, &cachedImage); requeue { + return ctrl.Result{Requeue: true}, nil + } else if err != nil { + return ctrl.Result{}, err + } + + // Set an expiration date for unused CachedImage expiresAt := cachedImage.Spec.ExpiresAt + if len(cachedImage.Status.UsedBy.Pods) == 0 && !cachedImage.Spec.Retain { + expiresAt := metav1.NewTime(time.Now().Add(r.ExpiryDelay)) + log.Info("cachedimage is no longer used, setting an expiry date", "cachedImage", klog.KObj(&cachedImage), "expiresAt", expiresAt) + cachedImage.Spec.ExpiresAt = &expiresAt + + err := r.Patch(ctx, &cachedImage, client.Merge) + if err != nil && !apierrors.IsNotFound(err) { + return ctrl.Result{}, err + } + } else { + log.Info("cachedimage is used or retained", "cachedImage", klog.KObj(&cachedImage), "expiresAt", expiresAt, "retain", cachedImage.Spec.Retain) + patch := client.MergeFrom(cachedImage.DeepCopy()) + cachedImage.Spec.ExpiresAt = nil + err := r.Patch(ctx, &cachedImage, patch) + if err != nil && !apierrors.IsNotFound(err) { + return ctrl.Result{}, err + } + } + + // Delete expired CachedImage and schedule deletion for expiring ones if !expiresAt.IsZero() { if time.Now().After(expiresAt.Time) { log.Info("cachedimage expired, deleting it", "now", time.Now(), "expiresAt", expiresAt) @@ -156,25 +183,6 @@ func (r *CachedImageReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - // Update CachedImage UsedBy status - if requeue, err := r.updatePodCount(ctx, &cachedImage); requeue { - return ctrl.Result{Requeue: true}, nil - } else if err != nil { - return ctrl.Result{}, err - } - - // Set an expiration date for unused CachedImage - if len(cachedImage.Status.UsedBy.Pods) == 0 { - expiresAt := metav1.NewTime(time.Now().Add(r.ExpiryDelay)) - log.Info("cachedimage not is use anymore, setting an expiry date", "cachedImage", klog.KObj(&cachedImage), "expiresAt", expiresAt) - cachedImage.Spec.ExpiresAt = &expiresAt - - err := r.Patch(ctx, &cachedImage, client.Merge) - if err != nil && !apierrors.IsNotFound(err) { - return ctrl.Result{}, err - } - } - log.Info("reconciled cachedimage") return ctrl.Result{}, nil } @@ -211,6 +219,9 @@ func (r *CachedImageReconciler) SetupWithManager(mgr ctrl.Manager, maxConcurrent &source.Kind{Type: &corev1.Pod{}}, handler.EnqueueRequestsFromMapFunc(r.cachedImagesRequestFromPod), builder.WithPredicates(predicate.Funcs{ + // GenericFunc: func(e event.GenericEvent) bool { + // return true + // }, DeleteFunc: func(e event.DeleteEvent) bool { pod := e.Object.(*corev1.Pod) var currentPod corev1.Pod diff --git a/controllers/pod_controller.go b/controllers/pod_controller.go index 51f44fcd..73186baa 100644 --- a/controllers/pod_controller.go +++ b/controllers/pod_controller.go @@ -33,7 +33,7 @@ const LabelImageRewrittenName = "kuik.enix.io/images-rewritten" // PodReconciler reconciles a Pod object type PodReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme } //+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete @@ -89,7 +89,10 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R } } else { patch := client.MergeFrom(ci.DeepCopy()) - ci.Spec = cachedImage.Spec + + ci.Spec.PullSecretNames = cachedImage.Spec.PullSecretNames + ci.Spec.PullSecretsNamespace = cachedImage.Spec.PullSecretsNamespace + ci.Spec.SourceImage = cachedImage.Spec.SourceImage if err = r.Patch(ctx, &ci, patch); err != nil { return ctrl.Result{}, err diff --git a/helm/kube-image-keeper/templates/cachedimage-crd.yaml b/helm/kube-image-keeper/templates/cachedimage-crd.yaml index 08c8f0d8..bd9de492 100644 --- a/helm/kube-image-keeper/templates/cachedimage-crd.yaml +++ b/helm/kube-image-keeper/templates/cachedimage-crd.yaml @@ -18,6 +18,9 @@ spec: - jsonPath: .status.isCached name: Cached type: boolean + - jsonPath: .spec.retain + name: Retain + type: boolean - jsonPath: .spec.expiresAt name: Expires at type: string @@ -56,6 +59,8 @@ spec: type: array pullSecretsNamespace: type: string + retain: + type: boolean sourceImage: type: string required: