From 17adb16d0b7668448bad7ae08f4c3d08a6b0b13b Mon Sep 17 00:00:00 2001 From: NymanRobin Date: Wed, 20 Mar 2024 11:31:37 +0200 Subject: [PATCH] operator: improve the output of kubectl get make the kubectl output for crds more verbose. Following changes was done for each crd: CephBlockPool: Added print columns: - Type - FailureDomain - Replication - EC-CodingChunks - EC-DataChunks - Age Extended info field of status to include: type and failureDomain CephObjectStore: Added print columns: - Endpoint - SecureEndpoint - Age Added Age to print columns of following crds: CephObjectStoreUser, CephObjectZoneGroup, CephObjectZone, CephBucketTopic, CephClient, CephRBDMirror, CephFilesystemMirror, CephFilesystemSubVolumeGroup Signed-off-by: NymanRobin --- .../charts/rook-ceph/templates/resources.yaml | 54 ++++++++++++++++++ deploy/examples/crds.yaml | 54 ++++++++++++++++++ pkg/apis/ceph.rook.io/v1/types.go | 17 ++++++ pkg/operator/ceph/pool/controller.go | 10 ++-- pkg/operator/ceph/pool/status.go | 29 +++++++++- pkg/operator/ceph/pool/status_test.go | 57 +++++++++++++++++++ 6 files changed, 214 insertions(+), 7 deletions(-) diff --git a/deploy/charts/rook-ceph/templates/resources.yaml b/deploy/charts/rook-ceph/templates/resources.yaml index a98c6d201a16..30974dd9ddf1 100644 --- a/deploy/charts/rook-ceph/templates/resources.yaml +++ b/deploy/charts/rook-ceph/templates/resources.yaml @@ -89,6 +89,27 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .status.info.type + name: Type + type: string + - jsonPath: .status.info.failureDomain + name: FailureDomain + type: string + - jsonPath: .spec.replicated.size + name: Replication + priority: 1 + type: integer + - jsonPath: .spec.erasureCoded.codingChunks + name: EC-CodingChunks + priority: 1 + type: integer + - jsonPath: .spec.erasureCoded.dataChunks + name: EC-DataChunks + priority: 1 + type: integer + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -629,6 +650,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -777,6 +801,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -4919,6 +4946,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -6926,6 +6956,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -8873,6 +8906,15 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .status.info.endpoint + name: Endpoint + type: string + - jsonPath: .status.info.secureEndpoint + name: SecureEndpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10225,6 +10267,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10451,6 +10496,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10535,6 +10583,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10986,6 +11037,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: diff --git a/deploy/examples/crds.yaml b/deploy/examples/crds.yaml index fadd17220540..d34b124c4aca 100644 --- a/deploy/examples/crds.yaml +++ b/deploy/examples/crds.yaml @@ -91,6 +91,27 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .status.info.type + name: Type + type: string + - jsonPath: .status.info.failureDomain + name: FailureDomain + type: string + - jsonPath: .spec.replicated.size + name: Replication + priority: 1 + type: integer + - jsonPath: .spec.erasureCoded.codingChunks + name: EC-CodingChunks + priority: 1 + type: integer + - jsonPath: .spec.erasureCoded.dataChunks + name: EC-DataChunks + priority: 1 + type: integer + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -629,6 +650,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -776,6 +800,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -4915,6 +4942,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -6920,6 +6950,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -8864,6 +8897,15 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .status.info.endpoint + name: Endpoint + type: string + - jsonPath: .status.info.secureEndpoint + name: SecureEndpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10215,6 +10257,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10440,6 +10485,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10523,6 +10571,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: @@ -10973,6 +11024,9 @@ spec: - jsonPath: .status.phase name: Phase type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1 schema: openAPIV3Schema: diff --git a/pkg/apis/ceph.rook.io/v1/types.go b/pkg/apis/ceph.rook.io/v1/types.go index 6a954e41fc00..6933bcb62b67 100755 --- a/pkg/apis/ceph.rook.io/v1/types.go +++ b/pkg/apis/ceph.rook.io/v1/types.go @@ -694,6 +694,12 @@ type CrashCollectorSpec struct { // CephBlockPool represents a Ceph Storage Pool // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.status.info.type` +// +kubebuilder:printcolumn:name="FailureDomain",type=string,JSONPath=`.status.info.failureDomain` +// +kubebuilder:printcolumn:name="Replication",type=integer,JSONPath=`.spec.replicated.size`,priority=1 +// +kubebuilder:printcolumn:name="EC-CodingChunks",type=integer,JSONPath=`.spec.erasureCoded.codingChunks`,priority=1 +// +kubebuilder:printcolumn:name="EC-DataChunks",type=integer,JSONPath=`.spec.erasureCoded.dataChunks`,priority=1 +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephBlockPool struct { metav1.TypeMeta `json:",inline"` @@ -1401,6 +1407,9 @@ type PeerStatSpec struct { // CephObjectStore represents a Ceph Object Store Gateway // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.status.info.endpoint` +// +kubebuilder:printcolumn:name="SecureEndpoint",type=string,JSONPath=`.status.info.secureEndpoint` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephObjectStore struct { metav1.TypeMeta `json:",inline"` @@ -1664,6 +1673,7 @@ type ObjectStoreHostingSpec struct { // CephObjectStoreUser represents a Ceph Object Store Gateway User // +kubebuilder:resource:shortName=rcou;objectuser // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephObjectStoreUser struct { metav1.TypeMeta `json:",inline"` @@ -1841,6 +1851,7 @@ type PullSpec struct { // CephObjectZoneGroup represents a Ceph Object Store Gateway Zone Group // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephObjectZoneGroup struct { metav1.TypeMeta `json:",inline"` @@ -1871,6 +1882,7 @@ type ObjectZoneGroupSpec struct { // CephObjectZone represents a Ceph Object Store Gateway Zone // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephObjectZone struct { metav1.TypeMeta `json:",inline"` @@ -1933,6 +1945,7 @@ type ObjectZoneSpec struct { // CephBucketTopic represents a Ceph Object Topic for Bucket Notifications // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephBucketTopic struct { metav1.TypeMeta `json:",inline"` @@ -2569,6 +2582,7 @@ type DisruptionManagementSpec struct { // CephClient represents a Ceph Client // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephClient struct { metav1.TypeMeta `json:",inline"` @@ -2656,6 +2670,7 @@ type SanitizeDisksSpec struct { // CephRBDMirror represents a Ceph RBD Mirror // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephRBDMirror struct { metav1.TypeMeta `json:",inline"` @@ -2727,6 +2742,7 @@ type MirroringPeerSpec struct { // CephFilesystemMirror is the Ceph Filesystem Mirror object definition // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephFilesystemMirror struct { metav1.TypeMeta `json:",inline"` @@ -2961,6 +2977,7 @@ type StorageClassDeviceSet struct { // CephFilesystemSubVolumeGroup represents a Ceph Filesystem SubVolumeGroup // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // +kubebuilder:subresource:status type CephFilesystemSubVolumeGroup struct { metav1.TypeMeta `json:",inline"` diff --git a/pkg/operator/ceph/pool/controller.go b/pkg/operator/ceph/pool/controller.go index 394d2b4e7180..6ba05af1bff5 100644 --- a/pkg/operator/ceph/pool/controller.go +++ b/pkg/operator/ceph/pool/controller.go @@ -175,7 +175,7 @@ func (r *ReconcileCephBlockPool) reconcile(request reconcile.Request) (reconcile // The CR was just created, initializing status fields if cephBlockPool.Status == nil { - updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionProgressing, nil, k8sutil.ObservedGenerationNotAvailable) + updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionProgressing, k8sutil.ObservedGenerationNotAvailable) } // Make sure a CephCluster is present otherwise do nothing @@ -287,7 +287,7 @@ func (r *ReconcileCephBlockPool) reconcile(request reconcile.Request) (reconcile logger.Info(opcontroller.OperatorNotInitializedMessage) return opcontroller.WaitForRequeueIfOperatorNotInitialized, *cephBlockPool, nil } - updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionFailure, nil, k8sutil.ObservedGenerationNotAvailable) + updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionFailure, k8sutil.ObservedGenerationNotAvailable) return reconcileResponse, *cephBlockPool, errors.Wrapf(err, "failed to create pool %q.", cephBlockPool.GetName()) } @@ -304,7 +304,7 @@ func (r *ReconcileCephBlockPool) reconcile(request reconcile.Request) (reconcile // Always create a bootstrap peer token in case another cluster wants to add us as a peer reconcileResponse, err = opcontroller.CreateBootstrapPeerSecret(r.context, clusterInfo, cephBlockPool, k8sutil.NewOwnerInfo(cephBlockPool, r.scheme)) if err != nil { - updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionFailure, nil, k8sutil.ObservedGenerationNotAvailable) + updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionFailure, k8sutil.ObservedGenerationNotAvailable) return reconcileResponse, *cephBlockPool, errors.Wrapf(err, "failed to create rbd-mirror bootstrap peer for pool %q.", cephBlockPool.GetName()) } @@ -336,7 +336,7 @@ func (r *ReconcileCephBlockPool) reconcile(request reconcile.Request) (reconcile // update ObservedGeneration in status at the end of reconcile // Set Ready status, we are done reconciling - updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionReady, opcontroller.GenerateStatusInfo(cephBlockPool), observedGeneration) + updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionReady, observedGeneration) // If not mirrored there is no Status Info field to fulfil } else { @@ -347,7 +347,7 @@ func (r *ReconcileCephBlockPool) reconcile(request reconcile.Request) (reconcile } // update ObservedGeneration in status at the end of reconcile // Set Ready status, we are done reconciling - updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionReady, nil, observedGeneration) + updateStatus(r.opManagerContext, r.client, request.NamespacedName, cephv1.ConditionReady, observedGeneration) // Stop monitoring the mirroring status of this pool if blockPoolContextsExists && r.blockPoolContexts[blockPoolChannelKey].started { diff --git a/pkg/operator/ceph/pool/status.go b/pkg/operator/ceph/pool/status.go index 85aaa2fbbd66..e9de744fd79c 100644 --- a/pkg/operator/ceph/pool/status.go +++ b/pkg/operator/ceph/pool/status.go @@ -22,6 +22,7 @@ import ( "time" cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" + opcontroller "github.com/rook/rook/pkg/operator/ceph/controller" "github.com/rook/rook/pkg/operator/ceph/reporting" "github.com/rook/rook/pkg/operator/k8sutil" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -30,7 +31,7 @@ import ( ) // updateStatus updates a pool CR with the given status -func updateStatus(ctx context.Context, client client.Client, poolName types.NamespacedName, status cephv1.ConditionType, info map[string]string, observedGeneration int64) { +func updateStatus(ctx context.Context, client client.Client, poolName types.NamespacedName, status cephv1.ConditionType, observedGeneration int64) { pool := &cephv1.CephBlockPool{} err := client.Get(ctx, poolName, pool) if err != nil { @@ -47,7 +48,7 @@ func updateStatus(ctx context.Context, client client.Client, poolName types.Name } pool.Status.Phase = status - pool.Status.Info = info + updateStatusInfo(pool) if observedGeneration != k8sutil.ObservedGenerationNotAvailable { pool.Status.ObservedGeneration = observedGeneration } @@ -130,3 +131,27 @@ func toCustomResourceStatus(currentStatus *cephv1.MirroringStatusSpec, mirroring return mirroringStatusSpec, mirroringInfoSpec, snapshotScheduleStatusSpec } + +func updateStatusInfo(cephBlockPool *cephv1.CephBlockPool) { + m := make(map[string]string) + if cephBlockPool.Status.Phase == cephv1.ConditionReady && cephBlockPool.Spec.Mirroring.Enabled { + mirroringInfo := opcontroller.GenerateStatusInfo(cephBlockPool) + for key, value := range mirroringInfo { + m[key] = value + } + } + + if cephBlockPool.Spec.IsReplicated() { + m["type"] = "Replicated" + } else { + m["type"] = "Erasure Coded" + } + + if cephBlockPool.Spec.FailureDomain != "" { + m["failureDomain"] = cephBlockPool.Spec.FailureDomain + } else { + m["failureDomain"] = cephv1.DefaultFailureDomain + } + + cephBlockPool.Status.Info = m +} diff --git a/pkg/operator/ceph/pool/status_test.go b/pkg/operator/ceph/pool/status_test.go index f9723a1a59d5..e34b58282aed 100644 --- a/pkg/operator/ceph/pool/status_test.go +++ b/pkg/operator/ceph/pool/status_test.go @@ -21,6 +21,7 @@ import ( "testing" cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" + opcontroller "github.com/rook/rook/pkg/operator/ceph/controller" "github.com/stretchr/testify/assert" ) @@ -58,3 +59,59 @@ func TestToCustomResourceStatus(t *testing.T) { assert.NotEmpty(t, newSnapshotScheduleStatus) } } + +func TestUpdateStatusInfo(t *testing.T) { + + cephBlockPoolReplicated := &cephv1.CephBlockPool{ + Spec: cephv1.NamedBlockPoolSpec{ + Name: "test-pool-replicated", + PoolSpec: cephv1.PoolSpec{ + Replicated: cephv1.ReplicatedSpec{ + Size: 3, + }, + }, + }, + Status: &cephv1.CephBlockPoolStatus{ + Phase: cephv1.ConditionProgressing, + }, + } + updateStatusInfo(cephBlockPoolReplicated) + statusInfo := cephBlockPoolReplicated.Status.Info + assert.Equal(t, "Replicated", statusInfo["type"]) + assert.Equal(t, cephv1.DefaultFailureDomain, statusInfo["failureDomain"]) + assert.Empty(t, statusInfo[opcontroller.RBDMirrorBootstrapPeerSecretName]) + + cephBlockPoolErasureCoded := &cephv1.CephBlockPool{ + Spec: cephv1.NamedBlockPoolSpec{ + Name: "test-pool-erasure-coded", + PoolSpec: cephv1.PoolSpec{ + FailureDomain: "osd", + ErasureCoded: cephv1.ErasureCodedSpec{ + CodingChunks: 6, + DataChunks: 2, + }, + }, + }, + Status: &cephv1.CephBlockPoolStatus{ + Phase: cephv1.ConditionProgressing, + }, + } + updateStatusInfo(cephBlockPoolErasureCoded) + statusInfo = cephBlockPoolErasureCoded.Status.Info + assert.Equal(t, "Erasure Coded", statusInfo["type"]) + assert.Equal(t, "osd", statusInfo["failureDomain"]) + assert.Empty(t, statusInfo[opcontroller.RBDMirrorBootstrapPeerSecretName]) + + cephBlockPoolErasureCoded.Spec.PoolSpec.Mirroring = cephv1.MirroringSpec{ + Enabled: true, + } + + updateStatusInfo(cephBlockPoolErasureCoded) + statusInfo = cephBlockPoolErasureCoded.Status.Info + assert.Empty(t, statusInfo[opcontroller.RBDMirrorBootstrapPeerSecretName]) + + cephBlockPoolErasureCoded.Status.Phase = cephv1.ConditionReady + updateStatusInfo(cephBlockPoolErasureCoded) + statusInfo = cephBlockPoolErasureCoded.Status.Info + assert.NotEmpty(t, statusInfo[opcontroller.RBDMirrorBootstrapPeerSecretName]) +}