Compare commits

..

No commits in common. "1f2baef062e1865de9e5da189a7d8d04b257c615" and "7ca94b404878a3ef0763ce4de487e6697265ffa7" have entirely different histories.

8 changed files with 41 additions and 107 deletions

View File

@ -1,6 +1,6 @@
# Build a small image
FROM --platform=linux/arm64 alpine:3
RUN apk add --no-cache su-exec restic
RUN apk add --no-cache su-exec restic postgresql-client
COPY ./bin/formolcli /usr/local/bin
# Command to run

View File

@ -1,18 +1,12 @@
GOARCH ?= amd64
GOOS ?= linux
VERSION ?= latest
IMG ?= docker.io/desmo999r/formolcli:$(VERSION)
MANIFEST = formol-multiarch
IMG ?= docker.io/desmo999r/formolcli:latest
BINDIR = ./bin
.PHONY: formolcli
formolcli: fmt vet
GO111MODULE=on CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(BINDIR)/formolcli main.go
#.PHONY: formolcli-arm64
#formolcli-arm64: GOARCH = arm64
#formolcli-arm64: formolcli
.PHONY: fmt
fmt:
go fmt ./...
@ -23,15 +17,11 @@ vet:
.PHONY: docker-build
docker-build: formolcli
buildah bud --tag $(IMG) --manifest $(MANIFEST) --arch $(GOARCH) Dockerfile.$(GOARCH)
.PHONY: docker-build-arm64
docker-build-arm64: GOARCH = arm64
docker-build-arm64: docker-build
buildah bud --tag $(IMG) Dockerfile.$(GOARCH)
.PHONY: docker-push
docker-push:
buildah manifest push --all --rm $(MANIFEST) "docker://$(IMG)"
docker-push: docker-build
buildah push $(IMG)
.PHONY: all
all: formolcli docker-build

View File

@ -21,8 +21,6 @@ type BackupSessionReconciler struct {
func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.Log = log.FromContext(ctx)
r.Context = ctx
r.Namespace = req.NamespacedName.Namespace
r.Name = req.NamespacedName.Name
backupSession := formolv1alpha1.BackupSession{}
err := r.Get(ctx, req.NamespacedName, &backupSession)
@ -49,6 +47,7 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req ctrl.Reques
}
return ctrl.Result{}, err
}
r.Namespace = backupConf.Namespace
// we don't want a copy because we will modify and update it.
var target formolv1alpha1.Target
@ -97,7 +96,7 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req ctrl.Reques
newSessionState = formolv1alpha1.Waiting
switch target.BackupType {
case formolv1alpha1.JobKind:
if backupResult, err := r.backupJob(target); err != nil {
if backupResult, err := r.backupJob(backupSession.Name, target); err != nil {
r.Log.Error(err, "unable to run backup job", "target", targetName)
newSessionState = formolv1alpha1.Failure
} else {
@ -107,7 +106,7 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req ctrl.Reques
}
case formolv1alpha1.OnlineKind:
backupPaths := strings.Split(os.Getenv(formolv1alpha1.BACKUP_PATHS), string(os.PathListSeparator))
if backupResult, result := r.backupPaths(backupPaths); result != nil {
if backupResult, result := r.backupPaths(backupSession.Name, backupPaths); result != nil {
r.Log.Error(result, "unable to backup paths", "target name", targetName, "paths", backupPaths)
newSessionState = formolv1alpha1.Failure
} else {
@ -127,6 +126,8 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req ctrl.Reques
default:
r.Log.Error(err, "unable to do snapshot backup")
// TODO: cleanup existing snapshots
r.deleteVolumeSnapshots(target)
newSessionState = formolv1alpha1.Failure
}
}
}

View File

@ -12,7 +12,10 @@ import (
"os"
"os/exec"
"sigs.k8s.io/controller-runtime/pkg/client"
"strings"
)
const (
SNAPSHOT_PREFIX = "formol-"
)
type BackupResult struct {
@ -20,13 +23,13 @@ type BackupResult struct {
Duration float64
}
func (r *BackupSessionReconciler) backupPaths(paths []string) (result BackupResult, err error) {
func (r *BackupSessionReconciler) backupPaths(tag string, paths []string) (result BackupResult, err error) {
if err = r.CheckRepo(); err != nil {
r.Log.Error(err, "unable to setup repo", "repo", os.Getenv(formolv1alpha1.RESTIC_REPOSITORY))
return
}
r.Log.V(0).Info("backing up paths", "paths", paths)
cmd := exec.Command(RESTIC_EXEC, append([]string{"backup", "--json", "--tag", r.Name}, paths...)...)
cmd := exec.Command(RESTIC_EXEC, append([]string{"backup", "--json", "--tag", tag}, paths...)...)
stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe()
_ = cmd.Start()
@ -52,7 +55,7 @@ func (r *BackupSessionReconciler) backupPaths(paths []string) (result BackupResu
return
}
func (r *BackupSessionReconciler) backupJob(target formolv1alpha1.Target) (result BackupResult, err error) {
func (r *BackupSessionReconciler) backupJob(tag string, target formolv1alpha1.Target) (result BackupResult, err error) {
paths := []string{}
for _, container := range target.Containers {
for _, job := range container.Job {
@ -72,7 +75,7 @@ func (r *BackupSessionReconciler) backupJob(target formolv1alpha1.Target) (resul
paths = append(paths, container.SharePath)
}
}
result, err = r.backupPaths(paths)
result, err = r.backupPaths(tag, paths)
return
}
@ -95,9 +98,10 @@ func (r *BackupSessionReconciler) backupSnapshot(target formolv1alpha1.Target) e
// sidecar := formolv1alpha1.GetSidecar(backupConf, target)
_, vms := formolv1alpha1.GetVolumeMounts(container, targetContainer)
if err := r.snapshotVolumes(vms, targetPodSpec); err != nil {
if IsNotReadyToUse(err) {
switch err.(type) {
case *NotReadyToUseError:
r.Log.V(0).Info("Some volumes are still not ready to use")
} else {
default:
r.Log.Error(err, "cannot snapshot the volumes")
return err
}
@ -115,15 +119,6 @@ func (e *NotReadyToUseError) Error() string {
return "Snapshot is not ready to use"
}
func IsNotReadyToUse(err error) bool {
switch err.(type) {
case *NotReadyToUseError:
return true
default:
return false
}
}
func (r *BackupSessionReconciler) snapshotVolume(volume corev1.Volume) (*volumesnapshotv1.VolumeSnapshot, error) {
r.Log.V(0).Info("Preparing snapshot", "volume", volume.Name)
if volume.VolumeSource.PersistentVolumeClaim != nil {
@ -153,11 +148,9 @@ func (r *BackupSessionReconciler) snapshotVolume(volume corev1.Volume) (*volumes
if volumeSnapshotClass.Driver == pv.Spec.PersistentVolumeSource.CSI.Driver {
// Check if a snapshot exist
volumeSnapshot := volumesnapshotv1.VolumeSnapshot{}
volumeSnapshotName := strings.Join([]string{"vs", r.Name, pv.Name}, "-")
if err := r.Get(r.Context, client.ObjectKey{
Namespace: r.Namespace,
Name: volumeSnapshotName,
Name: SNAPSHOT_PREFIX + pv.Name,
}, &volumeSnapshot); errors.IsNotFound(err) {
// No snapshot found. Create a new one.
// We want to snapshot using this VolumeSnapshotClass
@ -165,7 +158,7 @@ func (r *BackupSessionReconciler) snapshotVolume(volume corev1.Volume) (*volumes
volumeSnapshot = volumesnapshotv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Namespace: r.Namespace,
Name: volumeSnapshotName,
Name: SNAPSHOT_PREFIX + pv.Name,
},
Spec: volumesnapshotv1.VolumeSnapshotSpec{
VolumeSnapshotClassName: &volumeSnapshotClass.Name,
@ -200,79 +193,35 @@ func (r *BackupSessionReconciler) snapshotVolume(volume corev1.Volume) (*volumes
return nil, nil
}
func (r *BackupSessionReconciler) createVolumeFromSnapshot(vs *volumesnapshotv1.VolumeSnapshot) (backupPVCName string, err error) {
backupPVCName = strings.Replace(vs.Name, "vs", "bak", 1)
backupPVC := corev1.PersistentVolumeClaim{}
if err = r.Get(r.Context, client.ObjectKey{
Namespace: r.Namespace,
Name: backupPVCName,
}, &backupPVC); errors.IsNotFound(err) {
// The Volume does not exist. Create it.
pv := corev1.PersistentVolume{}
pvName, _ := strings.CutPrefix(vs.Name, strings.Join([]string{"vs", r.Name}, "-"))
if err = r.Get(r.Context, client.ObjectKey{
Name: pvName,
}, &pv); err != nil {
r.Log.Error(err, "unable to find pv", "pv", pvName)
return
}
backupPVC = corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: r.Namespace,
Name: backupPVCName,
},
Spec: corev1.PersistentVolumeClaimSpec{
StorageClassName: &pv.Spec.StorageClassName,
//AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany},
AccessModes: pv.Spec.AccessModes,
DataSource: &corev1.TypedLocalObjectReference{
APIGroup: func() *string { s := "snapshot.storage.k8s.io"; return &s }(),
Kind: "VolumeSnapshot",
Name: vs.Name,
},
},
}
if err = r.Create(r.Context, &backupPVC); err != nil {
r.Log.Error(err, "unable to create backup PVC", "backupPVC", backupPVC)
return
}
}
if err != nil {
r.Log.Error(err, "something went very wrong here")
}
return
func (r *BackupSessionReconciler) createVolumeFromSnapshot(vs *volumesnapshotv1.VolumeSnapshot) {
}
func (r *BackupSessionReconciler) snapshotVolumes(vms []corev1.VolumeMount, podSpec *corev1.PodSpec) (err error) {
// We snapshot/check all the volumes. If at least one of the snapshot is not ready to use. We reschedule.
for _, vm := range vms {
for i, volume := range podSpec.Volumes {
for _, volume := range podSpec.Volumes {
if vm.Name == volume.Name {
var vs *volumesnapshotv1.VolumeSnapshot
vs, err = r.snapshotVolume(volume)
if IsNotReadyToUse(err) {
defer func() {
err = &NotReadyToUseError{}
}()
continue
}
if err != nil {
return
switch err.(type) {
case *NotReadyToUseError:
defer func() {
err = &NotReadyToUseError{}
}()
default:
return
}
}
if vs != nil {
backupPVCName, err := r.createVolumeFromSnapshot(vs)
if err != nil {
r.Log.Error(err, "unable to create volume from snapshot", "vs", vs)
return err
}
podSpec.Volumes[i].VolumeSource.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: backupPVCName,
ReadOnly: true,
}
// The snapshot and the volume will be deleted by the Job when the backup is over
r.createVolumeFromSnapshot(vs)
}
}
}
}
return
}
func (r *BackupSessionReconciler) deleteVolumeSnapshots(target formolv1alpha1.Target) error {
return nil
}

View File

@ -19,8 +19,6 @@ type RestoreSessionReconciler struct {
func (r *RestoreSessionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.Log = log.FromContext(ctx)
r.Context = ctx
r.Namespace = req.NamespacedName.Namespace
r.Name = req.NamespacedName.Name
restoreSession := formolv1alpha1.RestoreSession{}
err := r.Get(r.Context, req.NamespacedName, &restoreSession)
@ -51,6 +49,7 @@ func (r *RestoreSessionReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
return ctrl.Result{}, err
}
r.Namespace = backupConf.Namespace
r.backupConf = backupConf
// we don't want a copy because we will modify and update it.

View File

@ -27,7 +27,6 @@ type Session struct {
Scheme *runtime.Scheme
context.Context
Namespace string
Name string
}
const (

2
formol

@ -1 +1 @@
Subproject commit 8975f77e5858ee167508ef0359c3b9d6cbaba6ee
Subproject commit 61f45a79404e1f71d9f7661d295d6ac3cd07dd8c

View File

@ -22,10 +22,6 @@ import (
"time"
)
const (
BACKUPSESSION_PREFIX = "bs"
)
var (
session controllers.Session
)
@ -132,7 +128,7 @@ func CreateBackupSession(ref corev1.ObjectReference) {
backupSession := &formolv1alpha1.BackupSession{
ObjectMeta: metav1.ObjectMeta{
Name: strings.Join([]string{BACKUPSESSION_PREFIX, ref.Name, strconv.FormatInt(time.Now().Unix(), 10)}, "-"),
Name: strings.Join([]string{"backupsession", ref.Name, strconv.FormatInt(time.Now().Unix(), 10)}, "-"),
Namespace: ref.Namespace,
},
Spec: formolv1alpha1.BackupSessionSpec{