diff --git a/cmd/commands/flatten_rbd_pvc.go b/cmd/commands/flatten_rbd_pvc.go new file mode 100644 index 00000000..bdceeaa4 --- /dev/null +++ b/cmd/commands/flatten_rbd_pvc.go @@ -0,0 +1,85 @@ +/* +Copyright 2023 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 command + +import ( + "encoding/json" + "fmt" + + "github.com/rook/kubectl-rook-ceph/pkg/exec" + "github.com/rook/kubectl-rook-ceph/pkg/logging" + "github.com/spf13/cobra" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type TrashLsOutput struct { + ID string `json:"id"` + Name string `json:"name"` +} + +var namespace string +var allowInUse bool + +// FlattenRBDPVCCmd represents the rook commands +var FlattenRBDPVCCmd = &cobra.Command{ + Use: "flatten-rbd-pvc", + Short: "Flatten the RBD image corresponding to the target RBD PVC", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + pvcName := args[0] + pvc, err := clientSets.Kube.CoreV1().PersistentVolumeClaims(namespace).Get(cmd.Context(), pvcName, v1.GetOptions{}) + if err != nil { + logging.Fatal(err, "failed to get pvc %s/%s", namespace, pvcName) + return + } + pvName := pvc.Spec.VolumeName + pv, err := clientSets.Kube.CoreV1().PersistentVolumes().Get(cmd.Context(), pvName, v1.GetOptions{}) + if err != nil { + logging.Fatal(fmt.Errorf("failed to get pv %s", pvName)) + return + } + imageName := pv.Spec.CSI.VolumeAttributes["imageName"] + poolName := pv.Spec.CSI.VolumeAttributes["pool"] + tempImageName := imageName + "-temp" + if !allowInUse { + // TODO: check whether the target PVC is mounted + logging.Warning("can't flatten in-use pvc") + } + logging.Info("removing the temporary RBD image %s/%s if exist", poolName, tempImageName) + exec.RunCommandInOperatorPod(cmd.Context(), clientSets, "rbd", []string{"-p", poolName, "trash", "mv", tempImageName}, cephClusterNamespace, operatorNamespace, false, false) + out := exec.RunCommandInOperatorPod(cmd.Context(), clientSets, "rbd", []string{"-p", poolName, "trash", "ls", "--format=json"}, cephClusterNamespace, operatorNamespace, true, false) + var data []TrashLsOutput + json.Unmarshal([]byte(out), &data) + var id string + for _, d := range data { + if d.Name == tempImageName { + id = d.ID + break + } + } + if id != "" { + exec.RunCommandInOperatorPod(cmd.Context(), clientSets, "ceph", []string{"rbd", "task", "add", "trash", "remove", fmt.Sprintf("%s/%s", poolName, id)}, cephClusterNamespace, operatorNamespace, false, false) + } + logging.Info("flattening the target RBD image %s/%s", poolName, imageName) + exec.RunCommandInOperatorPod(cmd.Context(), clientSets, "rbd", []string{"-p", poolName, "flatten", imageName}, cephClusterNamespace, operatorNamespace, false, false) + }, +} + +func init() { + FlattenRBDPVCCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "pvc's namespace") + FlattenRBDPVCCmd.Flags().BoolVarP(&allowInUse, "allow-in-use", "a", false, "allow to flatten in-use image") +} diff --git a/cmd/commands/rook.go b/cmd/commands/rook.go index 5478f012..3fd5d500 100644 --- a/cmd/commands/rook.go +++ b/cmd/commands/rook.go @@ -26,8 +26,6 @@ import ( "github.com/spf13/cobra" ) -var json bool - // RookCmd represents the rook commands var RookCmd = &cobra.Command{ Use: "rook", diff --git a/cmd/main.go b/cmd/main.go index a6a24906..3a21b9f1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -41,5 +41,6 @@ func addcommands() { command.RestoreCmd, command.DestroyClusterCmd, command.SubvolumeCmd, + command.FlattenRBDPVCCmd, ) }