From cb567c241d567bb233ee4110b6e73a7dbe4106b7 Mon Sep 17 00:00:00 2001 From: mschuller <74669876+mschuller@users.noreply.github.com> Date: Mon, 19 Feb 2024 08:10:19 +0100 Subject: [PATCH] add support for localfs provider (#34) --- Makefile | 4 + README.md | 1 + cmd/internal/database/localfs/localfs.go | 79 ++++++++ cmd/internal/utils/files.go | 35 ++++ cmd/main.go | 8 +- deploy/localfs-local.yaml | 124 +++++++++++++ go.mod | 1 + go.sum | 5 + integration/localfs_test.go | 36 ++++ integration/main_test.go | 40 ++++ pkg/generate/examples/dump.go | 5 + pkg/generate/examples/examples/localfs.go | 216 ++++++++++++++++++++++ 12 files changed, 553 insertions(+), 1 deletion(-) create mode 100644 cmd/internal/database/localfs/localfs.go create mode 100644 deploy/localfs-local.yaml create mode 100644 integration/localfs_test.go create mode 100644 pkg/generate/examples/examples/localfs.go diff --git a/Makefile b/Makefile index e09f437..6092a81 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,10 @@ start-meilisearch: start-redis: $(MAKE) start DB=redis +.PHONY: start-localfs +start-localfs: + $(MAKE) start DB=localfs + .PHONY: start start: kind-cluster-create kind --name backup-restore-sidecar load docker-image ghcr.io/metal-stack/backup-restore-sidecar:latest diff --git a/README.md b/README.md index cdc6007..44f450a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Probably, it does not make sense to use this project with large databases. Howev | meilisearch | >= 1.2.0 | alpha | ✅ | | redis | >= 6.0 | alpha | ❌ | | keydb | >= 6.0 | alpha | ❌ | +| localfs | | alpha | ❌ | Postgres also supports updates when using the TimescaleDB extension. Please consider the integration test for supported upgrade paths. diff --git a/cmd/internal/database/localfs/localfs.go b/cmd/internal/database/localfs/localfs.go new file mode 100644 index 0000000..b7c80a9 --- /dev/null +++ b/cmd/internal/database/localfs/localfs.go @@ -0,0 +1,79 @@ +package localfs + +import ( + "context" + "fmt" + "log/slog" + "os" + + "github.com/metal-stack/backup-restore-sidecar/cmd/internal/utils" + "github.com/metal-stack/backup-restore-sidecar/pkg/constants" +) + +type LocalFS struct { + datadir string + log *slog.Logger +} + +func New(log *slog.Logger, datadir string) *LocalFS { + return &LocalFS{ + datadir: datadir, + log: log, + } +} + +// Check if Datadir is empty +func (l *LocalFS) Check(ctx context.Context) (bool, error) { + empty, err := utils.IsEmpty(l.datadir) + if err != nil { + return false, err + } + if empty { + l.log.Info("data directory is empty") + return true, err + } + + return false, nil +} + +// put Datadir into constants.BackupDir directory +func (l *LocalFS) Backup(ctx context.Context) error { + if err := os.RemoveAll(constants.BackupDir); err != nil { + return fmt.Errorf("could not clean backup directory: %w", err) + } + + if err := os.MkdirAll(constants.BackupDir, 0777); err != nil { + return fmt.Errorf("could not create backup directory: %w", err) + } + + if err := utils.CopyFS(constants.BackupDir, os.DirFS(l.datadir)); err != nil { + return fmt.Errorf("could not copy contents: %w", err) + } + + l.log.Debug("Sucessfully took backup of localfs") + return nil +} + +// get data from constants.RestoreDir +func (l *LocalFS) Recover(ctx context.Context) error { + if err := utils.RemoveContents(l.datadir); err != nil { + return fmt.Errorf("Could not cleanup Datadir: %w", err) + } + + if err := utils.CopyFS(l.datadir, os.DirFS(constants.RestoreDir)); err != nil { + return fmt.Errorf("could not copy contents: %w", err) + } + + l.log.Debug("Successfully restored localfs") + return nil +} + +func (l *LocalFS) Probe(ctx context.Context) error { + //Nothing to do, not a real Database + return nil +} + +func (_ *LocalFS) Upgrade(ctx context.Context) error { + // Nothing to do here + return nil +} diff --git a/cmd/internal/utils/files.go b/cmd/internal/utils/files.go index 102680e..4784e44 100644 --- a/cmd/internal/utils/files.go +++ b/cmd/internal/utils/files.go @@ -2,6 +2,7 @@ package utils import ( "errors" + "fmt" "io" "io/fs" "os" @@ -80,3 +81,37 @@ func IsCommandPresent(command string) bool { return true } + +// TODO: replace once go-1.23 is released +func CopyFS(dir string, fsys fs.FS) error { + return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, error error) error { + targ := filepath.Join(dir, filepath.FromSlash(path)) + if d.IsDir() { + if err := os.MkdirAll(targ, 0777); err != nil { + return err + } + return nil + } + r, err := fsys.Open(path) + if err != nil { + return err + } + defer r.Close() + info, err := r.Stat() + if err != nil { + return err + } + w, err := os.OpenFile(targ, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666|info.Mode()&0777) + if err != nil { + return err + } + if _, err := io.Copy(w, r); err != nil { + w.Close() + return fmt.Errorf("copying %s: %w", path, err) + } + if err := w.Close(); err != nil { + return err + } + return nil + }) +} diff --git a/cmd/main.go b/cmd/main.go index 908741f..0474072 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,6 +18,7 @@ import ( "github.com/metal-stack/backup-restore-sidecar/cmd/internal/compress" "github.com/metal-stack/backup-restore-sidecar/cmd/internal/database" "github.com/metal-stack/backup-restore-sidecar/cmd/internal/database/etcd" + "github.com/metal-stack/backup-restore-sidecar/cmd/internal/database/localfs" "github.com/metal-stack/backup-restore-sidecar/cmd/internal/database/meilisearch" "github.com/metal-stack/backup-restore-sidecar/cmd/internal/database/postgres" "github.com/metal-stack/backup-restore-sidecar/cmd/internal/database/redis" @@ -273,7 +274,7 @@ func init() { rootCmd.AddCommand(startCmd, waitCmd, restoreCmd, createBackupCmd) rootCmd.PersistentFlags().StringP(logLevelFlg, "", "info", "sets the application log level") - rootCmd.PersistentFlags().StringP(databaseFlg, "", "", "the kind of the database [postgres|rethinkdb|etcd|meilisearch|redis|keydb]") + rootCmd.PersistentFlags().StringP(databaseFlg, "", "", "the kind of the database [postgres|rethinkdb|etcd|meilisearch|redis|keydb|localfs]") rootCmd.PersistentFlags().StringP(databaseDatadirFlg, "", "", "the directory where the database stores its data in") err := viper.BindPFlags(rootCmd.PersistentFlags()) @@ -459,6 +460,11 @@ func initDatabase() error { if err != nil { return err } + case "localfs": + db = localfs.New( + logger.WithGroup("localfs"), + datadir, + ) default: return fmt.Errorf("unsupported database type: %s", dbString) } diff --git a/deploy/localfs-local.yaml b/deploy/localfs-local.yaml new file mode 100644 index 0000000..103395e --- /dev/null +++ b/deploy/localfs-local.yaml @@ -0,0 +1,124 @@ +# THESE EXAMPLES ARE GENERATED! +# Use them as a template for your deployment, but do not commit manual changes to these files. +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + creationTimestamp: null + labels: + app: localfs + name: localfs +spec: + replicas: 1 + selector: + matchLabels: + app: localfs + serviceName: localfs + template: + metadata: + creationTimestamp: null + labels: + app: localfs + spec: + containers: + - command: + - backup-restore-sidecar + - wait + image: alpine:3.19 + name: localfs + resources: {} + volumeMounts: + - mountPath: /data + name: data + - mountPath: /usr/local/bin/backup-restore-sidecar + name: bin-provision + subPath: backup-restore-sidecar + - mountPath: /etc/backup-restore-sidecar + name: backup-restore-sidecar-config + - command: + - backup-restore-sidecar + - start + - --log-level=debug + image: alpine:3.19 + name: backup-restore-sidecar + ports: + - containerPort: 8000 + name: grpc + resources: {} + volumeMounts: + - mountPath: /backup + name: backup + - mountPath: /data + name: data + - mountPath: /etc/backup-restore-sidecar + name: backup-restore-sidecar-config + - mountPath: /usr/local/bin/backup-restore-sidecar + name: bin-provision + subPath: backup-restore-sidecar + initContainers: + - command: + - cp + - /backup-restore-sidecar + - /bin-provision + image: ghcr.io/metal-stack/backup-restore-sidecar:latest + imagePullPolicy: IfNotPresent + name: backup-restore-sidecar-provider + resources: {} + volumeMounts: + - mountPath: /bin-provision + name: bin-provision + volumes: + - name: data + persistentVolumeClaim: + claimName: data + - name: backup + persistentVolumeClaim: + claimName: backup + - configMap: + name: backup-restore-sidecar-config-localfs + name: backup-restore-sidecar-config + - emptyDir: {} + name: bin-provision + updateStrategy: {} + volumeClaimTemplates: + - metadata: + creationTimestamp: null + name: data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + status: {} + - metadata: + creationTimestamp: null + name: backup + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + status: {} +status: + availableReplicas: 0 + replicas: 0 +--- +apiVersion: v1 +data: + config.yaml: | + --- + bind-addr: 0.0.0.0 + db: localfs + db-data-directory: /data/ + backup-provider: local + backup-cron-schedule: "*/1 * * * *" + object-prefix: localfs-test + redis-addr: localhost:6379 + post-exec-cmds: + - tail -f /etc/hosts +kind: ConfigMap +metadata: + creationTimestamp: null + name: backup-restore-sidecar-config-localfs diff --git a/go.mod b/go.mod index 52ce72d..9b13063 100644 --- a/go.mod +++ b/go.mod @@ -97,6 +97,7 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect diff --git a/go.sum b/go.sum index 0b3c0f9..5e84959 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/aws/aws-sdk-go v1.45.7 h1:k4QsvWZhm8409TYeRuTV1P6+j3lLKoe+giFA/j3VAps= @@ -179,6 +181,7 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= @@ -246,6 +249,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= diff --git a/integration/localfs_test.go b/integration/localfs_test.go new file mode 100644 index 0000000..15fd0b2 --- /dev/null +++ b/integration/localfs_test.go @@ -0,0 +1,36 @@ +//go:build integration + +package integration_test + +import ( + "context" + "testing" + + "github.com/metal-stack/backup-restore-sidecar/pkg/generate/examples/examples" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Localfs_Restore(t *testing.T) { + restoreFlow(t, &flowSpec{ + databaseType: examples.Localfs, + sts: examples.LocalfsSts, + backingResources: examples.LocalfsBackingResources, + addTestData: addLocalfsTestData, + verifyTestData: verifyLocalfsTestData, + }) +} + +func addLocalfsTestData(t *testing.T, ctx context.Context) { + namespace := namespaceName(t) + _, err := execCommand(ctx, "localfs-0", namespace, "backup-restore-sidecar", []string{"sh", "-c", "echo 'I am precious' > /data/test.txt"}) + require.NoError(t, err) +} + +func verifyLocalfsTestData(t *testing.T, ctx context.Context) { + namespace := namespaceName(t) + resp, err := execCommand(ctx, "localfs-0", namespace, "backup-restore-sidecar", []string{"cat", "/data/test.txt"}) + require.NoError(t, err) + + assert.Equal(t, "I am precious", resp) +} diff --git a/integration/main_test.go b/integration/main_test.go index ad0bc89..9d3485b 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -3,6 +3,7 @@ package integration_test import ( + "bytes" "context" "errors" "fmt" @@ -23,7 +24,10 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/remotecommand" ) type flowSpec struct { @@ -396,3 +400,39 @@ func waitUntilNotFound(ctx context.Context, obj client.Object) error { return fmt.Errorf("resource is still running: %s", obj.GetName()) }, retry.Context(ctx), retry.Attempts(0), retry.MaxDelay(2*time.Second)) } + +func execCommand(ctx context.Context, podName string, namespace string, containerName string, cmd []string) (string, error) { + var stdout, stderr bytes.Buffer + client, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return "", err + } + + req := client.CoreV1().RESTClient().Post().Resource("pods").Name(podName).Namespace(namespace).SubResource("exec") + option := &corev1.PodExecOptions{ + Command: cmd, + Stdin: false, + Stdout: true, + Stderr: true, + TTY: true, + Container: containerName, + } + + req.VersionedParams( + option, + scheme.ParameterCodec, + ) + exec, err := remotecommand.NewSPDYExecutor(restConfig, "POST", req.URL()) + if err != nil { + return "", err + } + err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{ + Stdin: nil, + Stdout: &stdout, + Stderr: &stderr, + }) + if err != nil { + return "", err + } + return strings.TrimSpace(stdout.String()), nil +} diff --git a/pkg/generate/examples/dump.go b/pkg/generate/examples/dump.go index c51f708..6bb3f00 100644 --- a/pkg/generate/examples/dump.go +++ b/pkg/generate/examples/dump.go @@ -49,6 +49,11 @@ func main() { sts: examples.KeyDBSts, backing: examples.KeyDBBackingResources, }, + { + db: examples.Localfs, + sts: examples.LocalfsSts, + backing: examples.LocalfsBackingResources, + }, } { err := dumpToExamples(localExample.db+"-local.yaml", append([]client.Object{localExample.sts("default")}, localExample.backing("default")...)...) if err != nil { diff --git a/pkg/generate/examples/examples/localfs.go b/pkg/generate/examples/examples/localfs.go new file mode 100644 index 0000000..31c48cd --- /dev/null +++ b/pkg/generate/examples/examples/localfs.go @@ -0,0 +1,216 @@ +package examples + +import ( + "github.com/metal-stack/backup-restore-sidecar/pkg/constants" + "github.com/metal-stack/metal-lib/pkg/pointer" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + Localfs = "localfs" + LocalfsContainerImage = "alpine:3.19" +) + +func LocalfsSts(namespace string) *appsv1.StatefulSet { + return &appsv1.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "StatefulSet", + APIVersion: appsv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "localfs", + Namespace: namespace, + Labels: map[string]string{ + "app": "localfs", + }, + }, + Spec: appsv1.StatefulSetSpec{ + ServiceName: "localfs", + Replicas: pointer.Pointer(int32(1)), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "localfs", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "localfs", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + Containers: []corev1.Container{ + { + Name: "localfs", + Image: LocalfsContainerImage, + Command: []string{"backup-restore-sidecar", "wait"}, + + VolumeMounts: []corev1.VolumeMount{ + { + Name: "data", + MountPath: "/data", + }, + { + Name: "bin-provision", + SubPath: "backup-restore-sidecar", + MountPath: "/usr/local/bin/backup-restore-sidecar", + }, + { + Name: "backup-restore-sidecar-config", + MountPath: "/etc/backup-restore-sidecar", + }, + }, + }, + { + Name: "backup-restore-sidecar", + Image: LocalfsContainerImage, + Command: []string{"backup-restore-sidecar", "start", "--log-level=debug"}, + Ports: []corev1.ContainerPort{ + { + Name: "grpc", + ContainerPort: 8000, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "backup", + MountPath: constants.SidecarBaseDir, + }, + { + Name: "data", + MountPath: "/data", + }, + { + Name: "backup-restore-sidecar-config", + MountPath: "/etc/backup-restore-sidecar", + }, + { + Name: "bin-provision", + SubPath: "backup-restore-sidecar", + MountPath: "/usr/local/bin/backup-restore-sidecar", + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "backup-restore-sidecar-provider", + Image: backupRestoreSidecarContainerImage, + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{ + "cp", + "/backup-restore-sidecar", + "/bin-provision", + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "bin-provision", + MountPath: "/bin-provision", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "data", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "data", + }, + }, + }, + { + Name: "backup", + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: "backup", + }, + }, + }, + { + Name: "backup-restore-sidecar-config", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "backup-restore-sidecar-config-localfs", + }, + }, + }, + }, + { + Name: "bin-provision", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + }, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "data", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "backup", + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + }, + }, + }, + } +} + +func LocalfsBackingResources(namespace string) []client.Object { + return []client.Object{ + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: corev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "backup-restore-sidecar-config-localfs", + Namespace: namespace, + }, + Data: map[string]string{ + "config.yaml": `--- +bind-addr: 0.0.0.0 +db: localfs +db-data-directory: /data/ +backup-provider: local +backup-cron-schedule: "*/1 * * * *" +object-prefix: localfs-test +redis-addr: localhost:6379 +post-exec-cmds: +- tail -f /etc/hosts +`, + }, + }, + } +}