Skip to content

Commit

Permalink
Fix stash broken cli (#148)
Browse files Browse the repository at this point in the history
Signed-off-by: hmsayem <[email protected]>
  • Loading branch information
hmsayem authored Jan 31, 2022
1 parent 7060e6f commit 34e30bd
Show file tree
Hide file tree
Showing 156 changed files with 28,649 additions and 14,727 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ require (
k8s.io/client-go v0.21.1
k8s.io/klog/v2 v2.8.0
k8s.io/kubectl v0.21.1
kmodules.xyz/client-go v0.0.0-20211122091731-6c471b24a4ea
kmodules.xyz/client-go v0.0.0-20220108081101-27afc2ac4ebe
kmodules.xyz/objectstore-api v0.0.0-20211116180107-8720be0c9bf7
kmodules.xyz/offshoot-api v0.0.0-20210829122105-6f4d481b0c61
kmodules.xyz/openshift v0.0.0-20210618001443-f2507caa512f
stash.appscode.dev/apimachinery v0.17.0
stash.appscode.dev/stash v0.17.0
stash.appscode.dev/apimachinery v0.17.1-0.20220131063340-553a63a8479a
stash.appscode.dev/stash v0.17.1-0.20220125101015-1e41192e71c8
)

replace bitbucket.org/ww/goautoneg => gomodules.xyz/goautoneg v0.0.0-20120707110453-a547fc61f48d
Expand Down
48 changes: 16 additions & 32 deletions go.sum

Large diffs are not rendered by default.

68 changes: 7 additions & 61 deletions pkg/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,79 +17,41 @@ limitations under the License.
package pkg

import (
"io/ioutil"
"os"
"path/filepath"

docker_image "stash.appscode.dev/apimachinery/pkg/docker"
"stash.appscode.dev/apimachinery/pkg/restic"
"stash.appscode.dev/cli/pkg/docker"

core "k8s.io/api/core/v1"
"stash.appscode.dev/apimachinery/pkg/docker"
)

const (
secretDirName = "secret"
configDirName = "config"
ResticEnvs = "restic-envs"
)

type cliLocalDirectories struct {
secretDir string // temp dir
configDir string // temp dir
downloadDir string // user provided or, current working dir
}

// These variables will be set during build time
const (
ScratchDir = "/tmp/scratch"
DestinationDir = "/tmp/destination"
)

var (
ResticRegistry = "restic"
ResticImage = "restic"
ResticTag = "latest"
)

var imgRestic docker_image.Docker
var imgRestic docker.Docker

func init() {
imgRestic.Registry = ResticRegistry
imgRestic.Image = ResticImage
imgRestic.Tag = ResticTag
}

func (localDirs *cliLocalDirectories) prepareSecretDir(tempDir string, secret *core.Secret) error {
// write repository secrets in a sub-dir insider tempDir
localDirs.secretDir = filepath.Join(tempDir, secretDirName)
if err := os.MkdirAll(localDirs.secretDir, 0755); err != nil {
return err
}
for key, value := range secret.Data {
if err := ioutil.WriteFile(filepath.Join(localDirs.secretDir, key), value, 0755); err != nil {
return err
}
}
return nil
}

func (localDirs *cliLocalDirectories) prepareConfigDir(tempDir string, setupOpt *restic.SetupOptions, restoreOpt *restic.RestoreOptions) error {
// write restic options in a sub-dir insider tempDir
localDirs.configDir = filepath.Join(tempDir, configDirName)
if err := os.MkdirAll(localDirs.secretDir, 0755); err != nil {
return err
}
if setupOpt != nil {
err := docker.WriteSetupOptionToFile(setupOpt, filepath.Join(localDirs.configDir, docker.SetupOptionsFile))
if err != nil {
return err
}
}
if restoreOpt != nil {
err := docker.WriteRestoreOptionToFile(restoreOpt, filepath.Join(localDirs.configDir, docker.RestoreOptionsFile))
if err != nil {
return err
}
}
return nil
}

func (localDirs *cliLocalDirectories) prepareDownloadDir() (err error) {
// if destination flag is not specified, restore in current directory
if localDirs.downloadDir == "" {
Expand All @@ -99,19 +61,3 @@ func (localDirs *cliLocalDirectories) prepareDownloadDir() (err error) {
}
return os.MkdirAll(localDirs.downloadDir, 0755)
}

// Write Storage Secret credentials in secret dir inside tempDir
func (localDirs *cliLocalDirectories) dumpSecret(temDir string, secret *core.Secret) error {
localDirs.secretDir = filepath.Join(temDir, secretDirName)
if err := os.MkdirAll(localDirs.secretDir, 0755); err != nil {
return err
}

for key, val := range secret.Data {
if err := ioutil.WriteFile(filepath.Join(localDirs.secretDir, key), []byte(val), 0755); err != nil {
return err
}
}

return nil
}
63 changes: 44 additions & 19 deletions pkg/clone_pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"k8s.io/kubectl/pkg/util/templates"
kmapi "kmodules.xyz/client-go/api/v1"
ofst "kmodules.xyz/offshoot-api/api/v1"
)

Expand Down Expand Up @@ -74,7 +75,9 @@ func NewCmdClonePVC() *cobra.Command {
}
klog.Infof("Repository has been created successfully.")

err = backupPVC(pvcName, repoName)
err = backupPVC(pvcName, kmapi.ObjectReference{
Name: repository.Name,
})
if err != nil {
return err
}
Expand All @@ -85,8 +88,13 @@ func NewCmdClonePVC() *cobra.Command {
if err != nil {
return err
}

err = restorePVC(pvc, repoName)
err = ensurePVC(pvc)
if err != nil {
return err
}
err = restorePVC(pvc.Name, kmapi.ObjectReference{
Name: repoName,
})
if err != nil {
return err
}
Expand All @@ -111,12 +119,12 @@ func NewCmdClonePVC() *cobra.Command {

// at first, create BackupConfiguration to take backup
// after successful taking backup, delete the BackupConfiguration to stop taking backup
func backupPVC(pvcName string, repoName string) error {
func backupPVC(pvcName string, repository kmapi.ObjectReference) error {
// configure BackupConfiguration
opt := backupConfigOption{
task: "pvc-backup",
schedule: "*/59 * * * *", // we have to set a large value then trigger an instant backup immediately.
repository: repoName,
repository: repository,
retentionPolicy: v1alpha1.RetentionPolicy{
Name: "keep-last-5",
KeepLast: 5,
Expand Down Expand Up @@ -155,21 +163,44 @@ func backupPVC(pvcName string, repoName string) error {

// create RestoreSession to create a new PVC in the destination namespace
// then restore the backed up data into the PVC
func restorePVC(pvc *core.PersistentVolumeClaim, repoName string) error {

func restorePVC(pvcName string, repository kmapi.ObjectReference) error {
// configure RestoreSession
opt := restoreSessionOption{
repository: repoName,
repository: repository,
task: "pvc-restore",
rule: v1beta1.Rule{
Snapshots: []string{"latest"},
},
targetRef: v1beta1.TargetRef{
Name: pvcName,
Kind: apis.KindPersistentVolumeClaim,
APIVersion: core.SchemeGroupVersion.String(),
},
}

restoreSession, err := opt.newRestoreSession(fmt.Sprintf("%s-%s", pvc.Name, "restore"), dstNamespace)
restoreSession, err := opt.newRestoreSession(fmt.Sprintf("%s-%s", pvcName, "restore"), dstNamespace)
if err != nil {
return err
}
restoreSession.Spec.Target.VolumeClaimTemplates = []ofst.PersistentVolumeClaim{

klog.Infof("Creating RestoreSession: %s to the namespace: %s", restoreSession.Name, restoreSession.Namespace)
restoreSession, err = createRestoreSession(restoreSession)
if err != nil {
return err
}
klog.Infof("RestoreSession has been created successfully.")
err = WaitUntilRestoreSessionCompleted(restoreSession.Name, restoreSession.Namespace)
if err != nil {
return err
}
klog.Infof("RestoreSession has been succeeded.")
// delete RestoreSession
return stashClient.StashV1beta1().RestoreSessions(dstNamespace).Delete(context.TODO(), restoreSession.Name, metav1.DeleteOptions{})
}
func ensurePVC(pvc *core.PersistentVolumeClaim) error {
klog.Infof("Creating pvc in %s namespace", pvc.Namespace)
pvcTemplates := []ofst.PersistentVolumeClaim{
{
PartialObjectMeta: ofst.PartialObjectMeta{
Name: pvc.Name,
Expand All @@ -182,20 +213,14 @@ func restorePVC(pvc *core.PersistentVolumeClaim, repoName string) error {
},
},
}
claim := pvcTemplates[0].DeepCopy().ToCorePVC()
_, err := kubeClient.CoreV1().PersistentVolumeClaims(dstNamespace).Create(context.TODO(), claim, metav1.CreateOptions{})

klog.Infof("Creating RestoreSession: %s to the namespace: %s", restoreSession.Name, restoreSession.Namespace)
restoreSession, err = createRestoreSession(restoreSession)
if err != nil {
return err
}
klog.Infof("RestoreSession has been created successfully.")
err = WaitUntilRestoreSessionCompleted(restoreSession.Name, restoreSession.Namespace)
if err != nil {
return err
}
klog.Infof("RestoreSession has been succeeded.")
// delete RestoreSession
return stashClient.StashV1beta1().RestoreSessions(dstNamespace).Delete(context.TODO(), restoreSession.Name, metav1.DeleteOptions{})
klog.Infof("PVC %q has been created successfully in %s namespace", pvc.Name, pvc.Namespace)
return nil
}

func cleanupRepository(repoName string) error {
Expand Down
12 changes: 8 additions & 4 deletions pkg/create_backupconfiguration.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"k8s.io/kubectl/pkg/util/templates"
kmapi "kmodules.xyz/client-go/api/v1"
)

var (
createBackupConfigExample = templates.Examples(`
# Create a new BackupConfiguration
# stash create backupconfig --namespace=<namespace> gcs-repo [Flag]
# For Restic driver
stash create backupconfig ss-backup --namespace=demo --repository=gcs-repo --schedule="*/4 * * * *" --target-apiversion=apps/v1 --target-kind=StatefulSet --target-name=stash-demo --paths=/source/data --volume-mounts=source-data:/source/data --keep-last=5 --prune=true
stash create backupconfig ss-backup --namespace=demo --repo-name=gcs-repo --schedule="*/4 * * * *" --target-apiversion=apps/v1 --target-kind=StatefulSet --target-name=stash-demo --paths=/source/data --volume-mounts=source-data:/source/data --keep-last=5 --prune=true
# For VolumeSnapshotter driver
stash create backupconfig statefulset-volume-snapshot --namespace=demo --driver=VolumeSnapshotter --schedule="*/4 * * * *" --target-apiversion=apps/v1 --target-kind=StatefulSet --target-name=stash-demo --replica=1 --volumesnpashotclass=default-snapshot-class --keep-last=5 --prune=true`)
)
Expand All @@ -48,7 +49,7 @@ type backupConfigOption struct {

targetRef v1beta1.TargetRef
retentionPolicy v1alpha1.RetentionPolicy
repository string
repository kmapi.ObjectReference
schedule string
driver string
volumesnpashotclass string
Expand Down Expand Up @@ -88,7 +89,8 @@ func NewCmdCreateBackupConfiguration() *cobra.Command {
cmd.Flags().StringVar(&backupConfigOpt.targetRef.APIVersion, "target-apiversion", backupConfigOpt.targetRef.APIVersion, "API-Version of the target resource")
cmd.Flags().StringVar(&backupConfigOpt.targetRef.Kind, "target-kind", backupConfigOpt.targetRef.Kind, "Kind of the target resource")
cmd.Flags().StringVar(&backupConfigOpt.targetRef.Name, "target-name", backupConfigOpt.targetRef.Name, "Name of the target resource")
cmd.Flags().StringVar(&backupConfigOpt.repository, "repository", backupConfigOpt.repository, "Name of the Repository")
cmd.Flags().StringVar(&backupConfigOpt.repository.Name, "repo-name", backupConfigOpt.repository.Name, "Name of the Repository")
cmd.Flags().StringVar(&backupConfigOpt.repository.Namespace, "repo-namespace", namespace, "Namespace of the Repository")
cmd.Flags().StringVar(&backupConfigOpt.schedule, "schedule", backupConfigOpt.schedule, "Schedule of the Backup")
cmd.Flags().StringVar(&backupConfigOpt.driver, "driver", backupConfigOpt.driver, "Driver indicates the mechanism used to backup (i.e. VolumeSnapshotter, Restic)")
cmd.Flags().StringVar(&backupConfigOpt.task, "task", backupConfigOpt.task, "Name of the Task")
Expand Down Expand Up @@ -126,7 +128,9 @@ func (opt backupConfigOption) newBackupConfiguration(name string, namespace stri
if v1beta1.Snapshotter(opt.driver) == v1beta1.VolumeSnapshotter {
backupConfig.Spec.Driver = v1beta1.Snapshotter(opt.driver)
} else {
backupConfig.Spec.Repository = core.LocalObjectReference{Name: opt.repository}
backupConfig.Spec.Repository = kmapi.ObjectReference{
Name: opt.repository.Name,
Namespace: opt.repository.Namespace}
backupConfig.Spec.Task = v1beta1.TaskRef{Name: opt.task}
}

Expand Down
24 changes: 13 additions & 11 deletions pkg/create_restoresession.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import (
"context"
"fmt"

"stash.appscode.dev/apimachinery/apis"
"stash.appscode.dev/apimachinery/apis/stash/v1beta1"
v1beta1_util "stash.appscode.dev/apimachinery/client/clientset/versioned/typed/stash/v1beta1/util"
"stash.appscode.dev/stash/pkg/util"

vs "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1"
"github.com/spf13/cobra"
Expand All @@ -33,6 +31,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog/v2"
"k8s.io/kubectl/pkg/util/templates"
kmapi "kmodules.xyz/client-go/api/v1"
ofst "kmodules.xyz/offshoot-api/api/v1"
)

Expand All @@ -41,7 +40,7 @@ var (
# Create a RestoreSession
# stash create restore --namespace=demo <restore session name> [Flag]
# For Restic driver
stash create restoresession ss-restore --namespace=demo --repository=gcs-repo --target-apiversion=apps/v1 --target-kind=StatefulSet --target-name=stash-recovered --paths=/source/data --volume-mounts=source-data:/source/data
stash create restoresession ss-restore --namespace=demo --repo-name=gcs-repo --target-apiversion=apps/v1 --target-kind=StatefulSet --target-name=stash-recovered --paths=/source/data --volume-mounts=source-data:/source/data
# For VolumeSnapshotter driver
stash create restoresession restore-pvc --namespace=demo --driver=VolumeSnapshotter --replica=3 --claim.name=restore-data-restore-demo-${POD_ORDINAL} --claim.access-modes=ReadWriteOnce --claim.storageclass=standard --claim.size=1Gi --claim.datasource=source-data-stash-demo-0-1567146010`)
)
Expand All @@ -50,7 +49,7 @@ type restoreSessionOption struct {
volumeMounts []string
task string
targetRef v1beta1.TargetRef
repository string
repository kmapi.ObjectReference
driver string
replica int32
alias string
Expand Down Expand Up @@ -101,7 +100,8 @@ func NewCmdCreateRestoreSession() *cobra.Command {
cmd.Flags().StringVar(&restoreSessionOpt.targetRef.Kind, "target-kind", restoreSessionOpt.targetRef.Kind, "Kind of the target resource")
cmd.Flags().StringVar(&restoreSessionOpt.targetRef.Name, "target-name", restoreSessionOpt.targetRef.Name, "Name of the target resource")

cmd.Flags().StringVar(&restoreSessionOpt.repository, "repository", restoreSessionOpt.repository, "Name of the Repository")
cmd.Flags().StringVar(&restoreSessionOpt.repository.Name, "repo-name", restoreSessionOpt.repository.Name, "Name of the Repository")
cmd.Flags().StringVar(&restoreSessionOpt.repository.Namespace, "repo-namespace", namespace, "Namespace of the Repository")
cmd.Flags().StringVar(&restoreSessionOpt.driver, "driver", restoreSessionOpt.driver, "Driver indicates the mechanism used to backup (i.e. VolumeSnapshotter, Restic)")
cmd.Flags().StringVar(&restoreSessionOpt.task, "task", restoreSessionOpt.task, "Name of the Task")
cmd.Flags().Int32Var(&restoreSessionOpt.replica, "replica", restoreSessionOpt.replica, "Replica specifies the number of replicas whose data should be backed up")
Expand Down Expand Up @@ -132,11 +132,12 @@ func (opt restoreSessionOption) newRestoreSession(name string, namespace string)
restoreSession.Spec.Driver = v1beta1.Snapshotter(opt.driver)
} else {
restoreSession.Spec = v1beta1.RestoreSessionSpec{
Repository: core.LocalObjectReference{Name: opt.repository},
Repository: kmapi.ObjectReference{
Name: opt.repository.Name,
Namespace: opt.repository.Namespace},
}
restoreSession.Spec.Task = v1beta1.TaskRef{Name: opt.task}
}

err := opt.setRestoreTarget(restoreSession)
if err != nil {
return nil, err
Expand Down Expand Up @@ -171,16 +172,17 @@ func (opt restoreSessionOption) setRestoreTarget(restoreSession *v1beta1.Restore
APIGroup: pointer.StringP(vs.GroupName),
}
} else {
if opt.targetRef.Kind != "" && util.BackupModel(opt.targetRef.Kind) == apis.ModelSidecar {
if opt.volumeClaimTemplate.name != "" && opt.volumeClaimTemplate.size != "" {
restoreSession.Spec.Target = &v1beta1.RestoreTarget{
Ref: opt.targetRef,
VolumeClaimTemplates: opt.getRestoredPVCTemplates(),
}

} else {
restoreSession.Spec.Target = &v1beta1.RestoreTarget{
VolumeClaimTemplates: opt.getRestoredPVCTemplates(),
Ref: opt.targetRef,
}

}

if len(opt.volumeMounts) > 0 {
volumeMounts, err := getVolumeMounts(opt.volumeMounts)
if err != nil {
Expand Down
Loading

0 comments on commit 34e30bd

Please sign in to comment.