diff --git a/api/v1/snapshot_types.go b/api/v1/snapshot_types.go index cd017201a..e5f019daf 100644 --- a/api/v1/snapshot_types.go +++ b/api/v1/snapshot_types.go @@ -22,6 +22,9 @@ type SnapshotStatus struct { // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Date taken",type="string",JSONPath=`.spec.date`,description="Date when snapshot was taken" +// +kubebuilder:printcolumn:name="Paths",type="string",JSONPath=`.spec.paths[*]`,description="Snapshot's paths" +// +kubebuilder:printcolumn:name="Repository",type="string",JSONPath=`.spec.repository`,description="Repository Url" // Snapshot is the Schema for the snapshots API type Snapshot struct { diff --git a/cli/restore/helpers.go b/cli/restore/helpers.go new file mode 100644 index 000000000..90045f915 --- /dev/null +++ b/cli/restore/helpers.go @@ -0,0 +1,16 @@ +package restore + +import ( + "math/rand" + "time" +) + +func RandomStringGenerator(n int) string { + var characters = []rune("abcdefghijklmnopqrstuvwxyz1234567890") + rand.Seed(time.Now().UnixNano()) + b := make([]rune, n) + for i := range b { + b[i] = characters[rand.Intn(len(characters))] + } + return string(b) +} diff --git a/cli/restore/restore.go b/cli/restore/restore.go new file mode 100644 index 000000000..41b7f900a --- /dev/null +++ b/cli/restore/restore.go @@ -0,0 +1,46 @@ +package restore + +var ( + Cfg = &RestoreConfig{} +) + +type RestoreConfig struct { + // spec.restoreMethod.folder.claimName + ClaimName string + Kubeconfig string + Namespace string + // metadata.name + RestoreName string + // spec.podSecurityContext.runAsUser + RunAsUser int64 + // one of restore methods s3 || pvc + RestoreMethod string + // spec.snapshot + Snapshot string + // spec.backend.repoPasswordSecretRef.name + SecretRef string + // spec.backend.repoPasswordSecretRef.key + SecretRefKey string + + // spec.backend.s3.endpoint + S3Endpoint string + // spec.backend.s3.bucket + S3Bucket string + // spec.backend.s3.accessKeyIDSecretRef.name && spec.backend.s3.secretAccessKeySecretRef.name + S3SecretRef string + // spec.backend.s3.accessKeyIDSecretRef.key + S3SecretRefUsernameKey string + // spec.backend.s3.secretAccessKeySecretRef.key + S3SecretRefPasswordKey string + + // spec.restoreMethod.s3.endpoint + RestoreToS3Endpoint string + // spec.restoreMethod.s3.bucket + RestoreToS3Bucket string + // spec.restoreMethod.s3.accessKeyIDSecretRef.name && spec.restoreMethod.s3.secretAccessKeySecretRef.name + RestoreToS3Secret string + // spec.restoreMethod.s3.accessKeyIDSecretRef.name + RestoreToS3SecretUsernameKey string + // spec.restoreMethod.s3.secretAccessKeySecretRef.name + RestoreToS3SecretPasswordKey string +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 000000000..1406f4ac9 --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,377 @@ +package cli + +import ( + "context" + "encoding/json" + "fmt" + "log" + "strings" + + v1 "github.com/k8up-io/k8up/v2/api/v1" + "github.com/k8up-io/k8up/v2/cli/restore" + "github.com/k8up-io/k8up/v2/cmd" + "github.com/urfave/cli/v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +var ( + Command = &cli.Command{ + Name: "cli", + Description: "CLI commands that can be executed everywhere, currently just restore is supported", + Subcommands: []*cli.Command{ + { + Name: "restore", + Action: RunRestore, + Flags: []cli.Flag{ + &cli.StringFlag{ + Destination: &restore.Cfg.Snapshot, + Required: false, + Name: "snapshot", + Value: "latest", + Usage: "Optional ; ID of the snapshot `kubectl get snapshots`, if left empty 'latest' will be used, set it via cli or via env: ", + EnvVars: []string{ + "SNAPSHOT", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.SecretRef, + Required: true, + Name: "secretRef", + Usage: "Required ; Set secret name from which You want to take S3 credentials, via cli or via env: ", + EnvVars: []string{ + "SECRET_REF", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.S3Endpoint, + Required: true, + Name: "s3endpoint", + Usage: "Required ; Set s3endpoint from which backup will be taken, via cli or via env: ", + EnvVars: []string{ + "S3ENDPOINT", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.S3Bucket, + Required: true, + Name: "s3bucket", + Usage: "Required ; Set s3bucket from which backup will be taken, via cli or via env: ", + EnvVars: []string{ + "S3BUCKET", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.S3SecretRef, + Required: true, + Name: "s3secretRef", + Usage: "Required ; Set secret name, where S3 username & password are stored from which backup will be taken, via cli or via env: ", + EnvVars: []string{ + "S3SECRETREF", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreMethod, + Required: true, + Name: "restoreMethod", + Value: "pvc", + Usage: "Required ; Set restore method [ pvc|s3 ], via cli or via env: ", + Action: func(ctx *cli.Context, s string) error { + if s != "pvc" && s != "s3" { + return fmt.Errorf("--restoreMethod must be set to either 'pvc' or 's3'") + } + return nil + }, + EnvVars: []string{ + "RESTOREMETHOD", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.SecretRefKey, + Required: false, + Name: "secretRefKey", + Value: "password", + Usage: "Optional ; Set key name, where restic password is stored, via cli or via env: ", + EnvVars: []string{ + "SECRETREFKEY", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.ClaimName, + Required: false, + Name: "claimName", + Usage: "Required ; Set claimName field, via cli or via env: ", + EnvVars: []string{ + "CLAIMNAME", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.S3SecretRefUsernameKey, + Required: false, + Value: "username", + Name: "S3SecretRefUsernameKey", + Usage: "Optional ; Set S3SecretRefUsernameKey, key inside secret, under which S3 username is stored, via cli or via env: ", + EnvVars: []string{ + "S3SECRETREFUSERNAMEKEY", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.S3SecretRefPasswordKey, + Required: false, + Value: "password", + Name: "S3SecretRefPasswordKey", + Usage: "Optional ; Set S3SecretRefPasswordKey, key inside secret, under which Restic repo password is stored, via cli or via env: ", + EnvVars: []string{ + "S3SECRETREFPASSWORDKEY", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreName, + Required: false, + Name: "restoreName", + Usage: "Optional ; Set restoreName - metadata.Name field, if empty, k8up will generate name, via cli or via env: ", + EnvVars: []string{ + "RESTORENAME", + }, + }, + &cli.Int64Flag{ + Destination: &restore.Cfg.RunAsUser, + Required: false, + Name: "runAsUser", + Usage: "Optional ; Set user UID, via cli or via env: ", + EnvVars: []string{ + "RUNASUSER", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreToS3Endpoint, + Required: false, + Name: "restoreToS3Endpoint", + Usage: "Optional ; Set restore endpoint, only when using s3 restore method, via cli or via env: ", + EnvVars: []string{ + "RESTORETOS3ENDPOINT", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreToS3Bucket, + Required: false, + Name: "restoreToS3Bucket", + Usage: "Optional ; Set restore bucket, only when using s3 restore method, via cli or via env: ", + EnvVars: []string{ + "RESTORETOS3BUCKET", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreToS3Secret, + Required: false, + Name: "restoreToS3Secret", + Usage: "Optional ; Set restore Secret, only when using s3 restore method, expecting secret name containing key value pair with 'username' and 'password' keys, via cli or via env: ", + EnvVars: []string{ + "RESTORETOS3SECRET", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreToS3SecretUsernameKey, + Required: false, + Value: "username", + Name: "RestoreToS3SecretUsernameKey", + Usage: "Optional ; Set RestoreToS3SecretUsernameKey, key inside secret, under which S3 username is stored, via cli or via env: ", + EnvVars: []string{ + "RESTORETOS3SECRETUSERNAMEKEY", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.RestoreToS3SecretPasswordKey, + Required: false, + Value: "password", + Name: "RestoreToS3SecretPasswordKey", + Usage: "Optional ; Set RestoreToS3SecretPasswordKey, key inside secret, under which Restic repo password is stored, via cli or via env: ", + EnvVars: []string{ + "RESTORETOS3SECRETPASSWORDKEY", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.Namespace, + Required: false, + Value: "default", + Aliases: []string{"n"}, + Name: "namespace", + Usage: "Optional ; Set namespace in which You want to execute restore, via cli or via env: ", + EnvVars: []string{ + "NAMESPACE", + }, + }, + &cli.StringFlag{ + Destination: &restore.Cfg.Kubeconfig, + Required: false, + Value: "~/.kube/config", + Name: "kubeconfig", + Usage: "Optional ; Set kubeconfig to connect to cluster, via cli or via env:", + EnvVars: []string{ + "KUBECONFIG", + }, + }, + }, + }, + }, + } +) + +func RunRestore(ctx *cli.Context) error { + var restoreName, snapshot string + var s3, pvc v1.RestoreMethod + logger := cmd.AppLogger(ctx).WithName("cli-restore") + + // avoid name crashes + generate name if naming is not important for user + if restore.Cfg.RestoreName == "" { + restoreName = "cli-restore-" + restore.RandomStringGenerator(5) + log.Println("Creating restore with name: ", restoreName, " in napespace: ", restore.Cfg.Namespace) + } else { + restoreName = restore.Cfg.RestoreName + } + + kconfig, err := clientcmd.BuildConfigFromFlags("", restore.Cfg.Kubeconfig) + if err != nil { + logger.Error(err, "Failed to create KubeConfig") + return err + } + logger.Info("Found Kubeconfig", "KUBECONFIG", restore.Cfg.Kubeconfig) + + client, err := kubernetes.NewForConfig(kconfig) + if err != nil { + logger.Error(err, "Failed to create Kubernetes Client") + return err + } + + crd, err := client.RESTClient().Get().Namespace("default").AbsPath("/apis/k8up.io/v1/").Resource("snapshots").DoRaw(context.TODO()) + if err != nil { + logger.Error(err, "Failed to query Kubernetes api for snapshots, is it correct cluster and namespace? Is k8up installed correctly?") + return err + } + if restore.Cfg.Snapshot != "latest" { + + snpList := &v1.SnapshotList{} + + err = json.Unmarshal(crd, &snpList) + if err != nil { + logger.Error(err, "Failed to unmarshal Snapshots") + return err + } + // kubectl get snapshots returns 8 character values, while snapshots itself are much longer + // so it's simple enough to check prefix + for _, snap := range snpList.Items { + if strings.HasPrefix((*snap.Spec.ID), restore.Cfg.Snapshot) { + snapshot = (*snap.Spec.ID) + break + } + } + // nothing to do if there are no snapshots + if len(snapshot) == 0 { + logger.Error(err, "Snapshot ID wasn't found") + return err + } + logger.V(1).Info("Found correct snapshot", "SNAPSHOT", snapshot) + } + + s3 = v1.RestoreMethod{ + S3: &v1.S3Spec{ + Endpoint: restore.Cfg.RestoreToS3Endpoint, + Bucket: restore.Cfg.RestoreToS3Bucket, + AccessKeyIDSecretRef: &corev1.SecretKeySelector{ + Key: restore.Cfg.RestoreToS3SecretUsernameKey, + LocalObjectReference: corev1.LocalObjectReference{ + Name: restore.Cfg.RestoreToS3Secret, + }, + }, + SecretAccessKeySecretRef: &corev1.SecretKeySelector{ + Key: restore.Cfg.RestoreToS3SecretPasswordKey, + LocalObjectReference: corev1.LocalObjectReference{ + Name: restore.Cfg.RestoreToS3Secret, + }, + }, + }, + } + + pvc = v1.RestoreMethod{ + Folder: &v1.FolderRestore{ + PersistentVolumeClaimVolumeSource: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: restore.Cfg.ClaimName, + }, + }, + } + + restoreObject := v1.Restore{ + ObjectMeta: metav1.ObjectMeta{ + Name: restoreName, + Namespace: restore.Cfg.Namespace, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "Restore", + APIVersion: "k8up.io/v1", + }, + Spec: v1.RestoreSpec{ + //Snapshot: snapshot, + RunnableSpec: v1.RunnableSpec{ + Backend: &v1.Backend{ + RepoPasswordSecretRef: &corev1.SecretKeySelector{ + Key: restore.Cfg.SecretRefKey, + LocalObjectReference: corev1.LocalObjectReference{ + Name: restore.Cfg.SecretRef, + }, + }, + S3: &v1.S3Spec{ + Endpoint: restore.Cfg.S3Endpoint, + Bucket: restore.Cfg.S3Bucket, + AccessKeyIDSecretRef: &corev1.SecretKeySelector{ + Key: restore.Cfg.S3SecretRefUsernameKey, + LocalObjectReference: corev1.LocalObjectReference{ + Name: restore.Cfg.S3SecretRef, + }, + }, + SecretAccessKeySecretRef: &corev1.SecretKeySelector{ + Key: restore.Cfg.S3SecretRefPasswordKey, + LocalObjectReference: corev1.LocalObjectReference{ + Name: restore.Cfg.S3SecretRef, + }, + }, + }, + }, + }, + }, + } + if ctx.IsSet("runAsUser") { + restoreObject.Spec.PodSecurityContext = &corev1.PodSecurityContext{ + RunAsUser: &restore.Cfg.RunAsUser, + } + } + + if restore.Cfg.RestoreMethod == "s3" { + restoreObject.Spec.RestoreMethod = &s3 + } else { + restoreObject.Spec.RestoreMethod = &pvc + } + + yamled, err := json.Marshal(restoreObject) + if err != nil { + logger.Error(err, "Failed to marshal restoreObject") + return err + } + post := client.RESTClient().Post().Namespace("default").AbsPath("/apis/k8up.io/v1/").Resource("restores").Body(yamled).Do(context.TODO()) + + status, err := post.Raw() + if err != nil { + // lines below looks very weird, but it's necessary, as actual reason why request failed is hidden in status.Message variable + var out1 metav1.Status + err := json.Unmarshal(status, &out1) + if err != nil { + logger.Error(err, "Failed to unmarshal Status object") + } + logger.Error(err, out1.Message) + return err + } + logger.Info(fmt.Sprintf("Backup created successfully, You can find it running:\tkubectl -n %s get restores.k8up.io %s", restore.Cfg.Namespace, restoreName)) + logger.Info(fmt.Sprintf("To access logs please run:\tkubectl -n %s logs jobs/restore-%s", restore.Cfg.Namespace, restoreName)) + return nil +} diff --git a/cmd/k8up/main.go b/cmd/k8up/main.go index 38c52ce47..1f830b54f 100644 --- a/cmd/k8up/main.go +++ b/cmd/k8up/main.go @@ -14,6 +14,7 @@ import ( controllerzap "sigs.k8s.io/controller-runtime/pkg/log/zap" "github.com/k8up-io/k8up/v2/cmd" + k8upcli "github.com/k8up-io/k8up/v2/cmd/cli" "github.com/k8up-io/k8up/v2/cmd/operator" "github.com/k8up-io/k8up/v2/cmd/restic" ) @@ -81,6 +82,7 @@ func app() *cli.App { Commands: []*cli.Command{ operator.Command, restic.Command, + k8upcli.Command, }, } } diff --git a/cmd/restic/main.go b/cmd/restic/main.go index 5aa9cee85..d2725a2ea 100644 --- a/cmd/restic/main.go +++ b/cmd/restic/main.go @@ -34,7 +34,6 @@ var ( Command = &cli.Command{ Name: "restic", Description: "Start k8up in restic mode", - Category: "backup", Action: resticMain, Flags: []cli.Flag{ &cli.BoolFlag{Destination: &cfg.Config.DoCheck, Name: "check", Usage: "Set, if the container should do a check"}, diff --git a/config/crd/apiextensions.k8s.io/v1/k8up.io_snapshots.yaml b/config/crd/apiextensions.k8s.io/v1/k8up.io_snapshots.yaml index 0f8a00e85..2ad472e84 100644 --- a/config/crd/apiextensions.k8s.io/v1/k8up.io_snapshots.yaml +++ b/config/crd/apiextensions.k8s.io/v1/k8up.io_snapshots.yaml @@ -15,7 +15,20 @@ spec: singular: snapshot scope: Namespaced versions: - - name: v1 + - additionalPrinterColumns: + - description: Date when snapshot was taken + jsonPath: .spec.date + name: Date taken + type: string + - description: Snapshot's paths + jsonPath: .spec.paths[*] + name: Paths + type: string + - description: Repository Url + jsonPath: .spec.repository + name: Repository + type: string + name: v1 schema: openAPIV3Schema: description: Snapshot is the Schema for the snapshots API diff --git a/docs/modules/ROOT/examples/tutorial/backup.yaml b/docs/modules/ROOT/examples/tutorial/backup.yaml index eb2a29633..08116a0ce 100644 --- a/docs/modules/ROOT/examples/tutorial/backup.yaml +++ b/docs/modules/ROOT/examples/tutorial/backup.yaml @@ -5,7 +5,6 @@ metadata: spec: failedJobsHistoryLimit: 2 successfulJobsHistoryLimit: 2 - promURL: http://minio:9000 backend: repoPasswordSecretRef: name: backup-repo diff --git a/docs/modules/ROOT/examples/tutorial/restore/wordpress-bucket-to-bucket.yaml b/docs/modules/ROOT/examples/tutorial/restore/wordpress-bucket-to-bucket.yaml new file mode 100644 index 000000000..c696a83ff --- /dev/null +++ b/docs/modules/ROOT/examples/tutorial/restore/wordpress-bucket-to-bucket.yaml @@ -0,0 +1,31 @@ +apiVersion: k8up.io/v1 +kind: Restore +metadata: + name: restore-wordpress +spec: + snapshot: f0dd5684 + podSecurityContext: + runAsUser: 0 + restoreMethod: + s3: + endpoint: http://minio:9000 + bucket: backup2 + accessKeyIDSecretRef: + name: minio-credentials + key: username + secretAccessKeySecretRef: + name: minio-credentials + key: password + backend: + repoPasswordSecretRef: + name: backup-repo + key: password + s3: + endpoint: http://minio:9000 + bucket: backups + accessKeyIDSecretRef: + name: minio-credentials + key: username + secretAccessKeySecretRef: + name: minio-credentials + key: password diff --git a/docs/modules/ROOT/examples/tutorial/restore/wordpress.yaml b/docs/modules/ROOT/examples/tutorial/restore/wordpress.yaml index 0f6800aa0..cb31943ff 100644 --- a/docs/modules/ROOT/examples/tutorial/restore/wordpress.yaml +++ b/docs/modules/ROOT/examples/tutorial/restore/wordpress.yaml @@ -3,7 +3,9 @@ kind: Restore metadata: name: restore-wordpress spec: - snapshot: SNAPSHOT_ID + snapshot: f0dd5684 + podSecurityContext: + runAsUser: 0 restoreMethod: folder: claimName: wordpress-pvc diff --git a/docs/modules/ROOT/examples/tutorial/wordpress/deployment.yaml b/docs/modules/ROOT/examples/tutorial/wordpress/deployment.yaml index b9d3374b1..2bb475298 100644 --- a/docs/modules/ROOT/examples/tutorial/wordpress/deployment.yaml +++ b/docs/modules/ROOT/examples/tutorial/wordpress/deployment.yaml @@ -20,30 +20,6 @@ spec: containers: - image: wordpress:5.4-apache name: wordpress - readinessProbe: - failureThreshold: 3 - httpGet: - path: "/wp-admin/install.php" - port: 80 - scheme: HTTP - httpHeaders: - - name: X-Forwarded-Proto - value: https - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - livenessProbe: - failureThreshold: 3 - httpGet: - path: "/wp-admin/install.php" - port: 80 - scheme: HTTP - httpHeaders: - - name: X-Forwarded-Proto - value: https - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 env: - name: WORDPRESS_DB_HOST value: mariadb diff --git a/docs/modules/ROOT/examples/usage/k8up.txt b/docs/modules/ROOT/examples/usage/k8up.txt index 46fc08213..cbe44eb77 100644 --- a/docs/modules/ROOT/examples/usage/k8up.txt +++ b/docs/modules/ROOT/examples/usage/k8up.txt @@ -9,9 +9,9 @@ VERSION: COMMANDS: operator + restic + cli help, h Shows a list of commands or help for one command - backup: - restic GLOBAL OPTIONS: --debug, --verbose, -d sets the log level to debug (default: false) [$K8UP_DEBUG] diff --git a/docs/modules/ROOT/examples/usage/restic.txt b/docs/modules/ROOT/examples/usage/restic.txt index d7b80219f..db955c9c3 100644 --- a/docs/modules/ROOT/examples/usage/restic.txt +++ b/docs/modules/ROOT/examples/usage/restic.txt @@ -4,9 +4,6 @@ NAME: USAGE: k8up restic [command options] [arguments...] -CATEGORY: - backup - DESCRIPTION: Start k8up in restic mode diff --git a/docs/modules/ROOT/pages/how-tos/restore.adoc b/docs/modules/ROOT/pages/how-tos/restore.adoc index 573a612d8..d7a49e3a7 100644 --- a/docs/modules/ROOT/pages/how-tos/restore.adoc +++ b/docs/modules/ROOT/pages/how-tos/restore.adoc @@ -230,3 +230,112 @@ dr-xr-xr-x 1 simonbeck staff 0 Jun 8 09:21 tags ---- Here you can browse all backups by host, ids, snapshots or tags. + +=== Restore via CLI + +K8up supports restoring via CLI. It's a fast and simple way to create restore.k8up.io/v2 CRD. + +[source,bash] +---- +ohmybash ~ k8up restore --help + +NAME: + k8up restore + +USAGE: + k8up cli restore [command options] [arguments...] + +CATEGORY: + cli + +DESCRIPTION: + CLI commands that can be executed everywhere + +OPTIONS: + --snapshot kubectl get snapshots Required ; ID of the snapshot kubectl get snapshots, set, via cli or via env: [$SNAPSHOT] + --secretRef value Required ; Set secret name from which You want to take S3 credentials, via cli or via env: [$SECRET_REF] + --s3endpoint value Required ; Set s3endpoint from which backup will be taken, via cli or via env: [$S3ENDPOINT] + --s3bucket value Required ; Set s3bucket from which backup will be taken, via cli or via env: [$S3BUCKET] + --s3secretRef value Required ; Set secret name, where S3 username & password are stored from which backup will be taken, via cli or via env: [$S3SECRETREF] + --restoreMethod value Required ; Set restore method [ pvc|s3 ], via cli or via env: (default: pvc) [$RESTOREMETHOD] + --claimName value Required ; Set claimName field, via cli or via env: [$CLAIMNAME] + --S3SecretRefUsernameKey value Optional ; Set S3SecretRefUsernameKey, key inside secret, under which S3 username is stored, via cli or via env: (default: username) [$S3SECRETREFUSERNAMEKEY] + --S3SecretRefPasswordKey value Optional ; Set S3SecretRefPasswordKey, key inside secret, under which Restic repo password is stored, via cli or via env: (default: password) [$S3SECRETREFPASSWORDKEY] + --restoreName value Optional ; Set restoreName - metadata.Name field, if empty, k8up will generate name, via cli or via env: [$RESTORENAME] + --runAsUser value Optional ; Set user UID, via cli or via env: (default: 0) [$RUNASUSER] + --restoreToS3Endpoint value Optional ; Set restore endpoint, only when using s3 restore method, via cli or via env: [$RESTORETOS3ENDPOINT] + --restoreToS3Bucket value Optional ; Set restore bucket, only when using s3 restore method, via cli or via env: [$RESTORETOS3BUCKET] + --restoreToS3Secret value Optional ; Set restore Secret, only when using s3 restore method, expecting secret name containing key value pair with 'username' and 'password' keys, via cli or via env: [$RESTORETOS3SECRET] + --RestoreToS3SecretUsernameKey value Optional ; Set RestoreToS3SecretUsernameKey, key inside secret, under which S3 username is stored, via cli or via env: (default: username) [$RESTORETOS3SECRETUSERNAMEKEY] + --RestoreToS3SecretPasswordKey value Optional ; Set RestoreToS3SecretPasswordKey, key inside secret, under which Restic repo password is stored, via cli or via env: (default: password) [$RESTORETOS3SECRETPASSWORDKEY] + --namespace value Optional ; Set namespace in which You want to execute restore, via cli or via env: (default: default) [$NAMESPACE] + --kubeconfig value Optional ; Set kubeconfig to connect to cluster, via cli or via env: (default: ~/.kube/config) [$KUBECONFIG] + --help, -h show help (default: false) +---- + +Example usages: + +[source,bash] +---- +# restore using PVC + k8up cli restore \ + --restoreMethod pvc \ + --kubeconfig .e2e-test/kind-kubeconfig-v1.24.4 \ + --secretRef backup-repo \ + --namespace default \ + --s3endpoint http://minio:9000 \ + --s3bucket backups \ + --s3secretRef minio-credentials \ + --snapshot 5c3fc641 \ + --claimName wordpress-pvc \ + --runAsUser 0 + + +# restore using S3 => S3 + k8up cli restore \ + --restoreMethod s3 \ + --kubeconfig .e2e-test/kind-kubeconfig-v1.24.4 \ + --secretRef backup-repo \ + --namespace default \ + --s3endpoint http://minio:9000 \ + --s3bucket backups \ + --s3secretRef minio-credentials \ + --snapshot 5c3fc641 \ + --claimName wordpress-pvc \ + --runAsUser 0 \ + --restoreToS3Bucket backup2 \ + --restoreToS3Secret minio-credentials \ + --restoreToS3Endpoint http://minio:9000 +---- + +[source] +---- +# PVC restore + (where the backup is) + S3 storage ------------> k8up --------------> PVC restore + --s3endpoint --runAsUser --claimName + --s3bucket --kubeconfig + --s3secretRef --snapshot + --namespace + --restoreMethod + --secretRef + +# s3 to s3 restore + + (where the backup is) (different s3 where we want to copy our backup) + S3 storage ------------> k8up --------------> s3 storage + --s3endpoint --runAsUser --restoreToS3Bucket + --s3bucket --kubeconfig --restoreToS3Secret + --s3secretRef --snapshot --restoreToS3Endpoint + --namespace + --restoreMethod + --secretRef +---- + +As a result of this CLI will be created k8up object restore.k8up.io, which will create a job and job will create a pod to do actual restore. If You won't specify namespace all of those objects will be created in `default` one. You can easily access them using, for example: +``` +kubectl get restores.k8up.io +kubectl get jobs +kubectl logs -f jobs/resotre-job-123 +``` +