From f418bca2046c4cbf5bf6a85d524f9c8d5a603128 Mon Sep 17 00:00:00 2001 From: Jean-Marc Andre Date: Sun, 24 Jan 2021 20:40:54 +0100 Subject: [PATCH] Add run hooks commands --- Dockerfile | 2 +- go.mod | 2 +- pkg/controllers/backupsession_controller.go | 99 ++++++++------------- pkg/utils/root.go | 88 ++++++++++++++++++ 4 files changed, 129 insertions(+), 62 deletions(-) create mode 100644 pkg/utils/root.go diff --git a/Dockerfile b/Dockerfile index 68500fb..8f8be5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # Build a small image FROM arm32v7/alpine:3.12 -RUN apk add --no-cache restic postgresql-client +RUN apk add --no-cache su-exec restic postgresql-client COPY bin/formolcli /usr/local/bin # Command to run diff --git a/go.mod b/go.mod index 090031f..684f3a9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/desmo999r/formolcli go 1.14 require ( - github.com/desmo999r/formol v0.7.0 + github.com/desmo999r/formol v0.7.1 github.com/go-logr/logr v0.1.0 github.com/go-logr/zapr v0.1.0 github.com/mitchellh/go-homedir v1.1.0 diff --git a/pkg/controllers/backupsession_controller.go b/pkg/controllers/backupsession_controller.go index d437063..c453eb0 100644 --- a/pkg/controllers/backupsession_controller.go +++ b/pkg/controllers/backupsession_controller.go @@ -15,6 +15,7 @@ import ( formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1" "github.com/desmo999r/formolcli/pkg/backup" + formolcliutils "github.com/desmo999r/formolcli/pkg/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -95,72 +96,50 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro return ctrl.Result{}, client.IgnoreNotFound(err) } - // Found the BackupConfiguration. - // backupDeployment := func(target formolv1alpha1.Target) error { - // //backupSession.Status.BackupSessionState = formolv1alpha1.Running - // //if err := r.Client.Status().Update(ctx, backupSession); err != nil { - // // log.Error(err, "unable to update status", "backupsession", backupSession) - // // return err - // //} - // // Preparing for backup - // c := make(chan []byte) - // - // go func() { - // for msg := range c { - // var dat map[string]interface{} - // if err := json.Unmarshal(msg, &dat); err != nil { - // log.Error(err, "unable to unmarshal json", "msg", msg) - // continue - // } - // log.V(1).Info("message on stdout", "stdout", dat) - // //if message_type, ok := dat["message_type"]; ok && message_type == "summary" { - // // backupSession.Status.SnapshotId = dat["snapshot_id"].(string) - // // backupSession.Status.Duration = &metav1.Duration{Duration: time.Duration(dat["total_duration"].(float64)*1000) * time.Millisecond} - // //} - // } - // }() - // //result := formolv1alpha1.Failure - // defer func() { - // close(c) - // //backupSession.Status.BackupSessionState = result - // //if err := r.Status().Update(ctx, backupSession); err != nil { - // // log.Error(err, "unable to update status") - // //} - // }() - // // do the backup - // //backupSession.Status.StartTime = &metav1.Time{Time: time.Now()} - // if err := backup.BackupPaths(backupSession.Name, target.Paths, c); err != nil { - // log.Error(err, "unable to backup deployment") - // return err - // } - // //result = formolv1alpha1.Success - // - // return nil - // } - for _, target := range backupConf.Spec.Targets { switch target.Kind { case "Deployment": if target.Name == deploymentName { for i, status := range backupSession.Status.Targets { - if status.Name == target.Name && status.BackupState == formolv1alpha1.New { + if status.Name == target.Name { log.V(0).Info("It's for us!", "target", target) - result := formolv1alpha1.Success - status.StartTime = &metav1.Time{Time: time.Now()} - output, err := backup.BackupPaths(backupSession.Name, target.Paths) - if err != nil { - log.Error(err, "unable to backup deployment", "output", string(output)) - result = formolv1alpha1.Failure - } else { - snapshotId, duration := backup.GetBackupResults(output) - backupSession.Status.Targets[i].SnapshotId = snapshotId - backupSession.Status.Targets[i].Duration = &metav1.Duration{Duration: duration} - } - backupSession.Status.Targets[i].BackupState = result - log.V(1).Info("current backupSession status", "status", backupSession.Status) - if err := r.Status().Update(ctx, backupSession); err != nil { - log.Error(err, "unable to update backupsession status") - return ctrl.Result{}, err + switch status.BackupState { + case formolv1alpha1.New: + // TODO: Run beforeBackup + log.V(0).Info("New session, run the beforeBackup hooks if any") + result := formolv1alpha1.Running + if err := formolcliutils.RunBeforeBackup(target); err != nil { + result = formolv1alpha1.Failure + } + backupSession.Status.Targets[i].BackupState = result + log.V(1).Info("current backupSession status", "status", backupSession.Status) + if err := r.Status().Update(ctx, backupSession); err != nil { + log.Error(err, "unable to update backupsession status") + return ctrl.Result{}, err + } + case formolv1alpha1.Running: + log.V(0).Info("Running session. Do the backup") + result := formolv1alpha1.Success + status.StartTime = &metav1.Time{Time: time.Now()} + output, err := backup.BackupPaths(backupSession.Name, target.Paths) + if err != nil { + log.Error(err, "unable to backup deployment", "output", string(output)) + result = formolv1alpha1.Failure + } else { + snapshotId, duration := backup.GetBackupResults(output) + backupSession.Status.Targets[i].SnapshotId = snapshotId + backupSession.Status.Targets[i].Duration = &metav1.Duration{Duration: duration} + } + backupSession.Status.Targets[i].BackupState = result + log.V(1).Info("current backupSession status", "status", backupSession.Status) + if err := r.Status().Update(ctx, backupSession); err != nil { + log.Error(err, "unable to update backupsession status") + return ctrl.Result{}, err + } + case formolv1alpha1.Success, formolv1alpha1.Failure: + // I decided not to flag the backup as a failure if the AfterBackup command fail. But maybe I'm wrong + log.V(0).Info("Backup is over, run the afterBackup hooks if any") + formolcliutils.RunAfterBackup(target) } } } diff --git a/pkg/utils/root.go b/pkg/utils/root.go new file mode 100644 index 0000000..d7b730e --- /dev/null +++ b/pkg/utils/root.go @@ -0,0 +1,88 @@ +package utils + +import ( + formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1" + "github.com/go-logr/logr" + "github.com/go-logr/zapr" + "go.uber.org/zap" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" +) + +var logger logr.Logger + +func init() { + zapLog, _ := zap.NewDevelopment() + logger = zapr.NewLogger(zapLog) +} + +func runHook(hooks []formolv1alpha1.Hook, label string) error { + log := logger.WithName(label) + log.V(0).Info("Run commands") + for _, hook := range hooks { + err := RunChroot(hook.Cmd, hook.Args...) + if err != nil { + return err + } + } + return nil +} + +func Run(runCmd string, args []string) error { + log := logger.WithValues("Run", runCmd, "Args", args) + cmd := exec.Command(runCmd, args...) + output, err := cmd.CombinedOutput() + log.V(1).Info("result", "output", string(output)) + if err != nil { + log.Error(err, "something went wrong") + return err + } + return nil +} + +func RunChroot(runCmd string, args ...string) error { + log := logger.WithValues("RunChroot", runCmd, "Args", args) + root := regexp.MustCompile(`/proc/[0-9]+/root`) + pid := strconv.Itoa(os.Getpid()) + skip := false + if err := filepath.Walk("/proc", func(path string, info os.FileInfo, err error) error { + if skip { + return filepath.SkipDir + } + if err != nil { + return nil + } + if info.IsDir() && (info.Name() == "1" || info.Name() == pid) { + return filepath.SkipDir + } + if root.MatchString(path) { + if _, err := filepath.EvalSymlinks(path); err != nil { + return filepath.SkipDir + } + cmd := exec.Command("chroot", append([]string{path, runCmd}, args...)...) + output, err := cmd.CombinedOutput() + log.V(0).Info("result", "output", string(output)) + if err != nil { + log.Error(err, "something went wrong") + return err + } + skip = true + return filepath.SkipDir + } + return nil + }); err != nil { + return err + } + return nil +} + +func RunBeforeBackup(target formolv1alpha1.Target) error { + return runHook(target.BeforeBackup, "runBeforeBackup") +} + +func RunAfterBackup(target formolv1alpha1.Target) error { + return runHook(target.AfterBackup, "runAfterBackup") +}