Skip to content

Commit e55a557

Browse files
committed
implement verify function
Signed-off-by: Yuji Ito <llamerada.jp@gmail.com>
1 parent 7e25f99 commit e55a557

2 files changed

Lines changed: 214 additions & 10 deletions

File tree

api/v1/mantlebackup_types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type MantleBackupStatus struct {
5959
const (
6060
BackupConditionReadyToUse = "ReadyToUse"
6161
BackupConditionSyncedToRemote = "SyncedToRemote"
62+
BackupConditionVerified = "Verified"
6263

6364
// Reasons for ConditionReadyToUse
6465
BackupReasonNone = "NoProblem"

internal/controller/mantlebackup_controller.go

Lines changed: 213 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const (
5353
labelComponentExportJob = "export-job"
5454
labelComponentUploadJob = "upload-job"
5555
labelComponentImportJob = "import-job"
56+
labelComponentVerifyJob = "verify-job"
57+
labelComponentVerifyVolume = "verify-volume"
5658
labelComponentZeroOutJob = "zeroout-job"
5759
labelComponentZeroOutVolume = "zeroout-volume"
5860
annotRemoteUID = "mantle.cybozu.io/remote-uid"
@@ -619,6 +621,115 @@ func (r *MantleBackupReconciler) replicateManifests(
619621
return ctrl.Result{}, nil
620622
}
621623

624+
func (r *MantleBackupReconciler) verify(
625+
ctx context.Context,
626+
backup *mantlev1.MantleBackup,
627+
) error {
628+
var storedPVC corev1.PersistentVolumeClaim
629+
if err := json.Unmarshal([]byte(backup.Status.PVCManifest), &storedPVC); err != nil {
630+
return fmt.Errorf("failed to unmarshal PVC manifest: %w", err)
631+
}
632+
633+
var storedPV corev1.PersistentVolume
634+
if err := json.Unmarshal([]byte(backup.Status.PVManifest), &storedPV); err != nil {
635+
return fmt.Errorf("failed to unmarshal PV manifest: %w", err)
636+
}
637+
638+
snapshotName := backup.Name
639+
cloneName := fmt.Sprintf("mantle-verify-%s", backup.UID)
640+
pvName := fmt.Sprintf("mantle-verify-%s", backup.UID)
641+
pvcName := fmt.Sprintf("mantle-verify-%s", backup.UID)
642+
jobName := fmt.Sprintf("mantle-verify-%s", backup.UID)
643+
644+
// create a clone by the snapshot bound to the MB
645+
if err := createCloneByPV(ctx, r.ceph, &storedPV, snapshotName, cloneName); err != nil {
646+
return fmt.Errorf("failed to create a clone by the snapshot: %w", err)
647+
}
648+
649+
// create a static PV with the clone
650+
if err := r.createOrUpdateStaticPV(
651+
ctx,
652+
&storedPV,
653+
cloneName,
654+
corev1.ResourceList{
655+
corev1.ResourceStorage: *resource.NewQuantity(*backup.Status.SnapSize, resource.BinarySI),
656+
},
657+
pvName,
658+
labelComponentVerifyVolume,
659+
); err != nil {
660+
return fmt.Errorf("failed to create a static PV with the clone: %w", err)
661+
}
662+
663+
// create a PVC with the PV
664+
if err := r.createOrUpdateStaticPVC(ctx, pvcName, pvName, labelComponentVerifyVolume, storedPVC.Spec.Resources); err != nil {
665+
return fmt.Errorf("failed to create a PVC with the PV: %w", err)
666+
}
667+
668+
// create a Job to execute e2fsck on the PVC
669+
if err := r.createOrUpdateVerifyJob(ctx, jobName, pvcName); err != nil {
670+
return fmt.Errorf("failed to create a Job to execute e2fsck on the PVC: %w", err)
671+
}
672+
673+
// wait for the Job to complete
674+
jobFinished, jobSucceeded, err := r.checkJobStatus(ctx, jobName)
675+
if err != nil {
676+
return fmt.Errorf("failed to check the Job status: %w", err)
677+
}
678+
if !jobFinished {
679+
// still running
680+
return nil
681+
}
682+
683+
// update MB's conditions field
684+
condition := metav1.ConditionTrue
685+
reason := ""
686+
if !jobSucceeded {
687+
condition = metav1.ConditionFalse
688+
}
689+
if err := r.updateMantleBackupCondition(
690+
ctx, backup,
691+
mantlev1.BackupConditionVerified,
692+
condition,
693+
reason,
694+
); err != nil {
695+
return fmt.Errorf("failed to update MantleBackup condition: %w", err)
696+
}
697+
698+
return nil
699+
}
700+
701+
// checkJobStatus checks the status of the Job with the given name.
702+
// It returns (is job finished, is job succeeded, error).
703+
func (r *MantleBackupReconciler) checkJobStatus(ctx context.Context, jobName string) (bool, bool, error) {
704+
var job batchv1.Job
705+
if err := r.Get(
706+
ctx,
707+
types.NamespacedName{
708+
Namespace: r.managedCephClusterID,
709+
Name: jobName,
710+
},
711+
&job,
712+
); err != nil {
713+
if aerrors.IsNotFound(err) {
714+
// The cache must be stale.
715+
return false, false, nil
716+
}
717+
return false, false, fmt.Errorf("failed to get Job for checking job status: %w", err)
718+
}
719+
720+
for _, c := range job.Status.Conditions {
721+
if c.Type == batchv1.JobComplete && c.Status == corev1.ConditionTrue {
722+
return true, true, nil
723+
}
724+
if c.Type == batchv1.JobFailed && c.Status == corev1.ConditionTrue {
725+
return true, false, nil
726+
}
727+
}
728+
729+
// job is still running
730+
return false, false, nil
731+
}
732+
622733
func (r *MantleBackupReconciler) provisionRBDSnapshot(
623734
ctx context.Context,
624735
backup *mantlev1.MantleBackup,
@@ -2125,6 +2236,80 @@ blkdiscard -z /dev/zeroout-rbd
21252236
return err
21262237
}
21272238

2239+
func (r *MantleBackupReconciler) createOrUpdateVerifyJob(ctx context.Context, jobName, pvcName string) error {
2240+
var job batchv1.Job
2241+
job.SetName(jobName)
2242+
job.SetNamespace(r.managedCephClusterID)
2243+
_, err := ctrl.CreateOrUpdate(ctx, r.Client, &job, func() error {
2244+
labels := job.GetLabels()
2245+
if labels == nil {
2246+
labels = map[string]string{}
2247+
}
2248+
labels["app.kubernetes.io/name"] = labelAppNameValue
2249+
labels["app.kubernetes.io/component"] = labelComponentVerifyJob
2250+
job.SetLabels(labels)
2251+
2252+
backoffLimit := int32(65535)
2253+
job.Spec.BackoffLimit = &backoffLimit
2254+
2255+
verifyContainerName := "verify"
2256+
job.Spec.PodFailurePolicy = &batchv1.PodFailurePolicy{
2257+
Rules: []batchv1.PodFailurePolicyRule{
2258+
{
2259+
Action: "FailJob",
2260+
OnExitCodes: &batchv1.PodFailurePolicyOnExitCodesRequirement{
2261+
ContainerName: &verifyContainerName,
2262+
Operator: batchv1.PodFailurePolicyOnExitCodesOpIn,
2263+
Values: []int32{1, 2, 3, 4, 5, 6, 7}, // File system errors
2264+
},
2265+
},
2266+
},
2267+
}
2268+
2269+
tru := true
2270+
var zero int64 = 0
2271+
job.Spec.Template.Spec.Containers = []corev1.Container{
2272+
{
2273+
Name: "verify",
2274+
Image: r.podImage,
2275+
Command: []string{
2276+
"/user/sbin/e2fsck",
2277+
"-fn",
2278+
"/dev/verify-rbd",
2279+
},
2280+
SecurityContext: &corev1.SecurityContext{
2281+
Privileged: &tru,
2282+
RunAsGroup: &zero,
2283+
RunAsUser: &zero,
2284+
},
2285+
VolumeDevices: []corev1.VolumeDevice{
2286+
{
2287+
Name: "verify-rbd",
2288+
DevicePath: "/dev/verify-rbd",
2289+
},
2290+
},
2291+
ImagePullPolicy: corev1.PullIfNotPresent,
2292+
},
2293+
}
2294+
2295+
job.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyNever
2296+
2297+
job.Spec.Template.Spec.Volumes = []corev1.Volume{
2298+
{
2299+
Name: "verify-rbd",
2300+
VolumeSource: corev1.VolumeSource{
2301+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
2302+
ClaimName: pvcName,
2303+
},
2304+
},
2305+
},
2306+
}
2307+
2308+
return nil
2309+
})
2310+
return err
2311+
}
2312+
21282313
func (r *MantleBackupReconciler) hasZeroOutJobCompleted(ctx context.Context, backup *mantlev1.MantleBackup) (bool, error) {
21292314
var job batchv1.Job
21302315
if err := r.Get(
@@ -2460,16 +2645,13 @@ func (r *MantleBackupReconciler) primaryCleanup(
24602645
return ctrl.Result{}, nil
24612646
}
24622647

2463-
// Update the status of the MantleBackup. Use Patch here because Update() is
2464-
// likely to fail due to "the object has been modified" error.
2465-
newTarget := target.DeepCopy()
2466-
meta.SetStatusCondition(&newTarget.Status.Conditions, metav1.Condition{
2467-
Type: mantlev1.BackupConditionSyncedToRemote,
2468-
Status: metav1.ConditionTrue,
2469-
Reason: mantlev1.BackupReasonNone,
2470-
})
2471-
if err := r.Client.Status().Patch(ctx, newTarget, client.MergeFrom(target)); err != nil {
2472-
return ctrl.Result{}, fmt.Errorf("failed to update SyncedToRemote to True by Patch: %w", err)
2648+
if err := r.updateMantleBackupCondition(
2649+
ctx, target,
2650+
mantlev1.BackupConditionSyncedToRemote,
2651+
metav1.ConditionTrue,
2652+
mantlev1.BackupReasonNone,
2653+
); err != nil {
2654+
return ctrl.Result{}, fmt.Errorf("failed to set SyncedToRemote condition to True: %w", err)
24732655
}
24742656
logger.Info("succeeded to sync a backup to the remote ceph cluster")
24752657

@@ -2677,3 +2859,24 @@ func (r *MantleBackupReconciler) deleteMiddleSnapshots(backup *mantlev1.MantleBa
26772859

26782860
return nil
26792861
}
2862+
2863+
func (r *MantleBackupReconciler) updateMantleBackupCondition(
2864+
ctx context.Context,
2865+
target *mantlev1.MantleBackup,
2866+
conditionType string,
2867+
status metav1.ConditionStatus,
2868+
reason string,
2869+
) error {
2870+
// Update the status of the MantleBackup. Use Patch here because Update() is
2871+
// likely to fail due to "the object has been modified" error.
2872+
willBeApplied := target.DeepCopy()
2873+
meta.SetStatusCondition(&willBeApplied.Status.Conditions, metav1.Condition{
2874+
Type: mantlev1.BackupConditionSyncedToRemote,
2875+
Status: metav1.ConditionTrue,
2876+
Reason: mantlev1.BackupReasonNone,
2877+
})
2878+
if err := r.Client.Status().Patch(ctx, willBeApplied, client.MergeFrom(target)); err != nil {
2879+
return fmt.Errorf("failed to update MantleBackup condition by patch: %w", err)
2880+
}
2881+
return nil
2882+
}

0 commit comments

Comments
 (0)