From 877892fa10562535254dd09aac93e5517313f05d Mon Sep 17 00:00:00 2001
From: Oded Viner <oviner@redhat.com>
Date: Sun, 27 Oct 2024 10:25:30 +0200
Subject: [PATCH] csi: add rbd ls option

This commit adds the ability to list RBD images.

Signed-off-by: Oded Viner <oviner@redhat.com>
---
 .github/workflows/go-test-config/action.yaml |  6 ++
 cmd/commands/rbd.go                          | 15 ++++
 go.mod                                       | 10 +--
 go.sum                                       | 20 ++---
 pkg/rbd/rbd.go                               | 93 ++++++++++++++++++++
 5 files changed, 129 insertions(+), 15 deletions(-)
 create mode 100644 pkg/rbd/rbd.go

diff --git a/.github/workflows/go-test-config/action.yaml b/.github/workflows/go-test-config/action.yaml
index 1f72dfcc..23bde1ba 100644
--- a/.github/workflows/go-test-config/action.yaml
+++ b/.github/workflows/go-test-config/action.yaml
@@ -116,6 +116,12 @@ runs:
         tests/github-action-helper.sh create_stale_subvolume
         subVol=$(kubectl rook-ceph ${NS_OPT} subvolume ls --stale | awk '{print $2}' | grep csi-vol)
         kubectl rook_ceph ${NS_OPT} subvolume delete myfs $subVol
+      
+    - name: Get rbd list
+      shell: bash --noprofile --norc -eo pipefail -x {0}
+      run: |
+        set -ex
+        kubectl rook-ceph ${NS_OPT} rbd ls
 
     - name: Get mon endpoints
       shell: bash --noprofile --norc -eo pipefail -x {0}
diff --git a/cmd/commands/rbd.go b/cmd/commands/rbd.go
index 32480e9a..b710e08c 100644
--- a/cmd/commands/rbd.go
+++ b/cmd/commands/rbd.go
@@ -17,6 +17,8 @@ limitations under the License.
 package command
 
 import (
+	rbd "github.com/rook/kubectl-rook-ceph/pkg/rbd"
+
 	"github.com/rook/kubectl-rook-ceph/pkg/exec"
 	"github.com/rook/kubectl-rook-ceph/pkg/logging"
 	"github.com/spf13/cobra"
@@ -38,3 +40,16 @@ var RbdCmd = &cobra.Command{
 		}
 	},
 }
+
+var listCmdRbd = &cobra.Command{
+	Use:   "ls",
+	Short: "Print the list of rbd images.",
+	Run: func(cmd *cobra.Command, args []string) {
+		ctx := cmd.Context()
+		rbd.ListImages(ctx, clientSets, operatorNamespace, cephClusterNamespace)
+	},
+}
+
+func init() {
+	RbdCmd.AddCommand(listCmdRbd)
+}
diff --git a/go.mod b/go.mod
index 1f88e172..35005c0d 100644
--- a/go.mod
+++ b/go.mod
@@ -9,14 +9,14 @@ require (
 	github.com/golang/mock v1.6.0
 	github.com/kubernetes-csi/external-snapshotter/client/v8 v8.0.0
 	github.com/pkg/errors v0.9.1
-	github.com/rook/rook v1.15.5
+	github.com/rook/rook v1.15.6
 	github.com/rook/rook/pkg/apis v0.0.0-20231204200402-5287527732f7
 	github.com/spf13/cobra v1.8.1
-	github.com/stretchr/testify v1.9.0
+	github.com/stretchr/testify v1.10.0
 	gopkg.in/yaml.v3 v3.0.1
-	k8s.io/api v0.31.2
-	k8s.io/apimachinery v0.31.2
-	k8s.io/client-go v0.31.2
+	k8s.io/api v0.31.3
+	k8s.io/apimachinery v0.31.3
+	k8s.io/client-go v0.31.3
 )
 
 require (
diff --git a/go.sum b/go.sum
index 5b3fa865..830ff767 100644
--- a/go.sum
+++ b/go.sum
@@ -688,8 +688,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
 github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
-github.com/rook/rook v1.15.5 h1:5CaeHUDnVYVC38en60XBCwSRh84jrgk8GrMyMO96lLg=
-github.com/rook/rook v1.15.5/go.mod h1:/SklxXAcmvgkj5w+zZFXb5iImadQOTKduwtXVDhw/MY=
+github.com/rook/rook v1.15.6 h1:LEBtNqEdpNcRFg3LX8oSGkYM/NdpbNbeHFtMONvU+tc=
+github.com/rook/rook v1.15.6/go.mod h1:/SklxXAcmvgkj5w+zZFXb5iImadQOTKduwtXVDhw/MY=
 github.com/rook/rook/pkg/apis v0.0.0-20231204200402-5287527732f7 h1:jXRUM2OJDz6hwpO4fElAgUqdTKosO3LHV3fwEB6iYJM=
 github.com/rook/rook/pkg/apis v0.0.0-20231204200402-5287527732f7/go.mod h1:ZnB1JWtsxJLz2Q3ylHFS4lE/2A1lw2zIOKN0uAmhiZM=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -744,8 +744,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -1406,8 +1406,8 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
 k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
 k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
 k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
-k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
-k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
+k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8=
+k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE=
 k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
 k8s.io/apiextensions-apiserver v0.18.3/go.mod h1:TMsNGs7DYpMXd+8MOCX8KzPOCx8fnZMoIGB24m03+JE=
 k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
@@ -1420,8 +1420,8 @@ k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp
 k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
 k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
-k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
-k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
+k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4=
+k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
 k8s.io/apiserver v0.18.3/go.mod h1:tHQRmthRPLUtwqsOnJJMoI8SW3lnoReZeE861lH8vUw=
 k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
 k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
@@ -1430,8 +1430,8 @@ k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
 k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
 k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
 k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
-k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
-k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
+k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4=
+k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs=
 k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
 k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
 k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
diff --git a/pkg/rbd/rbd.go b/pkg/rbd/rbd.go
new file mode 100644
index 00000000..2c322a05
--- /dev/null
+++ b/pkg/rbd/rbd.go
@@ -0,0 +1,93 @@
+/*
+Copyright 2024 The Rook Authors. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package rbd
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"strings"
+	"text/tabwriter"
+
+	"github.com/rook/kubectl-rook-ceph/pkg/exec"
+	"github.com/rook/kubectl-rook-ceph/pkg/k8sutil"
+	"github.com/rook/kubectl-rook-ceph/pkg/logging"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// List retrieves and displays the Ceph block pools and their associated images.
+func ListImages(ctx context.Context, clientsets *k8sutil.Clientsets, operatorNamespace string, clusterNamespace string) {
+	// List Ceph Block Pools
+	blockPoolList, err := clientsets.Rook.CephV1().CephBlockPools(clusterNamespace).List(ctx, v1.ListOptions{})
+	if err != nil {
+		logging.Fatal(fmt.Errorf("failed to list CephBlockPools: %w", err))
+	}
+
+	// Initialize map to store block pool names and associated Rados namespaces
+	blockPoolNames := make(map[string][]string)
+	for _, blockPool := range blockPoolList.Items {
+		blockPoolNames[blockPool.Name] = []string{"--", "--"}
+	}
+
+	// List Ceph Block Pool Rados Namespaces
+	blockPoolNamespaceList, err := clientsets.Rook.CephV1().CephBlockPoolRadosNamespaces(clusterNamespace).List(ctx, v1.ListOptions{})
+	if err != nil {
+		logging.Fatal(fmt.Errorf("failed to list CephBlockPoolRadosNamespaces: %w", err))
+	}
+	for _, blockPoolNameSpace := range blockPoolNamespaceList.Items {
+		var name string
+		// Check if Spec.BlockPoolName exists; otherwise, use ObjectMeta.Name
+		if blockPoolNameSpace.Spec.Name != "" {
+			name = blockPoolNameSpace.Spec.Name
+		} else {
+			name = blockPoolNameSpace.ObjectMeta.Name
+		}
+		if _, exists := blockPoolNames[blockPoolNameSpace.Spec.BlockPoolName]; exists {
+			blockPoolNames[blockPoolNameSpace.Spec.BlockPoolName][1] = name
+		}
+	}
+
+	// Retrieve list of RBD images for each pool
+	cmd := "rbd"
+	var args []string
+	for poolName := range blockPoolNames {
+		if blockPoolNames[poolName][1] != "--" {
+			args = []string{"ls", "--pool=" + poolName, "--namespace=" + blockPoolNames[poolName][1]}
+		} else {
+			args = []string{"ls", "--pool=" + poolName}
+		}
+		list, err := exec.RunCommandInOperatorPod(ctx, clientsets, cmd, args, operatorNamespace, clusterNamespace, true)
+		if err == nil && len(list) > 0 {
+			blockPoolNames[poolName][0] = strings.ReplaceAll(list, "\n", "")
+		}
+	}
+	PrintBlockPoolNames(blockPoolNames)
+}
+
+// PrintBlockPoolNames takes a map of block pool names to image names and prints them in a tabular format.
+func PrintBlockPoolNames(blockPoolNames map[string][]string) {
+	writer := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+	defer writer.Flush()
+	// Print header with column names
+	fmt.Fprintln(writer, "poolName\timageName\tnamespace\t")
+	fmt.Fprintln(writer, "--------\t---------\t---------\t")
+
+	// Print each row from the map
+	for poolName, poolData := range blockPoolNames {
+		fmt.Fprintf(writer, "%s\t%s\t%s\t\n", poolName, poolData[0], poolData[1])
+	}
+}