Skip to content

Commit 86078cd

Browse files
committed
Add Support for Restoring OCS Operator CRs in ODF CLI
This PR adds support for restoring deleted Custom Resources for OCS operator via the ODF CLI. It includes logic for handling storageclusters CRs by dynamically setting groupName and versionResource, ensuring compatibility and reliability during the restore process. Signed-off-by: Oded Viner <[email protected]>
1 parent b620cbf commit 86078cd

File tree

4 files changed

+169
-5
lines changed

4 files changed

+169
-5
lines changed

cmd/odf/restore/crds.go

+91-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,55 @@
11
package restore
22

33
import (
4+
"fmt"
5+
"slices"
6+
"strings"
7+
48
"github.com/red-hat-storage/odf-cli/cmd/odf/root"
59
"github.com/rook/kubectl-rook-ceph/pkg/k8sutil"
10+
"github.com/rook/kubectl-rook-ceph/pkg/logging"
611
pkgrestore "github.com/rook/kubectl-rook-ceph/pkg/restore"
712
"github.com/spf13/cobra"
813
)
914

15+
// groupVersions defines the supported CRD groups and their corresponding API versions.
16+
var groupVersions = map[string]string{
17+
"ocs.openshift.io": "v1",
18+
"ceph.rook.io": "v1",
19+
"csi.ceph.io": "v1alpha1",
20+
"odf.openshift.io": "v1alpha1",
21+
"noobaa.io": "v1alpha1",
22+
"csiaddons.openshift.io": "v1alpha1",
23+
}
24+
25+
// groupNameKeys returns the keys of a string map. It is used to print out supported group names.
26+
func groupNameKeys(m map[string]string) []string {
27+
out := make([]string, 0, len(m))
28+
for k := range m {
29+
out = append(out, k)
30+
}
31+
return out
32+
}
33+
34+
// parseFullyQualifiedCRD takes a fully qualified CRD type of the form "resource.group"
35+
// (for example, "cephclusters.ceph.rook.io") and returns the resource name, group name, and
36+
// the API version associated with that group. It returns an error if the format is invalid
37+
// or the group is not recognized.
38+
func parseFullyQualifiedCRD(fqcrd string) (resourceName, groupName, version string, err error) {
39+
parts := strings.SplitN(fqcrd, ".", 2)
40+
if len(parts) != 2 {
41+
return "", "", "", fmt.Errorf("invalid CRD format %q; expected format <resource>.<group>", fqcrd)
42+
}
43+
resourceName = parts[0]
44+
groupName = parts[1]
45+
46+
version, ok := groupVersions[groupName]
47+
if !ok {
48+
return "", "", "", fmt.Errorf("unsupported group %q; supported groups are: %v", groupName, groupNameKeys(groupVersions))
49+
}
50+
return resourceName, groupName, version, nil
51+
}
52+
1053
// deletedCmd represents the deleted command
1154
var deletedCmd = &cobra.Command{
1255
Use: "deleted",
@@ -20,8 +63,54 @@ var deletedCmd = &cobra.Command{
2063
},
2164
Run: func(cmd *cobra.Command, args []string) {
2265
k8sutil.SetDeploymentScale(cmd.Context(), root.ClientSets.Kube, root.OperatorNamespace, "ocs-operator", 0)
23-
pkgrestore.RestoreCrd(cmd.Context(), root.ClientSets, root.OperatorNamespace, root.StorageClusterNamespace, args)
24-
k8sutil.SetDeploymentScale(cmd.Context(), root.ClientSets.Kube, root.OperatorNamespace, "ocs-operator", 1)
66+
// Parse the fully qualified CRD (e.g. "cephclusters.ceph.rook.io").
67+
resourceName, groupName, version, err := parseFullyQualifiedCRD(args[0])
68+
if err != nil {
69+
logging.Fatal(fmt.Errorf("Error parsing CRD type: %v\n", err))
70+
}
71+
// Construct a new args slice with the resource name as the first argument.
72+
newArgs := make([]string, len(args))
73+
newArgs[0] = resourceName
74+
if len(args) > 1 {
75+
newArgs[1] = args[1]
76+
}
77+
var customResources []pkgrestore.CustomResource
78+
if slices.Contains(newArgs, "storageclusters") {
79+
customResources = []pkgrestore.CustomResource{
80+
// ceph.rook.io/v1
81+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephblockpoolradosnamespaces"},
82+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephblockpools"},
83+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephbucketnotifications"},
84+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephbuckettopics"},
85+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephclients"},
86+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephclusters"},
87+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephcosidrivers"},
88+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephfilesystemmirrors"},
89+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephfilesystems"},
90+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephfilesystemsubvolumegroups"},
91+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephnfses"},
92+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephobjectrealms"},
93+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephobjectstores"},
94+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephobjectstoreusers"},
95+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephobjectzonegroups"},
96+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephobjectzones"},
97+
{Group: "ceph.rook.io", Version: "v1", Resource: "cephrbdmirrors"},
2598

99+
// noobaa.io/v1alpha1
100+
{Group: "noobaa.io", Version: "v1alpha1", Resource: "backingstores"},
101+
{Group: "noobaa.io", Version: "v1alpha1", Resource: "bucketclasses"},
102+
{Group: "noobaa.io", Version: "v1alpha1", Resource: "namespacestores"},
103+
{Group: "noobaa.io", Version: "v1alpha1", Resource: "noobaaaccounts"},
104+
{Group: "noobaa.io", Version: "v1alpha1", Resource: "noobaas"},
105+
106+
// ocs.openshift.io/v1alpha1
107+
{Group: "ocs.openshift.io", Version: "v1alpha1", Resource: "storageconsumers"},
108+
}
109+
} else {
110+
customResources = []pkgrestore.CustomResource{}
111+
}
112+
113+
pkgrestore.RestoreCrd(cmd.Context(), root.ClientSets, root.OperatorNamespace, root.StorageClusterNamespace, groupName, version, "ocs-operator", customResources, newArgs)
114+
k8sutil.SetDeploymentScale(cmd.Context(), root.ClientSets.Kube, root.OperatorNamespace, "ocs-operator", 1)
26115
},
27116
}

cmd/odf/restore/restore_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package restore
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func Test_parseFullyQualifiedCRD(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
input string
12+
expectResource string
13+
expectGroup string
14+
expectVersion string
15+
expectErr bool
16+
expectedErrMsg string
17+
}{
18+
{
19+
name: "valid CRD: cephclusters.ceph.rook.io",
20+
input: "cephclusters.ceph.rook.io",
21+
expectResource: "cephclusters",
22+
expectGroup: "ceph.rook.io",
23+
expectVersion: "v1",
24+
},
25+
{
26+
name: "valid CRD: storageclusters.ocs.openshift.io",
27+
input: "storageclusters.ocs.openshift.io",
28+
expectResource: "storageclusters",
29+
expectGroup: "ocs.openshift.io",
30+
expectVersion: "v1",
31+
},
32+
{
33+
name: "valid CRD: cephconnections.csi.ceph.io",
34+
input: "cephconnections.csi.ceph.io",
35+
expectResource: "cephconnections",
36+
expectGroup: "csi.ceph.io",
37+
expectVersion: "v1beta1",
38+
},
39+
{
40+
name: "valid CRD: storagesystems.odf.openshift.io",
41+
input: "storagesystems.odf.openshift.io",
42+
expectResource: "storagesystems",
43+
expectGroup: "odf.openshift.io",
44+
expectVersion: "v1alpha1",
45+
},
46+
{
47+
name: "invalid format: missing dot",
48+
input: "invalidformat",
49+
expectErr: true,
50+
expectedErrMsg: "invalid CRD format",
51+
},
52+
{
53+
name: "unsupported group",
54+
input: "foo.unsupported.group",
55+
expectErr: true,
56+
expectedErrMsg: "unsupported group",
57+
},
58+
}
59+
60+
for _, tt := range tests {
61+
tt := tt // capture range variable
62+
t.Run(tt.name, func(t *testing.T) {
63+
resource, group, version, err := parseFullyQualifiedCRD(tt.input)
64+
if tt.expectErr {
65+
assert.Error(t, err)
66+
assert.Contains(t, err.Error(), tt.expectedErrMsg)
67+
} else {
68+
assert.NoError(t, err)
69+
assert.Equal(t, tt.expectResource, resource)
70+
assert.Equal(t, tt.expectGroup, group)
71+
assert.Equal(t, tt.expectVersion, version)
72+
}
73+
})
74+
}
75+
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/pkg/errors v0.9.1
99
github.com/ramendr/ramenctl v0.4.0
1010
github.com/red-hat-storage/ocs-operator/api/v4 v4.0.0-20240701091545-dfffbde82a9d
11-
github.com/rook/kubectl-rook-ceph v0.9.3
11+
github.com/rook/kubectl-rook-ceph v0.9.4
1212
github.com/rook/rook v1.17.1
1313
github.com/spf13/cobra v1.9.1
1414
github.com/stretchr/testify v1.10.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
743743
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
744744
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
745745
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
746-
github.com/rook/kubectl-rook-ceph v0.9.3 h1:+7THA8a+S2ArJrs9jpY1eJscAjPBKjlLXSmCVPU3eoY=
747-
github.com/rook/kubectl-rook-ceph v0.9.3/go.mod h1:dOQ+Yccc41DxZqe9jpvAUHsYTquYP/SKClrPmG70SLM=
746+
github.com/rook/kubectl-rook-ceph v0.9.4 h1:RgBr/nkgSMcNHE0zi4w3KDaQqGWCtsdI5L2Pa17JXYg=
747+
github.com/rook/kubectl-rook-ceph v0.9.4/go.mod h1:UvOPYgfA3+ulKE2omD+A6g82hst24jwO4gY9JIZqfu8=
748748
github.com/rook/rook v1.17.1 h1:nNUGesl5UtCVGi8Ta/Gl2m5z9rNmqnEzqSjY6vRFzKA=
749749
github.com/rook/rook v1.17.1/go.mod h1:Z5nuT7cGJLek9nRDH92J3XTcdqGMIyGDsFYo/O3NuT0=
750750
github.com/rook/rook/pkg/apis v0.0.0-20241216163035-3170ac6a0c58 h1:z7TkRb4D+XvXIGg4ClSjZw8gAPQcZg9zUSFfZ0pUcTY=

0 commit comments

Comments
 (0)