226 lines
8.5 KiB
Go
226 lines
8.5 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"github.com/go-logr/logr"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"os"
|
|
"regexp"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
"time"
|
|
|
|
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
|
|
"github.com/desmo999r/formolcli/pkg/restic"
|
|
formolcliutils "github.com/desmo999r/formolcli/pkg/utils"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
// BackupSessionReconciler reconciles a BackupSession object
|
|
type BackupSessionReconciler struct {
|
|
client.Client
|
|
Log logr.Logger
|
|
Scheme *runtime.Scheme
|
|
}
|
|
|
|
var _ reconcile.Reconciler = &BackupSessionReconciler{}
|
|
|
|
func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
|
|
time.Sleep(2 * time.Second)
|
|
log := r.Log.WithValues("backupsession", req.NamespacedName)
|
|
|
|
// your logic here
|
|
backupSession := &formolv1alpha1.BackupSession{}
|
|
if err := r.Get(ctx, req.NamespacedName, backupSession); err != nil {
|
|
log.Error(err, "unable to get backupsession")
|
|
return reconcile.Result{}, client.IgnoreNotFound(err)
|
|
}
|
|
backupConf := &formolv1alpha1.BackupConfiguration{}
|
|
if err := r.Get(ctx, client.ObjectKey{
|
|
Namespace: backupSession.Namespace,
|
|
Name: backupSession.Spec.Ref.Name,
|
|
}, backupConf); err != nil {
|
|
log.Error(err, "unable to get backupConfiguration")
|
|
return reconcile.Result{}, client.IgnoreNotFound(err)
|
|
}
|
|
|
|
deploymentName := os.Getenv(formolv1alpha1.TARGET_NAME)
|
|
for _, target := range backupConf.Spec.Targets {
|
|
switch target.Kind {
|
|
case formolv1alpha1.SidecarKind:
|
|
if target.Name == deploymentName {
|
|
// We are involved in that Backup, let's see if it's our turn
|
|
status := &(backupSession.Status.Targets[len(backupSession.Status.Targets)-1])
|
|
if status.Name == deploymentName {
|
|
log.V(0).Info("It's for us!", "target", target)
|
|
switch status.SessionState {
|
|
case formolv1alpha1.New:
|
|
log.V(0).Info("New session, move to Initializing state")
|
|
status.SessionState = formolv1alpha1.Init
|
|
if err := r.Status().Update(ctx, backupSession); err != nil {
|
|
log.Error(err, "unable to update backupsession status")
|
|
return reconcile.Result{}, err
|
|
}
|
|
case formolv1alpha1.Init:
|
|
log.V(0).Info("Start to run the backup initializing steps if any")
|
|
result := formolv1alpha1.Running
|
|
for _, step := range target.Steps {
|
|
if step.Finalize != nil && *step.Finalize == true {
|
|
continue
|
|
}
|
|
function := &formolv1alpha1.Function{}
|
|
if err := r.Get(ctx, client.ObjectKey{
|
|
Name: step.Name,
|
|
Namespace: backupConf.Namespace,
|
|
}, function); err != nil {
|
|
log.Error(err, "unable to get function", "function", step.Name)
|
|
return reconcile.Result{}, err
|
|
}
|
|
// TODO: check the command arguments for $(VAR_NAME) arguments. If some are found, try to expand them from
|
|
// the Function.Spec EnvFrom and Env in that order
|
|
pattern := regexp.MustCompile(`^\$\((\w+)\)$`)
|
|
for i, arg := range function.Spec.Command[1:] {
|
|
i++
|
|
if match, _ := regexp.MatchString(`^\$\$`, arg); match {
|
|
continue
|
|
}
|
|
if pattern.MatchString(arg) {
|
|
arg = pattern.ReplaceAllString(arg, "$1")
|
|
// TODO: Find arg in EnvFrom key and replace it by the value in Command[i]
|
|
for _, envFrom := range function.Spec.EnvFrom {
|
|
if envFrom.SecretRef != nil {
|
|
secret := &corev1.Secret{}
|
|
if err := r.Get(ctx, client.ObjectKey{
|
|
Name: envFrom.SecretRef.Name,
|
|
Namespace: backupConf.Namespace,
|
|
}, secret); err != nil {
|
|
log.Error(err, "unable to get secret", "secret", envFrom.SecretRef.Name)
|
|
return reconcile.Result{}, err
|
|
}
|
|
if val, ok := secret.Data[arg]; ok {
|
|
log.V(1).Info("Found EnvFrom value for arg", "arg", arg)
|
|
function.Spec.Command[i] = string(val)
|
|
}
|
|
|
|
}
|
|
if envFrom.ConfigMapRef != nil {
|
|
configMap := &corev1.ConfigMap{}
|
|
if err := r.Get(ctx, client.ObjectKey{
|
|
Name: envFrom.ConfigMapRef.Name,
|
|
Namespace: backupConf.Namespace,
|
|
}, configMap); err != nil {
|
|
log.Error(err, "unable to get configMap", "configMap", envFrom.ConfigMapRef.Name)
|
|
return reconcile.Result{}, err
|
|
}
|
|
if val, ok := configMap.Data[arg]; ok {
|
|
log.V(1).Info("Found EnvFrom value for arg", "arg", arg)
|
|
function.Spec.Command[i] = val
|
|
}
|
|
}
|
|
}
|
|
for _, env := range function.Spec.Env {
|
|
if env.Name == arg {
|
|
if env.Value == "" {
|
|
if env.ValueFrom != nil {
|
|
if env.ValueFrom.SecretKeyRef != nil {
|
|
secret := &corev1.Secret{}
|
|
if err := r.Get(ctx, client.ObjectKey{
|
|
Name: env.ValueFrom.SecretKeyRef.Name,
|
|
Namespace: backupConf.Namespace,
|
|
}, secret); err != nil {
|
|
log.Error(err, "unable to get secret", "secret", env.ValueFrom.SecretKeyRef.Name)
|
|
return reconcile.Result{}, err
|
|
}
|
|
log.V(1).Info("Found Env value for arg", "arg", arg)
|
|
function.Spec.Command[i] = string(secret.Data[env.ValueFrom.SecretKeyRef.Key])
|
|
}
|
|
}
|
|
} else {
|
|
function.Spec.Command[i] = env.Value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if err := formolcliutils.RunChroot(target.ContainerName != "", function.Spec.Command[0], function.Spec.Command[1:]...); err != nil {
|
|
log.Error(err, "unable to run function command", "command", function.Spec.Command)
|
|
result = formolv1alpha1.Failure
|
|
break
|
|
}
|
|
}
|
|
status.SessionState = result
|
|
|
|
if err := r.Status().Update(ctx, backupSession); err != nil {
|
|
log.Error(err, "unable to update backupsession status")
|
|
return reconcile.Result{}, err
|
|
}
|
|
case formolv1alpha1.Running:
|
|
log.V(0).Info("Running session. Do the backup")
|
|
result := formolv1alpha1.Finalize
|
|
status.StartTime = &metav1.Time{Time: time.Now()}
|
|
output, err := restic.BackupPaths(backupSession.Name, target.Paths)
|
|
if err != nil {
|
|
log.Error(err, "unable to backup deployment", "output", string(output))
|
|
result = formolv1alpha1.Failure
|
|
} else {
|
|
snapshotId := restic.GetBackupResults(output)
|
|
status.SnapshotId = snapshotId
|
|
status.Duration = &metav1.Duration{Duration: time.Now().Sub(status.StartTime.Time)}
|
|
}
|
|
status.SessionState = 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 reconcile.Result{}, err
|
|
}
|
|
case formolv1alpha1.Finalize:
|
|
log.V(0).Info("Start to run the backup finalizing steps if any")
|
|
result := formolv1alpha1.Success
|
|
for _, step := range target.Steps {
|
|
if step.Finalize != nil && *step.Finalize == true {
|
|
function := &formolv1alpha1.Function{}
|
|
if err := r.Get(ctx, client.ObjectKey{
|
|
Name: step.Name,
|
|
Namespace: backupConf.Namespace,
|
|
}, function); err != nil {
|
|
log.Error(err, "unable to get function", "function", step.Name)
|
|
return reconcile.Result{}, err
|
|
}
|
|
if err := formolcliutils.RunChroot(target.ContainerName != "", function.Spec.Command[0], function.Spec.Command[1:]...); err != nil {
|
|
log.Error(err, "unable to run function command", "command", function.Spec.Command)
|
|
result = formolv1alpha1.Failure
|
|
break
|
|
}
|
|
}
|
|
}
|
|
status.SessionState = result
|
|
|
|
if err := r.Status().Update(ctx, backupSession); err != nil {
|
|
log.Error(err, "unable to update backupsession status")
|
|
return reconcile.Result{}, err
|
|
}
|
|
|
|
case formolv1alpha1.Success, formolv1alpha1.Failure:
|
|
log.V(0).Info("Backup is over")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return reconcile.Result{}, nil
|
|
}
|
|
|
|
func (r *BackupSessionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|
return ctrl.NewControllerManagedBy(mgr).
|
|
For(&formolv1alpha1.BackupSession{}).
|
|
WithEventFilter(predicate.Funcs{
|
|
CreateFunc: func(e event.CreateEvent) bool { return false },
|
|
DeleteFunc: func(e event.DeleteEvent) bool { return false },
|
|
}).
|
|
Complete(r)
|
|
}
|