Skip to content

Commit

Permalink
[K10-1255] Kanister delete data (#5283)
Browse files Browse the repository at this point in the history
* Change func name

* Add Kanister DeleteData

* Address review comments

* Add delete to data_test
  • Loading branch information
DeepikaDixit authored and Ilya Kislenko committed Mar 27, 2019
1 parent 4dd70a4 commit c057e94
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 6 deletions.
15 changes: 15 additions & 0 deletions pkg/function/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,19 @@ func newCopyDataTestBlueprint() crv1alpha1.Blueprint {
},
},
},
"delete": &crv1alpha1.BlueprintAction{
Phases: []crv1alpha1.BlueprintPhase{
crv1alpha1.BlueprintPhase{
Name: "testDelete",
Func: "DeleteData",
Args: map[string]interface{}{
DeleteDataNamespaceArg: "{{ .PVC.Namespace }}",
DeleteDataBackupArtifactPrefixArg: fmt.Sprintf("{{ .Options.%s }}", CopyVolumeDataOutputBackupArtifactLocation),
DeleteDataBackupIdentifierArg: fmt.Sprintf("{{ .Options.%s }}", CopyVolumeDataOutputBackupID),
},
},
},
},
},
}
}
Expand Down Expand Up @@ -289,6 +302,8 @@ func (s *DataSuite) TestCopyData(c *C) {
_ = runAction(c, bp, "restore", tp)
// Validate file exists on this new PVC
_ = runAction(c, bp, "checkfile", tp)
// Delete data from copy
_ = runAction(c, bp, "delete", tp)
}

func runAction(c *C, bp crv1alpha1.Blueprint, action string, tp *param.TemplateParams) map[string]interface{} {
Expand Down
115 changes: 115 additions & 0 deletions pkg/function/delete_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package function

import (
"context"

"github.com/pkg/errors"

"github.com/kanisterio/kanister/pkg"
"github.com/kanisterio/kanister/pkg/format"
"github.com/kanisterio/kanister/pkg/kube"
"github.com/kanisterio/kanister/pkg/param"
"github.com/kanisterio/kanister/pkg/restic"
)

const (
// DeleteDataNamespaceArg provides the namespace
DeleteDataNamespaceArg = "namespace"
// DeleteDataBackupArtifactPrefixArg provides the path to restore backed up data
DeleteDataBackupArtifactPrefixArg = "backupArtifactPrefix"
// DeleteDataBackupIdentifierArg provides a unique ID added to the backed up artifacts
DeleteDataBackupIdentifierArg = "backupID"
// DeleteDataBackupTagArg provides a unique tag added to the backed up artifacts
DeleteDataBackupTagArg = "backupTag"
// DeleteDataEncryptionKeyArg provides the encryption key to be used for deletes
DeleteDataEncryptionKeyArg = "encryptionKey"
// DeleteDataReclaimSpace provides a way to specify if space should be reclaimed
DeleteDataReclaimSpace = "reclaimSpace"
deleteDataJobPrefix = "delete-data-"
)

func init() {
kanister.Register(&deleteDataFunc{})
}

var _ kanister.Func = (*deleteDataFunc)(nil)

type deleteDataFunc struct{}

func (*deleteDataFunc) Name() string {
return "DeleteData"
}

func (*deleteDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) {
var namespace, deleteArtifactPrefix, deleteIdentifier, deleteTag, encryptionKey string
var reclaimSpace bool
var err error
if err = Arg(args, DeleteDataNamespaceArg, &namespace); err != nil {
return nil, err
}
if err = Arg(args, DeleteDataBackupArtifactPrefixArg, &deleteArtifactPrefix); err != nil {
return nil, err
}
if err = OptArg(args, DeleteDataBackupIdentifierArg, &deleteIdentifier, ""); err != nil {
return nil, err
}
if err = OptArg(args, DeleteDataBackupTagArg, &deleteTag, ""); err != nil {
return nil, err
}
if err = OptArg(args, DeleteDataEncryptionKeyArg, &encryptionKey, restic.GeneratePassword()); err != nil {
return nil, err
}
if err = OptArg(args, DeleteDataReclaimSpace, &reclaimSpace, false); err != nil {
return nil, err
}
cli, err := kube.NewClient()
if err != nil {
return nil, errors.Wrapf(err, "Failed to create Kubernetes client")
}
pod, err := kube.CreatePod(ctx, cli, &kube.PodOptions{
Namespace: namespace,
GenerateName: deleteDataJobPrefix,
Image: kanisterToolsImage,
Command: []string{"sh", "-c", "tail -f /dev/null"},
})
if err != nil {
return nil, errors.Wrapf(err, "Failed to create pod to delete data")
}
defer kube.DeletePod(context.Background(), cli, pod)

// Wait for pod to reach running state
if err := kube.WaitForPodReady(ctx, cli, pod.Namespace, pod.Name); err != nil {
return nil, errors.Wrapf(err, "Failed while waiting for Pod %s to be ready", pod.Name)
}
if (deleteIdentifier != "") == (deleteTag != "") {
return nil, errors.Errorf("Require one argument: %s or %s", DeleteDataBackupIdentifierArg, DeleteDataBackupTagArg)
}
var cmd []string
if deleteIdentifier != "" {
cmd = restic.ForgetCommandByID(tp.Profile, deleteArtifactPrefix, deleteIdentifier, encryptionKey)
}
if deleteTag != "" {
cmd = restic.ForgetCommandByTag(tp.Profile, deleteArtifactPrefix, deleteTag, encryptionKey)
}
stdout, stderr, err := kube.Exec(cli, namespace, pod.Name, pod.Spec.Containers[0].Name, cmd)
format.Log(pod.Name, pod.Spec.Containers[0].Name, stdout)
format.Log(pod.Name, pod.Spec.Containers[0].Name, stderr)
if err != nil {
return nil, errors.Wrapf(err, "Failed to forget data")
}

if reclaimSpace {
cmdP := restic.PruneCommand(tp.Profile, deleteArtifactPrefix, encryptionKey)
stdoutP, stderrP, errP := kube.Exec(cli, namespace, pod.Name, pod.Spec.Containers[0].Name, cmdP)
format.Log(pod.Name, pod.Spec.Containers[0].Name, stdoutP)
format.Log(pod.Name, pod.Spec.Containers[0].Name, stderrP)
if err != nil {
return nil, errors.Wrapf(errP, "Failed to prune data after forget")
}
}
return nil, nil
}

func (*deleteDataFunc) RequiredArgs() []string {
return []string{DeleteDataNamespaceArg, DeleteDataBackupArtifactPrefixArg}
}
12 changes: 6 additions & 6 deletions pkg/function/location_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ const (
)

func init() {
kanister.Register(&deleteDataFunc{})
kanister.Register(&locationDeleteFunc{})
}

var _ kanister.Func = (*deleteDataFunc)(nil)
var _ kanister.Func = (*locationDeleteFunc)(nil)

type deleteDataFunc struct{}
type locationDeleteFunc struct{}

func (*deleteDataFunc) Name() string {
func (*locationDeleteFunc) Name() string {
return "LocationDelete"
}

func (*deleteDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) {
func (*locationDeleteFunc) Exec(ctx context.Context, tp param.TemplateParams, args map[string]interface{}) (map[string]interface{}, error) {
var artifact string
var err error
if err = Arg(args, LocationDeleteArtifactArg, &artifact); err != nil {
Expand All @@ -41,6 +41,6 @@ func (*deleteDataFunc) Exec(ctx context.Context, tp param.TemplateParams, args m
return nil, location.Delete(ctx, *tp.Profile, artifact)
}

func (*deleteDataFunc) RequiredArgs() []string {
func (*locationDeleteFunc) RequiredArgs() []string {
return []string{LocationDeleteArtifactArg}
}

0 comments on commit c057e94

Please sign in to comment.