Skip to content

Commit 2a48360

Browse files
committed
Sorting functions for common use
Signed-off-by: Yuji Ito <llamerada.jp@gmail.com>
1 parent d709325 commit 2a48360

3 files changed

Lines changed: 130 additions & 66 deletions

File tree

internal/controller/mantlebackup_controller.go

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,11 +1918,24 @@ func (r *MantleBackupReconciler) reconcileZeroOutJob(
19181918
return ctrl.Result{}, nil
19191919
}
19201920

1921-
if err := r.createOrUpdateZeroOutPV(ctx, backup, snapshotTarget.pv); err != nil {
1921+
if err := r.createOrUpdateStaticPV(
1922+
ctx,
1923+
snapshotTarget.pv,
1924+
snapshotTarget.pv.Spec.CSI.VolumeAttributes["imageName"],
1925+
snapshotTarget.pv.Spec.Capacity,
1926+
MakeZeroOutPVName(backup),
1927+
labelComponentZeroOutVolume,
1928+
); err != nil {
19221929
return ctrl.Result{}, err
19231930
}
19241931

1925-
if err := r.createOrUpdateZeroOutPVC(ctx, backup, snapshotTarget.pvc); err != nil {
1932+
if err := r.createOrUpdateStaticPVC(
1933+
ctx,
1934+
MakeZeroOutPVCName(backup),
1935+
MakeZeroOutPVName(backup),
1936+
labelComponentZeroOutVolume,
1937+
snapshotTarget.pvc.Spec.Resources,
1938+
); err != nil {
19261939
return ctrl.Result{}, err
19271940
}
19281941

@@ -1940,24 +1953,40 @@ func (r *MantleBackupReconciler) reconcileZeroOutJob(
19401953
return requeueReconciliation(), nil
19411954
}
19421955

1943-
func (r *MantleBackupReconciler) createOrUpdateZeroOutPV(
1956+
// createOrUpdateStaticPV creates or updates a static PersistentVolume (PV) resource.
1957+
// It copies relevant CSI and metadata fields from the basePV, sets the provided volume handle,
1958+
// capacity, and component label, and ensures the PV is configured for static provisioning.
1959+
//
1960+
// Parameters:
1961+
//
1962+
// ctx - context for the API calls
1963+
// basePV - source PV to copy CSI and metadata fields from
1964+
// volume - volume handle to assign to the new PV
1965+
// capacity - resource capacity for the PV
1966+
// newPvName - name for the new or updated PV
1967+
// componentName - label value for the component
1968+
//
1969+
// Returns an error if creation or update fails.
1970+
func (r *MantleBackupReconciler) createOrUpdateStaticPV(
19441971
ctx context.Context,
1945-
backup *mantlev1.MantleBackup,
1946-
targetPV *corev1.PersistentVolume,
1972+
basePV *corev1.PersistentVolume,
1973+
volume string,
1974+
capacity corev1.ResourceList,
1975+
newPvName, componentName string,
19471976
) error {
19481977
var pv corev1.PersistentVolume
1949-
pv.SetName(MakeZeroOutPVName(backup))
1978+
pv.SetName(newPvName)
19501979
_, err := ctrl.CreateOrUpdate(ctx, r.Client, &pv, func() error {
19511980
labels := pv.GetLabels()
19521981
if labels == nil {
19531982
labels = map[string]string{}
19541983
}
19551984
labels["app.kubernetes.io/name"] = labelAppNameValue
1956-
labels["app.kubernetes.io/component"] = labelComponentZeroOutVolume
1985+
labels["app.kubernetes.io/component"] = componentName
19571986
pv.SetLabels(labels)
19581987

19591988
pv.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}
1960-
pv.Spec.Capacity = targetPV.Spec.Capacity
1989+
pv.Spec.Capacity = capacity
19611990
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
19621991
pv.Spec.StorageClassName = ""
19631992

@@ -1967,47 +1996,60 @@ func (r *MantleBackupReconciler) createOrUpdateZeroOutPV(
19671996
if pv.Spec.CSI == nil {
19681997
pv.Spec.CSI = &corev1.CSIPersistentVolumeSource{}
19691998
}
1970-
pv.Spec.CSI.Driver = targetPV.Spec.CSI.Driver
1971-
pv.Spec.CSI.ControllerExpandSecretRef = targetPV.Spec.CSI.ControllerExpandSecretRef
1972-
pv.Spec.CSI.NodeStageSecretRef = targetPV.Spec.CSI.NodeStageSecretRef
1973-
pv.Spec.CSI.VolumeHandle = targetPV.Spec.CSI.VolumeAttributes["imageName"]
1999+
pv.Spec.CSI.Driver = basePV.Spec.CSI.Driver
2000+
pv.Spec.CSI.ControllerExpandSecretRef = basePV.Spec.CSI.ControllerExpandSecretRef
2001+
pv.Spec.CSI.NodeStageSecretRef = basePV.Spec.CSI.NodeStageSecretRef
2002+
pv.Spec.CSI.VolumeHandle = volume
19742003

19752004
if pv.Spec.CSI.VolumeAttributes == nil {
19762005
pv.Spec.CSI.VolumeAttributes = map[string]string{}
19772006
}
1978-
pv.Spec.CSI.VolumeAttributes["clusterID"] = targetPV.Spec.CSI.VolumeAttributes["clusterID"]
1979-
pv.Spec.CSI.VolumeAttributes["imageFeatures"] = targetPV.Spec.CSI.VolumeAttributes["imageFeatures"]
1980-
pv.Spec.CSI.VolumeAttributes["imageFormat"] = targetPV.Spec.CSI.VolumeAttributes["imageFormat"]
1981-
pv.Spec.CSI.VolumeAttributes["pool"] = targetPV.Spec.CSI.VolumeAttributes["pool"]
2007+
pv.Spec.CSI.VolumeAttributes["clusterID"] = basePV.Spec.CSI.VolumeAttributes["clusterID"]
2008+
pv.Spec.CSI.VolumeAttributes["imageFeatures"] = basePV.Spec.CSI.VolumeAttributes["imageFeatures"]
2009+
pv.Spec.CSI.VolumeAttributes["imageFormat"] = basePV.Spec.CSI.VolumeAttributes["imageFormat"]
2010+
pv.Spec.CSI.VolumeAttributes["pool"] = basePV.Spec.CSI.VolumeAttributes["pool"]
19822011
pv.Spec.CSI.VolumeAttributes["staticVolume"] = "true"
19832012

19842013
return nil
19852014
})
19862015
return err
19872016
}
19882017

1989-
func (r *MantleBackupReconciler) createOrUpdateZeroOutPVC(
2018+
// createOrUpdateStaticPVC creates or updates a PersistentVolumeClaim (PVC) for binding to a static PersistentVolume (PV).
2019+
// This function configures the PVC to reference the specified static PV, sets labels, access modes, storage class,
2020+
// and resource requirements for static volume provisioning.
2021+
//
2022+
// Parameters:
2023+
//
2024+
// ctx - context for the API calls
2025+
// pvcName - name for the new or updated PVC
2026+
// pvName - name of the static PV to bind to
2027+
// componentName - label value for the component
2028+
// resources - resource requirements for the PVC
2029+
//
2030+
// Returns an error if creation or update fails.
2031+
func (r *MantleBackupReconciler) createOrUpdateStaticPVC(
19902032
ctx context.Context,
1991-
backup *mantlev1.MantleBackup,
1992-
targetPVC *corev1.PersistentVolumeClaim,
2033+
pvcName, pvName, componentName string,
2034+
resources corev1.VolumeResourceRequirements,
19932035
) error {
19942036
var pvc corev1.PersistentVolumeClaim
1995-
pvc.SetName(MakeZeroOutPVCName(backup))
2037+
pvc.SetName(pvcName)
19962038
pvc.SetNamespace(r.managedCephClusterID)
19972039
_, err := ctrl.CreateOrUpdate(ctx, r.Client, &pvc, func() error {
19982040
labels := pvc.GetLabels()
19992041
if labels == nil {
20002042
labels = map[string]string{}
20012043
}
20022044
labels["app.kubernetes.io/name"] = labelAppNameValue
2003-
labels["app.kubernetes.io/component"] = labelComponentZeroOutVolume
2045+
labels["app.kubernetes.io/component"] = componentName
20042046
pvc.SetLabels(labels)
20052047

20062048
storageClassName := ""
20072049
pvc.Spec.StorageClassName = &storageClassName
20082050
pvc.Spec.AccessModes = []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}
2009-
pvc.Spec.Resources = targetPVC.Spec.Resources
2010-
pvc.Spec.VolumeName = MakeZeroOutPVName(backup)
2051+
pvc.Spec.Resources = resources
2052+
pvc.Spec.VolumeName = pvName
20112053

20122054
volumeMode := corev1.PersistentVolumeBlock
20132055
pvc.Spec.VolumeMode = &volumeMode

internal/controller/mantlerestore_controller.go

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"slices"
87

98
mantlev1 "github.com/cybozu-go/mantle/api/v1"
109
"github.com/cybozu-go/mantle/internal/ceph"
@@ -183,54 +182,13 @@ func (r *MantleRestoreReconciler) restoringPVName(restore *mantlev1.MantleRestor
183182
}
184183

185184
func (r *MantleRestoreReconciler) cloneImageFromBackup(ctx context.Context, restore *mantlev1.MantleRestore, backup *mantlev1.MantleBackup) error {
186-
logger := log.FromContext(ctx)
187185
pv := corev1.PersistentVolume{}
188186
err := json.Unmarshal([]byte(backup.Status.PVManifest), &pv)
189187
if err != nil {
190188
return fmt.Errorf("failed to unmarshal PV manifest: %w", err)
191189
}
192190

193-
bkImage := pv.Spec.CSI.VolumeAttributes["imageName"]
194-
if bkImage == "" {
195-
return fmt.Errorf("imageName not found in PV manifest")
196-
}
197-
pool := pv.Spec.CSI.VolumeAttributes["pool"]
198-
if pool == "" {
199-
return fmt.Errorf("pool not found in PV manifest")
200-
}
201-
202-
images, err := r.ceph.RBDLs(pool)
203-
if err != nil {
204-
return fmt.Errorf("failed to list RBD images: %w", err)
205-
}
206-
207-
// check if the image already exists
208-
if slices.Contains(images, r.restoringRBDImageName(restore)) {
209-
info, err := r.ceph.RBDInfo(pool, r.restoringRBDImageName(restore))
210-
if err != nil {
211-
return fmt.Errorf("failed to get RBD info: %w", err)
212-
}
213-
if info.Parent == nil {
214-
return fmt.Errorf("failed to get RBD info: parent field is empty")
215-
}
216-
217-
if info.Parent.Pool == pool && info.Parent.Image == bkImage && info.Parent.Snapshot == backup.Name {
218-
logger.Info("image already exists", "image", r.restoringRBDImageName(restore))
219-
return nil
220-
} else {
221-
return fmt.Errorf("image already exists but not a clone of the backup: %s", r.restoringRBDImageName(restore))
222-
}
223-
}
224-
225-
features := pv.Spec.CSI.VolumeAttributes["imageFeatures"]
226-
if features == "" {
227-
features = "deep-flatten"
228-
} else {
229-
features += ",deep-flatten"
230-
}
231-
232-
// create a clone image from the backup
233-
return r.ceph.RBDClone(pool, bkImage, backup.Name, r.restoringRBDImageName(restore), features)
191+
return createCloneByPV(ctx, r.ceph, &pv, backup.Name, r.restoringRBDImageName(restore))
234192
}
235193

236194
func (r *MantleRestoreReconciler) createOrUpdateRestoringPV(ctx context.Context, restore *mantlev1.MantleRestore, backup *mantlev1.MantleBackup) error {

internal/controller/util.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import (
55
"errors"
66
"fmt"
77
"os"
8+
"slices"
89
"strings"
910
"time"
1011

12+
"github.com/cybozu-go/mantle/internal/ceph"
1113
batchv1 "k8s.io/api/batch/v1"
1214
corev1 "k8s.io/api/core/v1"
1315
storagev1 "k8s.io/api/storage/v1"
@@ -19,6 +21,68 @@ import (
1921

2022
var errEmptyClusterID error = errors.New("cluster ID is empty")
2123

24+
// createCloneByPV creates a clone RBD image from a snapshot of the volume bound to the given PersistentVolume (PV).
25+
//
26+
// This function checks if the clone image already exists in the Ceph pool:
27+
// - If the clone exists and is a clone of the specified snapshot, it does nothing and returns nil.
28+
// - If the clone exists but is not a clone of the specified snapshot, it returns an error.
29+
// - If the clone does not exist, it creates a new clone image from the snapshot using the specified image features.
30+
//
31+
// Parameters:
32+
//
33+
// ctx - context for logging and API calls
34+
// cephCmd - Ceph command interface for RBD operations
35+
// pv - PersistentVolume object containing CSI and image attributes
36+
// snapshotName- name of the snapshot to clone from
37+
// cloneName - name for the new clone image
38+
//
39+
// Returns an error if required attributes are missing, if Ceph operations fail, or if a conflicting clone image exists.
40+
func createCloneByPV(ctx context.Context, cephCmd ceph.CephCmd, pv *corev1.PersistentVolume, snapshotName, cloneName string) error {
41+
logger := log.FromContext(ctx)
42+
43+
bkImage := pv.Spec.CSI.VolumeAttributes["imageName"]
44+
if bkImage == "" {
45+
return fmt.Errorf("imageName not found in PV manifest")
46+
}
47+
pool := pv.Spec.CSI.VolumeAttributes["pool"]
48+
if pool == "" {
49+
return fmt.Errorf("pool not found in PV manifest")
50+
}
51+
52+
images, err := cephCmd.RBDLs(pool)
53+
if err != nil {
54+
return fmt.Errorf("failed to list RBD images: %w", err)
55+
}
56+
57+
// check if the image already exists
58+
if slices.Contains(images, cloneName) {
59+
info, err := cephCmd.RBDInfo(pool, cloneName)
60+
if err != nil {
61+
return fmt.Errorf("failed to get RBD info: %w", err)
62+
}
63+
if info.Parent == nil {
64+
return fmt.Errorf("failed to get RBD info: parent field is empty")
65+
}
66+
67+
if info.Parent.Pool == pool && info.Parent.Image == bkImage && info.Parent.Snapshot == snapshotName {
68+
logger.Info("image already exists", "image", cloneName)
69+
return nil
70+
}
71+
// If the clone image already exists but is not a clone of the snapshot, it returns an error.
72+
return fmt.Errorf("image already exists but not a clone of the backup: %s", cloneName)
73+
}
74+
75+
features := pv.Spec.CSI.VolumeAttributes["imageFeatures"]
76+
if features == "" {
77+
features = "deep-flatten"
78+
} else {
79+
features += ",deep-flatten"
80+
}
81+
82+
// create a clone image from the backup
83+
return cephCmd.RBDClone(pool, bkImage, snapshotName, cloneName, features)
84+
}
85+
2286
func getCephClusterIDFromSCName(ctx context.Context, k8sClient client.Client, storageClassName string) (string, error) {
2387
var storageClass storagev1.StorageClass
2488
err := k8sClient.Get(ctx, types.NamespacedName{Name: storageClassName}, &storageClass)

0 commit comments

Comments
 (0)