Skip to content

Commit

Permalink
Add support to RestoreData with SnapshotID (#5466)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavannd1 authored and Ilya Kislenko committed Apr 25, 2019
1 parent 0105fd3 commit 9e7c9fe
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 23 deletions.
74 changes: 71 additions & 3 deletions pkg/function/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *DataSuite) TearDownSuite(c *C) {
}
}

func newRestoreDataBlueprint(pvc string) *crv1alpha1.Blueprint {
func newRestoreDataBlueprint(pvc, identifierArg, identifierVal string) *crv1alpha1.Blueprint {
return &crv1alpha1.Blueprint{
Actions: map[string]*crv1alpha1.BlueprintAction{
"restore": &crv1alpha1.BlueprintAction{
Expand All @@ -81,11 +81,11 @@ func newRestoreDataBlueprint(pvc string) *crv1alpha1.Blueprint {
RestoreDataImageArg: "kanisterio/kanister-tools:0.18.0",
RestoreDataBackupArtifactPrefixArg: "{{ .Profile.Location.Bucket }}/{{ .Profile.Location.Prefix }}",
RestoreDataRestorePathArg: "/mnt/data",
RestoreDataBackupTagArg: fmt.Sprintf("{{ .Options.%s }}", BackupDataOutputBackupTag),
RestoreDataEncryptionKeyArg: "{{ .Secrets.backupKey.Data.password | toString }}",
RestoreDataVolsArg: map[string]string{
pvc: "/mnt/data",
},
identifierArg: fmt.Sprintf("{{ .Options.%s }}", identifierVal),
},
},
},
Expand Down Expand Up @@ -183,7 +183,75 @@ func (s *DataSuite) TestBackupRestoreData(c *C) {
tp.Options = options

// Test restore
bp = *newRestoreDataBlueprint(pvc.GetName())
bp = *newRestoreDataBlueprint(pvc.GetName(), RestoreDataBackupTagArg, BackupDataOutputBackupTag)
_ = runAction(c, bp, "restore", tp)
}

func (s *DataSuite) TestBackupRestoreDataWithSnapshotID(c *C) {
ctx := context.Background()
ss, err := s.cli.AppsV1().StatefulSets(s.namespace).Create(testutil.NewTestStatefulSet())
c.Assert(err, IsNil)
err = kube.WaitOnStatefulSetReady(ctx, s.cli, ss.GetNamespace(), ss.GetName())
c.Assert(err, IsNil)

pvc := testutil.NewTestPVC()
pvc, err = s.cli.CoreV1().PersistentVolumeClaims(s.namespace).Create(pvc)
c.Assert(err, IsNil)

secret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret-datatest-id",
Namespace: s.namespace,
},
Type: "Opaque",
StringData: map[string]string{
"password": "myPassword",
},
}
secret, err = s.cli.CoreV1().Secrets(s.namespace).Create(secret)

as := crv1alpha1.ActionSpec{
Object: crv1alpha1.ObjectReference{
Kind: param.StatefulSetKind,
Name: ss.GetName(),
Namespace: s.namespace,
},
Profile: &crv1alpha1.ObjectReference{
Name: testutil.TestProfileName,
Namespace: s.namespace,
},
Secrets: map[string]crv1alpha1.ObjectReference{
"backupKey": crv1alpha1.ObjectReference{
Kind: "Secret",
Name: secret.GetName(),
Namespace: s.namespace,
},
},
}

tp, err := param.New(ctx, s.cli, s.crCli, as)
c.Assert(err, IsNil)

location := crv1alpha1.Location{
Type: crv1alpha1.LocationTypeS3Compliant,
Bucket: testutil.GetEnvOrSkip(c, testutil.TestS3BucketName),
}
tp.Profile = testutil.ObjectStoreProfileOrSkip(c, objectstore.ProviderTypeS3, location)

// Test backup
bp := *newBackupDataBlueprint()
out := runAction(c, bp, "backup", tp)
c.Assert(out[BackupDataOutputBackupID].(string), Not(Equals), "")
c.Assert(out[BackupDataOutputBackupTag].(string), Not(Equals), "")

options := map[string]string{
BackupDataOutputBackupID: out[BackupDataOutputBackupID].(string),
BackupDataOutputBackupTag: out[BackupDataOutputBackupTag].(string),
}
tp.Options = options

// Test restore with ID
bp = *newRestoreDataBlueprint(pvc.GetName(), RestoreDataBackupIdentifierArg, BackupDataOutputBackupID)
_ = runAction(c, bp, "restore", tp)
}

Expand Down
42 changes: 27 additions & 15 deletions pkg/function/restore_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,38 @@ func (*restoreDataFunc) Name() string {
return "RestoreData"
}

func validateAndGetOptArgs(args map[string]interface{}) (string, string, string, map[string]string, error) {
var restorePath, encryptionKey, pod string
func validateAndGetOptArgs(args map[string]interface{}) (string, string, string, map[string]string, string, string, error) {
var restorePath, encryptionKey, pod, tag, id string
var vols map[string]string
var err error

if err = OptArg(args, RestoreDataRestorePathArg, &restorePath, "/"); err != nil {
return restorePath, encryptionKey, pod, vols, err
return restorePath, encryptionKey, pod, vols, tag, id, err
}
if err = OptArg(args, RestoreDataEncryptionKeyArg, &encryptionKey, restic.GeneratePassword()); err != nil {
return restorePath, encryptionKey, pod, vols, err
return restorePath, encryptionKey, pod, vols, tag, id, err
}
if err = OptArg(args, RestoreDataPodArg, &pod, ""); err != nil {
return restorePath, encryptionKey, pod, vols, err
return restorePath, encryptionKey, pod, vols, tag, id, err
}
if err = OptArg(args, RestoreDataVolsArg, &vols, nil); err != nil {
return restorePath, encryptionKey, pod, vols, err
return restorePath, encryptionKey, pod, vols, tag, id, err
}
if (pod != "") == (len(vols) > 0) {
return restorePath, encryptionKey, pod, vols,
return restorePath, encryptionKey, pod, vols, tag, id,
errors.Errorf("Require one argument: %s or %s", RestoreDataPodArg, RestoreDataVolsArg)
}
return restorePath, encryptionKey, pod, vols, nil
if err = OptArg(args, RestoreDataBackupTagArg, &tag, nil); err != nil {
return restorePath, encryptionKey, pod, vols, tag, id, err
}
if err = OptArg(args, RestoreDataBackupIdentifierArg, &id, nil); err != nil {
return restorePath, encryptionKey, pod, vols, tag, id, err
}
if (tag != "") == (id != "") {
return restorePath, encryptionKey, pod, vols, tag, id,
errors.Errorf("Require one argument: %s or %s", RestoreDataBackupTagArg, RestoreDataBackupIdentifierArg)
}
return restorePath, encryptionKey, pod, vols, tag, id, nil
}

func fetchPodVolumes(pod string, tp param.TemplateParams) (map[string]string, error) {
Expand All @@ -86,7 +96,8 @@ func fetchPodVolumes(pod string, tp param.TemplateParams) (map[string]string, er
}

func (*restoreDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) {
var namespace, image, backupArtifactPrefix, backupTag string
var namespace, image, backupArtifactPrefix, backupTag, backupID string
var cmd []string
var err error
if err = Arg(args, RestoreDataNamespaceArg, &namespace); err != nil {
return nil, err
Expand All @@ -97,11 +108,8 @@ func (*restoreDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args
if err = Arg(args, RestoreDataBackupArtifactPrefixArg, &backupArtifactPrefix); err != nil {
return nil, err
}
if err = Arg(args, RestoreDataBackupTagArg, &backupTag); err != nil {
return nil, err
}
// Validate and get optional arguments
restorePath, encryptionKey, pod, vols, err := validateAndGetOptArgs(args)
restorePath, encryptionKey, pod, vols, backupTag, backupID, err := validateAndGetOptArgs(args)
if err != nil {
return nil, err
}
Expand All @@ -110,7 +118,11 @@ func (*restoreDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args
return nil, err
}
// Generate restore command based on the identifier passed
cmd := restic.RestoreCommandByTag(tp.Profile, backupArtifactPrefix, backupTag, restorePath, encryptionKey)
if backupTag != "" {
cmd = restic.RestoreCommandByTag(tp.Profile, backupArtifactPrefix, backupTag, restorePath, encryptionKey)
} else if backupID != "" {
cmd = restic.RestoreCommandByID(tp.Profile, backupArtifactPrefix, backupID, restorePath, encryptionKey)
}
if len(vols) == 0 {
// Fetch Volumes
vols, err = fetchPodVolumes(pod, tp)
Expand All @@ -128,5 +140,5 @@ func (*restoreDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args

func (*restoreDataFunc) RequiredArgs() []string {
return []string{RestoreDataNamespaceArg, RestoreDataImageArg,
RestoreDataBackupArtifactPrefixArg, RestoreDataBackupTagArg}
RestoreDataBackupArtifactPrefixArg}
}
38 changes: 33 additions & 5 deletions pkg/function/restore_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,25 @@ func (s *RestoreDataTestSuite) TestValidateAndGetOptArgs(c *C) {
{
name: "Args with Pod",
args: map[string]interface{}{
RestoreDataPodArg: "some-pod",
RestoreDataPodArg: "some-pod",
RestoreDataBackupTagArg: "backup123",
},
errChecker: IsNil,
},
{
name: "Args with Vols",
args: map[string]interface{}{
RestoreDataVolsArg: map[string]string{"pvc": "mount"},
RestoreDataVolsArg: map[string]string{"pvc": "mount"},
RestoreDataBackupTagArg: "backup123",
},
errChecker: IsNil,
},
{
name: "Args with Pod and Vols",
args: map[string]interface{}{
RestoreDataPodArg: "some-pod",
RestoreDataVolsArg: map[string]string{"pvc": "mount"},
RestoreDataPodArg: "some-pod",
RestoreDataVolsArg: map[string]string{"pvc": "mount"},
RestoreDataBackupTagArg: "backup123",
},
errChecker: NotNil,
},
Expand All @@ -129,9 +132,34 @@ func (s *RestoreDataTestSuite) TestValidateAndGetOptArgs(c *C) {
args: map[string]interface{}{},
errChecker: NotNil,
},
{
name: "Args with backupTag",
args: map[string]interface{}{
RestoreDataPodArg: "some-pod",
RestoreDataBackupTagArg: "backup123",
},
errChecker: IsNil,
},
{
name: "Args with ID",
args: map[string]interface{}{
RestoreDataPodArg: "some-pod",
RestoreDataBackupIdentifierArg: "backup123",
},
errChecker: IsNil,
},
{
name: "Args with backupTag and ID",
args: map[string]interface{}{
RestoreDataPodArg: "some-pod",
RestoreDataBackupTagArg: "backup123",
RestoreDataBackupIdentifierArg: "backup123",
},
errChecker: NotNil,
},
}
for _, tc := range testCases {
_, _, _, _, err := validateAndGetOptArgs(tc.args)
_, _, _, _, _, _, err := validateAndGetOptArgs(tc.args)
c.Check(err, tc.errChecker, Commentf("Case %s failed", tc.name))
}
}

0 comments on commit 9e7c9fe

Please sign in to comment.