Add run hooks commands

This commit is contained in:
jandre 2021-01-24 20:40:54 +01:00
parent 7f4ef3c94c
commit f418bca204
4 changed files with 129 additions and 62 deletions

View File

@ -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

2
go.mod
View File

@ -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

View File

@ -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)
}
}
}

88
pkg/utils/root.go Normal file
View File

@ -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")
}