Skip to content

Commit

Permalink
Merge pull request vmware-tanzu#7664 from shubham-pampattiwar/vol-pol…
Browse files Browse the repository at this point in the history
…icy-extension-impl

Extend Volume Policies feature to support more actions
  • Loading branch information
Lyndon-Li authored Apr 24, 2024
2 parents e718a13 + 8d2bef2 commit 01a2d95
Show file tree
Hide file tree
Showing 14 changed files with 1,348 additions and 63 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/7664-shubham-pampattiwar
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implementation for Extending VolumePolicies to support more actions
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/vmware-tanzu/velero

go 1.22
go 1.22.0

require (
cloud.google.com/go/storage v1.40.0
Expand Down
23 changes: 14 additions & 9 deletions internal/resourcepolicies/resource_policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ type VolumeActionType string

const (
// currently only support configmap type of resource config
ConfigmapRefType string = "configmap"
Skip VolumeActionType = "skip"
ConfigmapRefType string = "configmap"
// skip action implies the volume would be skipped from the backup operation
Skip VolumeActionType = "skip"
// fs-backup action implies that the volume would be backed up via file system copy method using the uploader(kopia/restic) configured by the user
FSBackup VolumeActionType = "fs-backup"
// snapshot action can have 3 different meaning based on velero configuration and backup spec - cloud provider based snapshots, local csi snapshots and datamover snapshots
Snapshot VolumeActionType = "snapshot"
)

// Action defined as one action for a specific way of backup
Expand All @@ -40,16 +45,16 @@ type Action struct {
}

// volumePolicy defined policy to conditions to match Volumes and related action to handle matched Volumes
type volumePolicy struct {
type VolumePolicy struct {
// Conditions defined list of conditions to match Volumes
Conditions map[string]interface{} `yaml:"conditions"`
Action Action `yaml:"action"`
}

// resourcePolicies currently defined slice of volume policies to handle backup
type resourcePolicies struct {
type ResourcePolicies struct {
Version string `yaml:"version"`
VolumePolicies []volumePolicy `yaml:"volumePolicies"`
VolumePolicies []VolumePolicy `yaml:"volumePolicies"`
// we may support other resource policies in the future, and they could be added separately
// OtherResourcePolicies []OtherResourcePolicy
}
Expand All @@ -60,16 +65,16 @@ type Policies struct {
// OtherPolicies
}

func unmarshalResourcePolicies(yamlData *string) (*resourcePolicies, error) {
resPolicies := &resourcePolicies{}
func unmarshalResourcePolicies(yamlData *string) (*ResourcePolicies, error) {
resPolicies := &ResourcePolicies{}
err := decodeStruct(strings.NewReader(*yamlData), resPolicies)
if err != nil {
return nil, fmt.Errorf("failed to decode yaml data into resource policies %v", err)
}
return resPolicies, nil
}

func (p *Policies) buildPolicy(resPolicies *resourcePolicies) error {
func (p *Policies) BuildPolicy(resPolicies *ResourcePolicies) error {
for _, vp := range resPolicies.VolumePolicies {
con, err := unmarshalVolConditions(vp.Conditions)
if err != nil {
Expand Down Expand Up @@ -162,7 +167,7 @@ func GetResourcePoliciesFromConfig(cm *v1.ConfigMap) (*Policies, error) {
}

policies := &Policies{}
if err := policies.buildPolicy(resPolicies); err != nil {
if err := policies.BuildPolicy(resPolicies); err != nil {
return nil, errors.WithStack(err)
}

Expand Down
20 changes: 10 additions & 10 deletions internal/resourcepolicies/resource_policies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ func TestLoadResourcePolicies(t *testing.T) {
}

func TestGetResourceMatchedAction(t *testing.T) {
resPolicies := &resourcePolicies{
resPolicies := &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -136,7 +136,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
},
},
{
Action: Action{Type: "volume-snapshot"},
Action: Action{Type: "snapshot"},
Conditions: map[string]interface{}{
"capacity": "10,100Gi",
"storageClass": []string{"gp2", "ebs-sc"},
Expand All @@ -147,7 +147,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
},
},
{
Action: Action{Type: "file-system-backup"},
Action: Action{Type: "fs-backup"},
Conditions: map[string]interface{}{
"storageClass": []string{"gp2", "ebs-sc"},
"csi": interface{}(
Expand Down Expand Up @@ -179,7 +179,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
storageClass: "ebs-sc",
csi: &csiVolumeSource{Driver: "aws.efs.csi.driver"},
},
expectedAction: &Action{Type: "volume-snapshot"},
expectedAction: &Action{Type: "snapshot"},
},
{
name: "dismatch all policies",
Expand All @@ -195,7 +195,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
policies := &Policies{}
err := policies.buildPolicy(resPolicies)
err := policies.BuildPolicy(resPolicies)
if err != nil {
t.Errorf("Failed to build policy with error %v", err)
}
Expand Down Expand Up @@ -237,9 +237,9 @@ func TestGetResourcePoliciesFromConfig(t *testing.T) {
// Check that the returned resourcePolicies object contains the expected data
assert.Equal(t, "v1", resPolicies.version)
assert.Len(t, resPolicies.volumePolicies, 1)
policies := resourcePolicies{
policies := ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Conditions: map[string]interface{}{
"capacity": "0,10Gi",
Expand All @@ -251,7 +251,7 @@ func TestGetResourcePoliciesFromConfig(t *testing.T) {
},
}
p := &Policies{}
err = p.buildPolicy(&policies)
err = p.BuildPolicy(&policies)
if err != nil {
t.Fatalf("failed to build policy with error %v", err)
}
Expand Down Expand Up @@ -424,7 +424,7 @@ volumePolicies:
}
assert.Nil(t, err)
policies := &Policies{}
err = policies.buildPolicy(resPolicies)
err = policies.BuildPolicy(resPolicies)
assert.Nil(t, err)
action, err := policies.GetMatchAction(tc.vol)
assert.Nil(t, err)
Expand Down
6 changes: 5 additions & 1 deletion internal/resourcepolicies/volume_resources_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ func decodeStruct(r io.Reader, s interface{}) error {
// validate check action format
func (a *Action) validate() error {
// validate Type
if a.Type != Skip {
valid := false
if a.Type == Skip || a.Type == Snapshot || a.Type == FSBackup {
valid = true
}
if !valid {
return fmt.Errorf("invalid action type %s", a.Type)
}

Expand Down
113 changes: 94 additions & 19 deletions internal/resourcepolicies/volume_resources_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ func TestCapacityConditionValidate(t *testing.T) {
func TestValidate(t *testing.T) {
testCases := []struct {
name string
res *resourcePolicies
res *ResourcePolicies
wantErr bool
}{
{
name: "unknown key in yaml",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -110,9 +110,9 @@ func TestValidate(t *testing.T) {
},
{
name: "error format of capacity",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -130,9 +130,9 @@ func TestValidate(t *testing.T) {
},
{
name: "error format of storageClass",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -150,9 +150,9 @@ func TestValidate(t *testing.T) {
},
{
name: "error format of csi",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -167,9 +167,9 @@ func TestValidate(t *testing.T) {
},
{
name: "unsupported version",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v2",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -186,9 +186,9 @@ func TestValidate(t *testing.T) {
},
{
name: "unsupported action",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "unsupported"},
Conditions: map[string]interface{}{
Expand All @@ -205,9 +205,9 @@ func TestValidate(t *testing.T) {
},
{
name: "error format of nfs",
res: &resourcePolicies{
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -221,10 +221,10 @@ func TestValidate(t *testing.T) {
wantErr: true,
},
{
name: "supported formart volume policies",
res: &resourcePolicies{
name: "supported format volume policies",
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []volumePolicy{
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
Expand All @@ -245,11 +245,86 @@ func TestValidate(t *testing.T) {
},
wantErr: false,
},
{
name: "supported format volume policies, action type snapshot",
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "snapshot"},
Conditions: map[string]interface{}{
"capacity": "0,10Gi",
"storageClass": []string{"gp2", "ebs-sc"},
"csi": interface{}(
map[string]interface{}{
"driver": "aws.efs.csi.driver",
}),
"nfs": interface{}(
map[string]interface{}{
"server": "192.168.20.90",
"path": "/mnt/data/",
}),
},
},
},
},
wantErr: false,
},
{
name: "supported format volume policies, action type fs-backup",
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: "fs-backup"},
Conditions: map[string]interface{}{
"capacity": "0,10Gi",
"storageClass": []string{"gp2", "ebs-sc"},
"csi": interface{}(
map[string]interface{}{
"driver": "aws.efs.csi.driver",
}),
"nfs": interface{}(
map[string]interface{}{
"server": "192.168.20.90",
"path": "/mnt/data/",
}),
},
},
},
},
wantErr: false,
},
{
name: "supported format volume policies, action type fs-backup and snapshot",
res: &ResourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
{
Action: Action{Type: Snapshot},
Conditions: map[string]interface{}{
"storageClass": []string{"gp2"},
},
},
{
Action: Action{Type: FSBackup},
Conditions: map[string]interface{}{
"nfs": interface{}(
map[string]interface{}{
"server": "192.168.20.90",
"path": "/mnt/data/",
}),
},
},
},
},
wantErr: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
policies := &Policies{}
err1 := policies.buildPolicy(tc.res)
err1 := policies.BuildPolicy(tc.res)
err2 := policies.Validate()

if tc.wantErr {
Expand Down
Loading

0 comments on commit 01a2d95

Please sign in to comment.