From 606b66506faf8dd531da674da3594cd8002a6744 Mon Sep 17 00:00:00 2001 From: manju956 Date: Fri, 27 Sep 2024 13:26:05 +0530 Subject: [PATCH] RSCT operator status changes Signed-off-by: manju956 --- api/v1alpha1/rsct_types.go | 1 + api/v1alpha1/zz_generated.deepcopy.go | 7 +- .../rsct-operator.clusterserviceversion.yaml | 13 +++- bundle/manifests/rsct.ibm.com_rscts.yaml | 6 ++ config/crd/bases/rsct.ibm.com_rscts.yaml | 6 ++ config/rbac/role.yaml | 8 ++ go.mod | 1 + go.sum | 2 + internal/controller/rsct_controller.go | 2 + internal/controller/status.go | 73 +++++++++++++++++++ 10 files changed, 116 insertions(+), 3 deletions(-) diff --git a/api/v1alpha1/rsct_types.go b/api/v1alpha1/rsct_types.go index c20aead..5916b92 100644 --- a/api/v1alpha1/rsct_types.go +++ b/api/v1alpha1/rsct_types.go @@ -38,6 +38,7 @@ type RSCTSpec struct { type RSCTStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file + CurrentStatus *string `json:"current_status,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index bf0cd19..5f4483f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -30,7 +30,7 @@ func (in *RSCT) DeepCopyInto(out *RSCT) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RSCT. @@ -106,6 +106,11 @@ func (in *RSCTSpec) DeepCopy() *RSCTSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RSCTStatus) DeepCopyInto(out *RSCTStatus) { *out = *in + if in.CurrentStatus != nil { + in, out := &in.CurrentStatus, &out.CurrentStatus + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RSCTStatus. diff --git a/bundle/manifests/rsct-operator.clusterserviceversion.yaml b/bundle/manifests/rsct-operator.clusterserviceversion.yaml index a0ae0d2..672facb 100644 --- a/bundle/manifests/rsct-operator.clusterserviceversion.yaml +++ b/bundle/manifests/rsct-operator.clusterserviceversion.yaml @@ -17,11 +17,12 @@ metadata: }, "name": "rsct", "namespace": "rsct-operator-system" - } + }, + "spec": {} } ] capabilities: Basic Install - createdAt: "2024-07-10T12:35:54Z" + createdAt: "2024-10-07T14:37:59Z" operators.operatorframework.io/builder: operator-sdk-v1.34.1 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 name: rsct-operator.v0.0.1 @@ -44,6 +45,14 @@ spec: spec: clusterPermissions: - rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch - apiGroups: - "" resources: diff --git a/bundle/manifests/rsct.ibm.com_rscts.yaml b/bundle/manifests/rsct.ibm.com_rscts.yaml index 26d1b63..137e516 100644 --- a/bundle/manifests/rsct.ibm.com_rscts.yaml +++ b/bundle/manifests/rsct.ibm.com_rscts.yaml @@ -46,6 +46,12 @@ spec: type: object status: description: RSCTStatus defines the observed state of RSCT + properties: + current_status: + description: |- + INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + Important: Run "make" to regenerate code after modifying this file + type: string type: object type: object served: true diff --git a/config/crd/bases/rsct.ibm.com_rscts.yaml b/config/crd/bases/rsct.ibm.com_rscts.yaml index 684e75f..af979a2 100644 --- a/config/crd/bases/rsct.ibm.com_rscts.yaml +++ b/config/crd/bases/rsct.ibm.com_rscts.yaml @@ -46,6 +46,12 @@ spec: type: object status: description: RSCTStatus defines the observed state of RSCT + properties: + current_status: + description: |- + INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + Important: Run "make" to regenerate code after modifying this file + type: string type: object type: object served: true diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 90bb3f6..e5d6eb4 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -4,6 +4,14 @@ kind: ClusterRole metadata: name: manager-role rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch - apiGroups: - "" resources: diff --git a/go.mod b/go.mod index 627b976..55894c3 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.12.0 // indirect diff --git a/go.sum b/go.sum index 810d59e..6aaaa89 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/controller/rsct_controller.go b/internal/controller/rsct_controller.go index 1d01087..84456c9 100644 --- a/internal/controller/rsct_controller.go +++ b/internal/controller/rsct_controller.go @@ -51,6 +51,7 @@ type RSCTReconciler struct { //+kubebuilder:rbac:groups=rsct.ibm.com,resources=rscts/finalizers,verbs=update //+kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -62,6 +63,7 @@ type RSCTReconciler struct { // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile func (r *RSCTReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) rsct := &rsctv1alpha1.RSCT{} if err := r.Client.Get(ctx, req.NamespacedName, rsct); err != nil { diff --git a/internal/controller/status.go b/internal/controller/status.go index 2b79cce..74c4fff 100644 --- a/internal/controller/status.go +++ b/internal/controller/status.go @@ -2,11 +2,84 @@ package controller import ( "context" + "fmt" + "slices" rsctv1alpha1 "github.com/ocp-power-automation/rsct-operator/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/client" ) +// matchPodsStatus checks if status of all the pods in slice are same or not +func matchPodsStatus(podsStatus []string, status string) bool { + for _, ps := range podsStatus { + if ps == status { + continue + } + return false + } + return true +} + +// evalOperatorStatus determines operator status based on the pods status +func evalOperatorStatus(podList *corev1.PodList) string { + var effectiveStatus string + var podsStatus []string + for _, pod := range podList.Items { + switch { + case pod.Status.Phase == corev1.PodPending: + podsStatus = append(podsStatus, "PENDING") + case pod.Status.Phase == corev1.PodFailed: + podsStatus = append(podsStatus, "FAILED") + case pod.Status.Phase == corev1.PodRunning: + podsStatus = append(podsStatus, "RUNNING") + continue + default: + podsStatus = append(podsStatus, "UNKNOWN") + } + } + + if slices.Contains(podsStatus, "RUNNING") { + if slices.Contains(podsStatus, "FAILED") || slices.Contains(podsStatus, "PENDING") || slices.Contains(podsStatus, "UNKNOWN") { + effectiveStatus = "PARTIALLY_RUNNING" + } else { + effectiveStatus = "RUNNING" + } + } else if matchPodsStatus(podsStatus, "FAILED") { + effectiveStatus = "FAILED" + } else if matchPodsStatus(podsStatus, "PENDING") { + effectiveStatus = "PENDING" + } + return effectiveStatus +} + +// updateRSCTStatus updates RSCT operator status func (r *RSCTReconciler) updateRSCTStatus(ctx context.Context, rsct *rsctv1alpha1.RSCT, currentDaemonSet *appsv1.DaemonSet) error { + // Operator status: + // 1. PENDING + // 2. RUNNING + // 3. PARTIALLY-RUNNING + // 4. FAILED + + pods := &corev1.PodList{} + + labelSelector := labels.SelectorFromSet(map[string]string{"app": currentDaemonSet.Name}) + listOpts := &client.ListOptions{Namespace: rsct.Namespace, LabelSelector: labelSelector} + listOpts.ApplyOptions([]client.ListOption{}) + + if err := r.List(ctx, pods, listOpts); err != nil { + return fmt.Errorf("failed to get list of rsct operator pods: %w", err) + } + + operatorStatus := evalOperatorStatus(pods) + rsct.Status.CurrentStatus = &operatorStatus + + err := r.Status().Update(ctx, rsct) + if err != nil { + return err + } + return nil }