224 lines
8.0 KiB
Go
224 lines
8.0 KiB
Go
package standalone
|
|
|
|
import (
|
|
"context"
|
|
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
|
|
"github.com/desmo999r/formolcli/controllers"
|
|
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/client-go/rest"
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
BACKUPSESSION_PREFIX = "bs"
|
|
)
|
|
|
|
var (
|
|
session controllers.Session
|
|
)
|
|
|
|
func init() {
|
|
session.Log = zap.New(zap.UseDevMode(true))
|
|
session.Context = context.Background()
|
|
log := session.Log.WithName("InitBackupSession")
|
|
ctrl.SetLogger(session.Log)
|
|
config, err := rest.InClusterConfig()
|
|
if err != nil {
|
|
config, err = clientcmd.BuildConfigFromFlags("", filepath.Join(os.Getenv("HOME"), ".kube", "config"))
|
|
if err != nil {
|
|
log.Error(err, "unable to get config")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
session.Scheme = runtime.NewScheme()
|
|
utilruntime.Must(formolv1alpha1.AddToScheme(session.Scheme))
|
|
utilruntime.Must(volumesnapshotv1.AddToScheme(session.Scheme))
|
|
utilruntime.Must(clientgoscheme.AddToScheme(session.Scheme))
|
|
session.Client, err = client.New(config, client.Options{Scheme: session.Scheme})
|
|
if err != nil {
|
|
log.Error(err, "unable to get client")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func BackupPaths(
|
|
backupSessionName string,
|
|
backupSessionNamespace string,
|
|
targetName string,
|
|
paths ...string) error {
|
|
log := session.Log.WithName("BackupPaths")
|
|
backupResult, err := session.BackupPaths(paths)
|
|
log.V(0).Info("Backup Job is over", "target", targetName, "snapshotID", backupResult.SnapshotId, "duration", backupResult.Duration)
|
|
if err != nil {
|
|
log.Error(err, "unable to backup paths", "paths", paths)
|
|
return err
|
|
}
|
|
backupSession := formolv1alpha1.BackupSession{}
|
|
if err := session.Get(session.Context, client.ObjectKey{
|
|
Name: backupSessionName,
|
|
Namespace: backupSessionNamespace,
|
|
}, &backupSession); err != nil {
|
|
log.Error(err, "unable to get backupsession", "name", backupSessionName, "namespace", backupSessionNamespace)
|
|
return err
|
|
}
|
|
for i, target := range backupSession.Status.Targets {
|
|
if target.TargetName == targetName {
|
|
backupSession.Status.Targets[i].SessionState = formolv1alpha1.Success
|
|
backupSession.Status.Targets[i].SnapshotId = backupResult.SnapshotId
|
|
backupSession.Status.Targets[i].Duration = &metav1.Duration{Duration: time.Now().Sub(backupSession.Status.Targets[i].StartTime.Time)}
|
|
if err := session.Status().Update(session.Context, &backupSession); err != nil {
|
|
log.Error(err, "unable to update backupSession status")
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
// Now find the PVC, VolumeSnapshots with the right label backupsession
|
|
// and delete them
|
|
vss := volumesnapshotv1.VolumeSnapshotList{}
|
|
if err := session.List(session.Context, &vss, client.InNamespace(backupSessionNamespace), client.MatchingLabels{"backupsession": backupSessionName}); err != nil {
|
|
log.Error(err, "unable to list the volumesnapshots", "backupsession", backupSessionName)
|
|
return err
|
|
}
|
|
for _, vs := range vss.Items {
|
|
if err := session.Delete(session.Context, &vs); err != nil {
|
|
log.Error(err, "unable to delete volumesnapshot", "vs", vs.Name)
|
|
return err
|
|
}
|
|
log.V(0).Info("volumesnapshot deleted", "vs", vs.Name)
|
|
}
|
|
pvcs := corev1.PersistentVolumeClaimList{}
|
|
if err := session.List(session.Context, &pvcs, client.InNamespace(backupSessionNamespace), client.MatchingLabels{"backupsession": backupSessionName}); err != nil {
|
|
log.Error(err, "unable to list the PVCs", "backupsession", backupSessionName)
|
|
return err
|
|
}
|
|
for _, pvc := range pvcs.Items {
|
|
if err := session.Delete(session.Context, &pvc); err != nil {
|
|
log.Error(err, "unable to delete PVC", "pvc", pvc.Name)
|
|
return err
|
|
}
|
|
log.V(0).Info("PVC deleted", "pvc", pvc.Name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func StartRestore(
|
|
restoreSessionName string,
|
|
restoreSessionNamespace string,
|
|
targetName string) {
|
|
log := session.Log.WithName("StartRestore")
|
|
if err := session.CheckRepo(); err != nil {
|
|
log.Error(err, "unable to check Repo")
|
|
return
|
|
}
|
|
restoreSession := formolv1alpha1.RestoreSession{}
|
|
if err := session.Get(session.Context, client.ObjectKey{
|
|
Name: restoreSessionName,
|
|
Namespace: restoreSessionNamespace,
|
|
}, &restoreSession); err != nil {
|
|
log.Error(err, "unable to get restoresession", "name", restoreSessionName, "namespace", restoreSessionNamespace)
|
|
return
|
|
}
|
|
backupSession := formolv1alpha1.BackupSession{
|
|
Spec: restoreSession.Spec.BackupSessionRef.Spec,
|
|
Status: restoreSession.Spec.BackupSessionRef.Status,
|
|
}
|
|
for i, target := range backupSession.Status.Targets {
|
|
if target.TargetName == targetName {
|
|
|
|
log.V(0).Info("StartRestore called", "restoring snapshot", target.SnapshotId)
|
|
cmd := exec.Command(controllers.RESTIC_EXEC, "restore", target.SnapshotId, "--target", "/")
|
|
// the restic restore command does not support JSON output
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
log.Error(err, "unable to restore snapshot", "output", output)
|
|
restoreSession.Status.Targets[i].SessionState = formolv1alpha1.Failure
|
|
} else {
|
|
restoreSession.Status.Targets[i].SessionState = formolv1alpha1.Waiting
|
|
log.V(0).Info("restore was a success. Moving to waiting state", "target", target.TargetName)
|
|
}
|
|
if err := session.Status().Update(session.Context, &restoreSession); err != nil {
|
|
log.Error(err, "unable to update RestoreSession", "restoreSession", restoreSession)
|
|
return
|
|
}
|
|
log.V(0).Info("restore over. removing the initContainer")
|
|
targetObject, targetPodSpec, _ := formolv1alpha1.GetTargetObjects(target.TargetKind)
|
|
if err := session.Get(session.Context, client.ObjectKey{
|
|
Namespace: restoreSessionNamespace,
|
|
Name: target.TargetName,
|
|
}, targetObject); err != nil {
|
|
log.Error(err, "unable to get target objects", "target", target.TargetName)
|
|
return
|
|
}
|
|
initContainers := []corev1.Container{}
|
|
for _, c := range targetPodSpec.InitContainers {
|
|
if c.Name == formolv1alpha1.RESTORECONTAINER_NAME {
|
|
continue
|
|
}
|
|
initContainers = append(initContainers, c)
|
|
}
|
|
targetPodSpec.InitContainers = initContainers
|
|
if err := session.Update(session.Context, targetObject); err != nil {
|
|
log.Error(err, "unable to remove the restore initContainer", "targetObject", targetObject)
|
|
return
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func CreateBackupSession(ref corev1.ObjectReference) {
|
|
log := session.Log.WithName("CreateBackupSession")
|
|
log.V(0).Info("CreateBackupSession called")
|
|
|
|
backupSession := &formolv1alpha1.BackupSession{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: strings.Join([]string{BACKUPSESSION_PREFIX, ref.Name, strconv.FormatInt(time.Now().Unix(), 10)}, "-"),
|
|
Namespace: ref.Namespace,
|
|
},
|
|
Spec: formolv1alpha1.BackupSessionSpec{
|
|
Ref: ref,
|
|
},
|
|
}
|
|
log.V(1).Info("create backupsession", "backupSession", backupSession)
|
|
if err := session.Create(session.Context, backupSession); err != nil {
|
|
log.Error(err, "unable to create backupsession")
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func DeleteSnapshot(namespace string, name string, targetName string, snapshotId string) {
|
|
log := session.Log.WithName("DeleteSnapshot")
|
|
session.Namespace = namespace
|
|
backupConf := formolv1alpha1.BackupConfiguration{}
|
|
if err := session.Get(session.Context, client.ObjectKey{
|
|
Namespace: namespace,
|
|
Name: name,
|
|
}, &backupConf); err != nil {
|
|
log.Error(err, "unable to get the BackupConf")
|
|
return
|
|
}
|
|
if err := session.SetResticEnv(backupConf, targetName); err != nil {
|
|
log.Error(err, "unable to set the restic env")
|
|
return
|
|
}
|
|
log.V(0).Info("deleting restic snapshot", "snapshotId", snapshotId)
|
|
cmd := exec.Command(controllers.RESTIC_EXEC, "forget", "--prune", snapshotId)
|
|
_, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
log.Error(err, "unable to delete snapshot", "snapshoId", snapshotId)
|
|
}
|
|
}
|