Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set proper status to MantleBackup #33

Merged
merged 4 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1/mantlebackup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type MantleBackupStatus struct {
PVCManifest string `json:"pvcManifest,omitempty"`
// 'pvManifest' saving backup target PV manifest
PVManifest string `json:"pvManifest,omitempty"`

// 'snapID' indicates SNAPID of `rbd snap ls`
SnapID int `json:"snapID,omitempty"`
}

const (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ spec:
pvcManifest:
description: '''pvcManifest'' saving backup target PVC manifests'
type: string
snapID:
description: '''snapID'' indicates SNAPID of `rbd snap ls`'
type: integer
type: object
type: object
served: true
Expand Down
3 changes: 3 additions & 0 deletions config/crd/bases/mantle.cybozu.io_mantlebackups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ spec:
pvcManifest:
description: '''pvcManifest'' saving backup target PVC manifests'
type: string
snapID:
description: '''snapID'' indicates SNAPID of `rbd snap ls`'
type: integer
type: object
type: object
served: true
Expand Down
82 changes: 50 additions & 32 deletions internal/controller/mantlebackup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,42 +131,49 @@ func (r *MantleBackupReconciler) removeRBDSnapshot(poolName, imageName, snapshot
return nil
}

func listRBDSnapshots(poolName, imageName string) ([]Snapshot, error) {
command := []string{"rbd", "snap", "ls", poolName + "/" + imageName, "--format=json"}
out, err := executeCommand(command, nil)
if err != nil {
return nil, fmt.Errorf("failed to execute `rbd snap ls`: %s: %s: %w", poolName, imageName, err)
}

var snapshots []Snapshot
err = json.Unmarshal(out, &snapshots)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal the output of `rbd snap ls`: %s: %s: %w", poolName, imageName, err)
}

return snapshots, nil
}

func findRBDSnapshot(poolName, imageName, snapshotName string) (*Snapshot, error) {
snapshots, err := listRBDSnapshots(poolName, imageName)
if err != nil {
return nil, err
}
for _, s := range snapshots {
if s.Name == snapshotName {
return &s, nil
}
}
return nil, fmt.Errorf("snapshot not found: %s: %s: %s", poolName, imageName, snapshotName)
}

func (r *MantleBackupReconciler) createRBDSnapshot(ctx context.Context, poolName, imageName string, backup *mantlev1.MantleBackup) (ctrl.Result, error) {
command := []string{"rbd", "snap", "create", poolName + "/" + imageName + "@" + backup.Name}
_, err := executeCommand(command, nil)
if err != nil {
command = []string{"rbd", "snap", "ls", poolName + "/" + imageName, "--format=json"}
out, err := executeCommand(command, nil)
_, err := findRBDSnapshot(poolName, imageName, backup.Name)
if err != nil {
logger.Info("failed to run `rbd snap ls`", "poolName", poolName, "imageName", imageName, "error", err)
err2 := r.updateStatus(ctx, backup, metav1.Condition{Type: mantlev1.BackupConditionReadyToUse, Status: metav1.ConditionFalse, Reason: mantlev1.BackupReasonFailedToCreateBackup})
if err2 != nil {
return ctrl.Result{}, err2
}
return ctrl.Result{Requeue: true}, nil
}
var snapshots []Snapshot
err = json.Unmarshal(out, &snapshots)
if err != nil {
logger.Error("failed to unmarshal json", "json", out, "error", err)
err2 := r.updateStatus(ctx, backup, metav1.Condition{Type: mantlev1.BackupConditionReadyToUse, Status: metav1.ConditionFalse, Reason: mantlev1.BackupReasonFailedToCreateBackup})
if err2 != nil {
return ctrl.Result{}, err2
}
return ctrl.Result{Requeue: true}, err
}
existSnapshot := false
for _, s := range snapshots {
if s.Name == backup.Name {
existSnapshot = true
break
}
}
if !existSnapshot {
logger.Info("snapshot does not exists", "snapshotName", backup.Name)
err2 := r.updateStatus(ctx, backup, metav1.Condition{Type: mantlev1.BackupConditionReadyToUse, Status: metav1.ConditionFalse, Reason: mantlev1.BackupReasonFailedToCreateBackup})
if err2 != nil {
return ctrl.Result{}, err2
logger.Error("failed to find rbd snapshot", "error", err)
err := r.updateStatus(ctx, backup, metav1.Condition{
Type: mantlev1.BackupConditionReadyToUse,
Status: metav1.ConditionFalse,
Reason: mantlev1.BackupReasonFailedToCreateBackup,
})
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
Expand Down Expand Up @@ -348,7 +355,18 @@ func (r *MantleBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request
}
backup.Status.PVManifest = string(pvJs)

backup.Status.CreatedAt = metav1.NewTime(time.Now())
snapshot, err := findRBDSnapshot(poolName, imageName, backup.Name)
if err != nil {
return ctrl.Result{}, err
}
backup.Status.SnapID = snapshot.Id

createdAt, err := time.Parse("Mon Jan 2 15:04:05 2006", snapshot.Timestamp)
if err != nil {
return ctrl.Result{}, err
}
backup.Status.CreatedAt = metav1.NewTime(createdAt)

err = r.updateStatus(ctx, &backup, metav1.Condition{Type: mantlev1.BackupConditionReadyToUse, Status: metav1.ConditionTrue, Reason: mantlev1.BackupReasonNone})
if err != nil {
return ctrl.Result{}, err
Expand Down
15 changes: 10 additions & 5 deletions internal/controller/mantlebackup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ import (
"k8s.io/client-go/kubernetes/scheme"
)

func mockExecuteCommand(command []string, input io.Reader) ([]byte, error) {
return nil, nil
}

var _ = Describe("MantleBackup controller", func() {
ctx := context.Background()
var mgrUtil util.ManagerUtil
Expand All @@ -43,7 +39,13 @@ var _ = Describe("MantleBackup controller", func() {
err := reconciler.SetupWithManager(mgrUtil.GetManager())
Expect(err).NotTo(HaveOccurred())

executeCommand = mockExecuteCommand
executeCommand = func(command []string, _ io.Reader) ([]byte, error) {
if command[0] == "rbd" && command[1] == "snap" && command[2] == "ls" {
return []byte(fmt.Sprintf("[{\"id\":1000,\"name\":\"backup\"," +
"\"timestamp\":\"Mon Sep 2 00:42:00 2024\"}]")), nil
}
return nil, nil
}

mgrUtil.Start()
time.Sleep(100 * time.Millisecond)
Expand Down Expand Up @@ -177,6 +179,9 @@ var _ = Describe("MantleBackup controller", func() {
Expect(err).NotTo(HaveOccurred())
Expect(pvStored.Name).To(Equal(pv.Name))

snapID := backup.Status.SnapID
Expect(snapID).To(Equal(1000))

err = k8sClient.Delete(ctx, &backup)
Expect(err).NotTo(HaveOccurred())

Expand Down
5 changes: 4 additions & 1 deletion internal/controller/mantlebackupconfig_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controller
import (
"context"
"fmt"
"io"
"time"

mantlev1 "github.com/cybozu-go/mantle/api/v1"
Expand Down Expand Up @@ -174,7 +175,9 @@ var _ = Describe("MantleBackupConfig controller", func() {
err = reconciler.SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

executeCommand = mockExecuteCommand
executeCommand = func(_ []string, _ io.Reader) ([]byte, error) {
return nil, nil
}

ctx, cancel := context.WithCancel(ctx)
stopFunc = cancel
Expand Down
8 changes: 7 additions & 1 deletion internal/controller/mantlerestore_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controller
import (
"context"
"fmt"
"io"
"time"

mantlev1 "github.com/cybozu-go/mantle/api/v1"
Expand Down Expand Up @@ -67,7 +68,12 @@ func (test *mantleRestoreControllerUnitTest) setupEnv() {

It("prepare reconcilers", func() {
By("prepare MantleBackup reconciler")
executeCommand = mockExecuteCommand
executeCommand = func(command []string, _ io.Reader) ([]byte, error) {
if command[0] == "rbd" && command[1] == "snap" && command[2] == "ls" {
return []byte(fmt.Sprintf("[{\"id\":1000,\"name\":\"%s\",\"timestamp\":\"Mon Sep 2 00:42:00 2024\"}]", test.backupName)), nil
}
return nil, nil
}
test.mgrUtil = testutil.NewManagerUtil(ctx, cfg, scheme.Scheme)
backupReconciler := NewMantleBackupReconciler(k8sClient, test.mgrUtil.GetScheme(), test.cephClusterID, RoleStandalone, nil)
err := backupReconciler.SetupWithManager(test.mgrUtil.GetManager())
Expand Down
Loading