Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Volume Policies feature to support more actions #7664

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading