Compare commits
3 Commits
bafffdb3d2
...
84347314fd
| Author | SHA1 | Date | |
|---|---|---|---|
| 84347314fd | |||
| ba80d2c83c | |||
| 4e1009fae1 |
@ -1,5 +1,5 @@
|
|||||||
# Build a small image
|
# Build a small image
|
||||||
FROM arm32v7/alpine:3.12
|
FROM arm64v8/alpine:3.14
|
||||||
|
|
||||||
RUN apk add --no-cache su-exec restic postgresql-client
|
RUN apk add --no-cache su-exec restic postgresql-client
|
||||||
COPY bin/formolcli /usr/local/bin
|
COPY bin/formolcli /usr/local/bin
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -3,7 +3,7 @@
|
|||||||
IMG ?= desmo999r/formolcli:latest
|
IMG ?= desmo999r/formolcli:latest
|
||||||
|
|
||||||
formolcli: fmt vet
|
formolcli: fmt vet
|
||||||
GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o bin/formolcli main.go
|
GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/formolcli main.go
|
||||||
|
|
||||||
test: fmt vet
|
test: fmt vet
|
||||||
go test ./... -coverprofile cover.out
|
go test ./... -coverprofile cover.out
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
|
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
|
||||||
"github.com/desmo999r/formolcli/pkg/restic"
|
"github.com/desmo999r/formolcli/pkg/restic"
|
||||||
formolcliutils "github.com/desmo999r/formolcli/pkg/utils"
|
formolcliutils "github.com/desmo999r/formolcli/pkg/utils"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,7 +80,73 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
log.Error(err, "unable to get function", "function", step.Name)
|
log.Error(err, "unable to get function", "function", step.Name)
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
if err := formolcliutils.RunChroot(function.Spec.Command[0], function.Spec.Command[1:]...); err != nil {
|
// 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)
|
log.Error(err, "unable to run function command", "command", function.Spec.Command)
|
||||||
result = formolv1alpha1.Failure
|
result = formolv1alpha1.Failure
|
||||||
break
|
break
|
||||||
@ -122,7 +190,7 @@ func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req reconcile.R
|
|||||||
log.Error(err, "unable to get function", "function", step.Name)
|
log.Error(err, "unable to get function", "function", step.Name)
|
||||||
return reconcile.Result{}, err
|
return reconcile.Result{}, err
|
||||||
}
|
}
|
||||||
if err := formolcliutils.RunChroot(function.Spec.Command[0], function.Spec.Command[1:]...); err != nil {
|
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)
|
log.Error(err, "unable to run function command", "command", function.Spec.Command)
|
||||||
result = formolv1alpha1.Failure
|
result = formolv1alpha1.Failure
|
||||||
break
|
break
|
||||||
|
|||||||
@ -124,7 +124,7 @@ func (r *RestoreSessionReconciler) Reconcile(ctx context.Context, req reconcile.
|
|||||||
}
|
}
|
||||||
if len(restoreFunction.Spec.Command) > 1 {
|
if len(restoreFunction.Spec.Command) > 1 {
|
||||||
log.V(0).Info("Running the restore function", "name", restoreFunction.Name, "command", restoreFunction.Spec.Command)
|
log.V(0).Info("Running the restore function", "name", restoreFunction.Name, "command", restoreFunction.Spec.Command)
|
||||||
if err := formolcliutils.RunChroot(restoreFunction.Spec.Command[0], restoreFunction.Spec.Command[1:]...); err != nil {
|
if err := formolcliutils.RunChroot(currentTarget.ContainerName != "", restoreFunction.Spec.Command[0], restoreFunction.Spec.Command[1:]...); err != nil {
|
||||||
log.Error(err, "unable to run function command", "command", restoreFunction.Spec.Command)
|
log.Error(err, "unable to run function command", "command", restoreFunction.Spec.Command)
|
||||||
result = formolv1alpha1.Failure
|
result = formolv1alpha1.Failure
|
||||||
break
|
break
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
|
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
"github.com/go-logr/zapr"
|
"github.com/go-logr/zapr"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -19,16 +21,6 @@ func init() {
|
|||||||
logger = zapr.NewLogger(zapLog)
|
logger = zapr.NewLogger(zapLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunHooks(hooks []formolv1alpha1.Hook) error {
|
|
||||||
for _, hook := range hooks {
|
|
||||||
err := RunChroot(hook.Cmd, hook.Args...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Run(runCmd string, args []string) error {
|
func Run(runCmd string, args []string) error {
|
||||||
log := logger.WithValues("Run", runCmd, "Args", args)
|
log := logger.WithValues("Run", runCmd, "Args", args)
|
||||||
cmd := exec.Command(runCmd, args...)
|
cmd := exec.Command(runCmd, args...)
|
||||||
@ -41,9 +33,10 @@ func Run(runCmd string, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunChroot(runCmd string, args ...string) error {
|
func RunChroot(lookForTag bool, runCmd string, args ...string) error {
|
||||||
log := logger.WithValues("RunChroot", runCmd, "Args", args)
|
log := logger.WithValues("RunChroot", runCmd, "Args", args)
|
||||||
root := regexp.MustCompile(`/proc/[0-9]+/root`)
|
root := regexp.MustCompile(`/proc/[0-9]+/root`)
|
||||||
|
env := regexp.MustCompile(`/proc/[0-9]+/environ`)
|
||||||
pid := strconv.Itoa(os.Getpid())
|
pid := strconv.Itoa(os.Getpid())
|
||||||
skip := false
|
skip := false
|
||||||
if err := filepath.Walk("/proc", func(path string, info os.FileInfo, err error) error {
|
if err := filepath.Walk("/proc", func(path string, info os.FileInfo, err error) error {
|
||||||
@ -56,10 +49,34 @@ func RunChroot(runCmd string, args ...string) error {
|
|||||||
if info.IsDir() && (info.Name() == "1" || info.Name() == pid) {
|
if info.IsDir() && (info.Name() == "1" || info.Name() == pid) {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
if lookForTag && env.MatchString(path) {
|
||||||
|
log.V(0).Info("Looking for tag", "file", path)
|
||||||
|
content, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
var matched bool
|
||||||
|
for _, envVar := range bytes.Split(content, []byte{'\000'}) {
|
||||||
|
matched, err = regexp.Match(formolv1alpha1.TARGETCONTAINER_TAG, envVar)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "cannot regexp")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
log.V(0).Info("Found the target tag", "file", path)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched == false {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
}
|
||||||
if root.MatchString(path) {
|
if root.MatchString(path) {
|
||||||
if _, err := filepath.EvalSymlinks(path); err != nil {
|
if _, err := filepath.EvalSymlinks(path); err != nil {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
log.V(0).Info("running chroot in", "path", path)
|
||||||
cmd := exec.Command("chroot", append([]string{path, runCmd}, args...)...)
|
cmd := exec.Command("chroot", append([]string{path, runCmd}, args...)...)
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
log.V(0).Info("result", "output", string(output))
|
log.V(0).Info("result", "output", string(output))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user