From 35e64bd4399a02e3cbfdffa95ab16902120dd2ae Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 10 Jan 2025 16:20:54 +0000 Subject: [PATCH] Work on flapping exports status --- .../v1/clusterimageexport_types.go | 6 + .../raczylo.com_clusterimageexports.yaml | 12 ++ config/rbac/role.yaml | 135 +++++++++--------- .../clusterimageexport_controller.go | 75 +++++----- 4 files changed, 124 insertions(+), 104 deletions(-) diff --git a/api/raczylo.com/v1/clusterimageexport_types.go b/api/raczylo.com/v1/clusterimageexport_types.go index 47e8a54..1dbe787 100644 --- a/api/raczylo.com/v1/clusterimageexport_types.go +++ b/api/raczylo.com/v1/clusterimageexport_types.go @@ -76,6 +76,10 @@ type ClusterImageExportSpec struct { // ClusterImageExportStatus defines the observed state of ClusterImageExport type ClusterImageExportStatus struct { Progress string `json:"progress,omitempty"` + // Total number of images to be exported + TotalImages int `json:"totalImages,omitempty"` + // Number of images that have completed export + CompletedImages int `json:"completedImages,omitempty"` } // +kubebuilder:object:root=true @@ -85,6 +89,8 @@ type ClusterImageExportStatus struct { // +kubebuilder:printcolumn:name="BasePath",type="string",JSONPath=".spec.basePath" // +kubebuilder:printcolumn:name="Storage",type="string",JSONPath=".spec.storage.target" // +kubebuilder:printcolumn:name="Progress",type="string",JSONPath=".status.progress" +// +kubebuilder:printcolumn:name="Images",type="string",JSONPath=".status.completedImages" +// +kubebuilder:printcolumn:name="Total",type="string",JSONPath=".status.totalImages" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" type ClusterImageExport struct { metav1.TypeMeta `json:",inline"` diff --git a/config/crd/bases/raczylo.com_clusterimageexports.yaml b/config/crd/bases/raczylo.com_clusterimageexports.yaml index f15faa2..d2ebbeb 100644 --- a/config/crd/bases/raczylo.com_clusterimageexports.yaml +++ b/config/crd/bases/raczylo.com_clusterimageexports.yaml @@ -24,6 +24,12 @@ spec: - jsonPath: .status.progress name: Progress type: string + - jsonPath: .status.completedImages + name: Images + type: string + - jsonPath: .status.totalImages + name: Total + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date @@ -160,8 +166,14 @@ spec: status: description: ClusterImageExportStatus defines the observed state of ClusterImageExport properties: + completedImages: + description: Number of images that have completed export + type: integer progress: type: string + totalImages: + description: Total number of images to be exported + type: integer type: object type: object served: true diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 4524309..e399ae8 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -4,71 +4,70 @@ kind: ClusterRole metadata: name: impex-mgr rules: - - apiGroups: - - "" - resources: - - pods - verbs: - - create - - delete - - deletecollection - - get - - list - - patch - - update - - watch - - apiGroups: - - apps - resources: - - daemonsets - - deployments - verbs: - - get - - list - - watch - - apiGroups: - - batch - resources: - - cronjobs - verbs: - - get - - list - - watch - - apiGroups: - - batch - resources: - - jobs - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - raczylo.com - resources: - - "*" - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - raczylo.com - resources: - - "*/finalizers" - verbs: - - update - - apiGroups: - - raczylo.com - resources: - - "*/status" - verbs: - - get - - patch - - update +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - daemonsets + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - batch + resources: + - cronjobs + verbs: + - get + - list + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - raczylo.com + resources: + - '*' + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - raczylo.com + resources: + - '*/finalizers' + verbs: + - update +- apiGroups: + - raczylo.com + resources: + - '*/status' + verbs: + - get + - patch + - update diff --git a/internal/controller/raczylo.com/clusterimageexport_controller.go b/internal/controller/raczylo.com/clusterimageexport_controller.go index 7b1d508..d303b78 100644 --- a/internal/controller/raczylo.com/clusterimageexport_controller.go +++ b/internal/controller/raczylo.com/clusterimageexport_controller.go @@ -83,38 +83,15 @@ func (r *ClusterImageExportReconciler) Reconcile(ctx context.Context, req ctrl.R } } - // Handle status updates with retries - if err := r.updateStatusWithRetry(ctx, clusterImageExport, func(export *raczylocomv1.ClusterImageExport) error { - // Reset status if the ClusterImageExport is in a completed state - if export.Status.Progress == shared.STATUS_SUCCESS || export.Status.Progress == shared.STATUS_FAILED { - export.Status.Progress = "" - return nil - } - - // Set to PENDING if empty - if export.Status.Progress == "" { - export.Status.Progress = shared.STATUS_PENDING - return nil - } - - return nil - }); err != nil { - l.Error(err, "unable to update ClusterImageExport status") - return ctrl.Result{}, err - } - - // If we just reset the status, requeue to process with reset status - if clusterImageExport.Status.Progress == "" { - return ctrl.Result{Requeue: true}, nil - } - - // Proceed with the rest of the reconciliation logic + // Proceed with reconciliation logic + // Get list of all images to be exported fullImagesList, err := r.listImagesInCluster(ctx, l, clusterImageExport) if err != nil { l.Error(err, "unable to list images in the cluster") return ctrl.Result{}, err } + // Add additional images if specified if len(clusterImageExport.Spec.AdditionalImages) > 0 { for _, image := range clusterImageExport.Spec.AdditionalImages { img, err := shared.ProcessContainerName(image) @@ -126,12 +103,19 @@ func (r *ClusterImageExportReconciler) Reconcile(ctx context.Context, req ctrl.R } } - // Update status to RUNNING with retry + // Update total image count and status + totalImages := len(fullImagesList.Containers) if err := r.updateStatusWithRetry(ctx, clusterImageExport, func(export *raczylocomv1.ClusterImageExport) error { - export.Status.Progress = shared.STATUS_RUNNING + if export.Status.Progress == "" { + export.Status.Progress = shared.STATUS_PENDING + } + if export.Status.Progress == shared.STATUS_PENDING { + export.Status.Progress = shared.STATUS_RUNNING + } + export.Status.TotalImages = totalImages return nil }); err != nil { - l.Error(err, "unable to update ClusterImageExport status to RUNNING") + l.Error(err, "unable to update ClusterImageExport status") return ctrl.Result{}, err } @@ -196,24 +180,43 @@ func (r *ClusterImageExportReconciler) Reconcile(ctx context.Context, req ctrl.R } } - // Check if all ClusterImages are completed - allCompleted, err := r.checkAllClusterImagesCompleted(ctx, clusterImageExport) - if err != nil { - l.Error(err, "unable to check ClusterImages status") + // Check completion status and update counts + completedCount := 0 + clusterImageList := &raczylocomv1.ClusterImageList{} + if err := r.List(ctx, clusterImageList, client.InNamespace(clusterImageExport.Namespace), + client.MatchingFields{"spec.exportName": clusterImageExport.Name}); err != nil { + l.Error(err, "unable to list ClusterImages") return ctrl.Result{}, err } - if allCompleted { + for _, ci := range clusterImageList.Items { + if ci.Status.Progress == shared.STATUS_SUCCESS || ci.Status.Progress == shared.STATUS_PRESENT { + completedCount++ + } + } + + // Update status with completion info + if completedCount == totalImages && totalImages > 0 { if err := r.updateStatusWithRetry(ctx, clusterImageExport, func(export *raczylocomv1.ClusterImageExport) error { export.Status.Progress = shared.STATUS_SUCCESS + export.Status.CompletedImages = completedCount return nil }); err != nil { - l.Error(err, "unable to update ClusterImageExport status to SUCCESS") + l.Error(err, "unable to update ClusterImageExport status") + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } else { + if err := r.updateStatusWithRetry(ctx, clusterImageExport, func(export *raczylocomv1.ClusterImageExport) error { + export.Status.CompletedImages = completedCount + return nil + }); err != nil { + l.Error(err, "unable to update ClusterImageExport status") return ctrl.Result{}, err } } - return ctrl.Result{Requeue: !allCompleted}, nil + return ctrl.Result{Requeue: true}, nil } // updateStatusWithRetry attempts to update the status of a ClusterImageExport with retries