Adjust match resource policies

Signed-off-by: Ming <mqiu@vmware.com>
This commit is contained in:
Ming
2023-03-21 08:33:15 +00:00
parent 086dbd344f
commit a3cef5b0d3
16 changed files with 167 additions and 160 deletions

View File

@@ -430,8 +430,8 @@ spec:
"objectname".
nullable: true
type: object
resourcePolices:
description: ResourcePolicies specifies the referenced resource policies
resourcePolicy:
description: ResourcePolicy specifies the referenced resource policies
that backup should follow
properties:
apiGroup:

View File

@@ -466,8 +466,8 @@ spec:
simply use "objectname".
nullable: true
type: object
resourcePolices:
description: ResourcePolicies specifies the referenced resource
resourcePolicy:
description: ResourcePolicy specifies the referenced resource
policies that backup should follow
properties:
apiGroup:

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,11 @@ import (
type VolumeActionType string
const Skip VolumeActionType = "skip"
const (
// currently only support configmap type of resource config
ConfigmapRefType string = "configmap"
Skip VolumeActionType = "skip"
)
// Action defined as one action for a specific way of backup
type Action struct {
@@ -20,33 +24,30 @@ type Action struct {
Parameters map[string]interface{} `yaml:"parameters,omitempty"`
}
// VolumePolicy defined policy to conditions to match Volumes and related action to handle matched Volumes
type VolumePolicy struct {
// volumePolicy defined policy to conditions to match Volumes and related action to handle matched Volumes
type volumePolicy struct {
// Conditions defined list of conditions to match Volumes
Conditions map[string]interface{} `yaml:"conditions"`
Action Action `yaml:"action"`
}
// currently only support configmap type of resource config
const ConfigmapRefType string = "configmap"
// resourcePolicies currently defined slice of volume policies to handle backup
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
// OtherResourcePolicies []OtherResourcePolicy
}
type Policies struct {
Version string
VolumePolicies []volumePolicy
version string
volumePolicies []volPolicy
// OtherPolicies
}
func unmarshalResourcePolicies(YamlData *string) (*resourcePolicies, error) {
func unmarshalResourcePolicies(yamlData *string) (*resourcePolicies, error) {
resPolicies := &resourcePolicies{}
if err := decodeStruct(strings.NewReader(*YamlData), resPolicies); err != nil {
if err := decodeStruct(strings.NewReader(*yamlData), resPolicies); err != nil {
return nil, fmt.Errorf("failed to decode yaml data into resource policies %v", err)
} else {
return resPolicies, nil
@@ -57,59 +58,68 @@ func (policies *Policies) buildPolicy(resPolicies *resourcePolicies) error {
for _, vp := range resPolicies.VolumePolicies {
con, err := unmarshalVolConditions(vp.Conditions)
if err != nil {
return errors.Wrap(err, "failed to unmarshl volume conditions")
return errors.WithStack(err)
}
volCap, err := parseCapacity(con.Capacity)
if err != nil {
return errors.Wrapf(err, "failed to parse condition capacity %s", con.Capacity)
return errors.WithStack(err)
}
var p volumePolicy
var p volPolicy
p.action = vp.Action
p.conditions = append(p.conditions, &capacityCondition{capacity: *volCap})
p.conditions = append(p.conditions, &storageClassCondition{storageClass: con.StorageClass})
p.conditions = append(p.conditions, &nfsCondition{nfs: con.NFS})
p.conditions = append(p.conditions, &csiCondition{csi: con.CSI})
policies.VolumePolicies = append(policies.VolumePolicies, p)
policies.volumePolicies = append(policies.volumePolicies, p)
}
// Other resource policies
policies.Version = resPolicies.Version
policies.version = resPolicies.Version
return nil
}
func (p *Policies) Match(res interface{}) *Action {
vol, ok := res.(*StructuredVolume)
if ok {
for _, policy := range p.VolumePolicies {
isAllMatch := false
for _, con := range policy.conditions {
if !con.Match(vol) {
isAllMatch = false
break
}
isAllMatch = true
}
if isAllMatch {
return &policy.action
func (p *Policies) match(res *structuredVolume) *Action {
for _, policy := range p.volumePolicies {
isAllMatch := false
for _, con := range policy.conditions {
if !con.match(res) {
isAllMatch = false
break
}
isAllMatch = true
}
if isAllMatch {
return &policy.action
}
}
return nil
}
func (p *Policies) GetMatchAction(res interface{}) (*Action, error) {
structuredVolume := new(structuredVolume)
if pv, ok := res.(*v1.PersistentVolume); ok {
structuredVolume.parsePV(pv)
} else if volume, ok := res.(*v1.Volume); ok {
structuredVolume.parsePodVolume(volume)
} else {
return nil, errors.Errorf("failed to convert object")
}
return p.match(structuredVolume), nil
}
func (p *Policies) Validate() error {
if p.Version != currentSupportDataVersion {
return fmt.Errorf("incompatible version number %s with supported version %s", p.Version, currentSupportDataVersion)
if p.version != currentSupportDataVersion {
return fmt.Errorf("incompatible version number %s with supported version %s", p.version, currentSupportDataVersion)
}
for _, policy := range p.VolumePolicies {
if err := policy.action.Validate(); err != nil {
return errors.Wrap(err, "failed to validate config")
for _, policy := range p.volumePolicies {
if err := policy.action.validate(); err != nil {
return errors.WithStack(err)
}
for _, con := range policy.conditions {
if err := con.Validate(); err != nil {
return errors.Wrap(err, "failed to validate conditions config")
if err := con.validate(); err != nil {
return errors.WithStack(err)
}
}
}

View File

@@ -108,7 +108,7 @@ func TestLoadResourcePolicies(t *testing.T) {
func TestGetResourceMatchedAction(t *testing.T) {
resPolicies := &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -145,12 +145,12 @@ func TestGetResourceMatchedAction(t *testing.T) {
}
testCases := []struct {
name string
volume *StructuredVolume
volume *structuredVolume
expectedAction *Action
}{
{
name: "match policy",
volume: &StructuredVolume{
volume: &structuredVolume{
capacity: *resource.NewQuantity(5<<30, resource.BinarySI),
storageClass: "ebs-sc",
csi: &csiVolumeSource{Driver: "aws.efs.csi.driver"},
@@ -159,7 +159,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
},
{
name: "both matches return the first policy",
volume: &StructuredVolume{
volume: &structuredVolume{
capacity: *resource.NewQuantity(50<<30, resource.BinarySI),
storageClass: "ebs-sc",
csi: &csiVolumeSource{Driver: "aws.efs.csi.driver"},
@@ -168,7 +168,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
},
{
name: "dismatch all policies",
volume: &StructuredVolume{
volume: &structuredVolume{
capacity: *resource.NewQuantity(50<<30, resource.BinarySI),
storageClass: "ebs-sc",
nfs: &nFSVolumeSource{},
@@ -185,7 +185,7 @@ func TestGetResourceMatchedAction(t *testing.T) {
t.Errorf("Failed to build policy with error %v", err)
}
action := policies.Match(tc.volume)
action := policies.match(tc.volume)
if action == nil {
if tc.expectedAction != nil {
t.Errorf("Expected action %v, but got result nil", tc.expectedAction.Type)
@@ -220,11 +220,11 @@ func TestGetResourcePoliciesFromConfig(t *testing.T) {
assert.Nil(t, err)
// Check that the returned resourcePolicies object contains the expected data
assert.Equal(t, "v1", resPolicies.Version)
assert.Len(t, resPolicies.VolumePolicies, 1)
assert.Equal(t, "v1", resPolicies.version)
assert.Len(t, resPolicies.volumePolicies, 1)
policies := resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Conditions: map[string]interface{}{
"capacity": "0,10Gi",

View File

@@ -11,30 +11,30 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
)
type volumePolicy struct {
type volPolicy struct {
action Action
conditions []VolumeCondition
conditions []volumeCondition
}
type VolumeCondition interface {
Match(v *StructuredVolume) bool
Validate() error
type volumeCondition interface {
match(v *structuredVolume) bool
validate() error
}
// Capacity consist of the lower and upper boundary
type Capacity struct {
// capacity consist of the lower and upper boundary
type capacity struct {
lower resource.Quantity
upper resource.Quantity
}
type StructuredVolume struct {
type structuredVolume struct {
capacity resource.Quantity
storageClass string
nfs *nFSVolumeSource
csi *csiVolumeSource
}
func (s *StructuredVolume) ParsePV(pv *corev1api.PersistentVolume) {
func (s *structuredVolume) parsePV(pv *corev1api.PersistentVolume) {
s.capacity = *pv.Spec.Capacity.Storage()
s.storageClass = pv.Spec.StorageClassName
nfs := pv.Spec.NFS
@@ -48,7 +48,7 @@ func (s *StructuredVolume) ParsePV(pv *corev1api.PersistentVolume) {
}
}
func (s *StructuredVolume) ParsePodVolume(vol *corev1api.Volume) {
func (s *structuredVolume) parsePodVolume(vol *corev1api.Volume) {
nfs := vol.NFS
if nfs != nil {
s.nfs = &nFSVolumeSource{Server: nfs.Server, Path: nfs.Path}
@@ -61,10 +61,10 @@ func (s *StructuredVolume) ParsePodVolume(vol *corev1api.Volume) {
}
type capacityCondition struct {
capacity Capacity
capacity capacity
}
func (c *capacityCondition) Match(v *StructuredVolume) bool {
func (c *capacityCondition) match(v *structuredVolume) bool {
return c.capacity.isInRange(v.capacity)
}
@@ -72,7 +72,7 @@ type storageClassCondition struct {
storageClass []string
}
func (s *storageClassCondition) Match(v *StructuredVolume) bool {
func (s *storageClassCondition) match(v *structuredVolume) bool {
if len(s.storageClass) == 0 {
return true
}
@@ -94,7 +94,7 @@ type nfsCondition struct {
nfs *nFSVolumeSource
}
func (c *nfsCondition) Match(v *StructuredVolume) bool {
func (c *nfsCondition) match(v *structuredVolume) bool {
if c.nfs == nil {
return true
}
@@ -128,7 +128,7 @@ type csiCondition struct {
csi *csiVolumeSource
}
func (c *csiCondition) Match(v *StructuredVolume) bool {
func (c *csiCondition) match(v *structuredVolume) bool {
if c.csi == nil {
return true
}
@@ -141,14 +141,14 @@ func (c *csiCondition) Match(v *StructuredVolume) bool {
}
// parseCapacity parse string into capacity format
func parseCapacity(capacity string) (*Capacity, error) {
if capacity == "" {
capacity = ","
func parseCapacity(cap string) (*capacity, error) {
if cap == "" {
cap = ","
}
capacities := strings.Split(capacity, ",")
capacities := strings.Split(cap, ",")
var quantities []resource.Quantity
if len(capacities) != 2 {
return nil, fmt.Errorf("wrong format of Capacity %v", capacity)
return nil, fmt.Errorf("wrong format of Capacity %v", cap)
} else {
for _, v := range capacities {
if strings.TrimSpace(v) == "" {
@@ -164,11 +164,11 @@ func parseCapacity(capacity string) (*Capacity, error) {
}
}
}
return &Capacity{lower: quantities[0], upper: quantities[1]}, nil
return &capacity{lower: quantities[0], upper: quantities[1]}, nil
}
// isInRange returns true if the quantity y is in range of capacity, or it returns false
func (c *Capacity) isInRange(y resource.Quantity) bool {
func (c *capacity) isInRange(y resource.Quantity) bool {
if c.lower.IsZero() && c.upper.Cmp(y) >= 0 {
// [0, a] y
return true

View File

@@ -10,8 +10,8 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
)
func setStructuredVolume(capacity resource.Quantity, sc string, nfs *nFSVolumeSource, csi *csiVolumeSource) *StructuredVolume {
return &StructuredVolume{
func setStructuredVolume(capacity resource.Quantity, sc string, nfs *nFSVolumeSource, csi *csiVolumeSource) *structuredVolume {
return &structuredVolume{
capacity: capacity,
storageClass: sc,
nfs: nfs,
@@ -20,14 +20,14 @@ func setStructuredVolume(capacity resource.Quantity, sc string, nfs *nFSVolumeSo
}
func TestParseCapacity(t *testing.T) {
var emptyCapacity Capacity
var emptyCapacity capacity
tests := []struct {
input string
expected Capacity
expected capacity
expectedErr error
}{
{"10Gi,20Gi", Capacity{lower: *resource.NewQuantity(10<<30, resource.BinarySI), upper: *resource.NewQuantity(20<<30, resource.BinarySI)}, nil},
{"10Gi,", Capacity{lower: *resource.NewQuantity(10<<30, resource.BinarySI), upper: *resource.NewQuantity(0, resource.DecimalSI)}, nil},
{"10Gi,20Gi", capacity{lower: *resource.NewQuantity(10<<30, resource.BinarySI), upper: *resource.NewQuantity(20<<30, resource.BinarySI)}, nil},
{"10Gi,", capacity{lower: *resource.NewQuantity(10<<30, resource.BinarySI), upper: *resource.NewQuantity(0, resource.DecimalSI)}, nil},
{"10Gi", emptyCapacity, fmt.Errorf("wrong format of Capacity 10Gi")},
{"", emptyCapacity, nil},
}
@@ -49,18 +49,18 @@ func TestCapacityIsInRange(t *testing.T) {
t.Parallel()
tests := []struct {
capacity *Capacity
capacity *capacity
quantity resource.Quantity
isInRange bool
}{
{&Capacity{*resource.NewQuantity(0, resource.BinarySI), *resource.NewQuantity(10<<30, resource.BinarySI)}, *resource.NewQuantity(5<<30, resource.BinarySI), true},
{&Capacity{*resource.NewQuantity(0, resource.BinarySI), *resource.NewQuantity(10<<30, resource.BinarySI)}, *resource.NewQuantity(15<<30, resource.BinarySI), false},
{&Capacity{*resource.NewQuantity(20<<30, resource.BinarySI), *resource.NewQuantity(0, resource.DecimalSI)}, *resource.NewQuantity(25<<30, resource.BinarySI), true},
{&Capacity{*resource.NewQuantity(20<<30, resource.BinarySI), *resource.NewQuantity(0, resource.DecimalSI)}, *resource.NewQuantity(15<<30, resource.BinarySI), false},
{&Capacity{*resource.NewQuantity(10<<30, resource.BinarySI), *resource.NewQuantity(20<<30, resource.BinarySI)}, *resource.NewQuantity(15<<30, resource.BinarySI), true},
{&Capacity{*resource.NewQuantity(10<<30, resource.BinarySI), *resource.NewQuantity(20<<30, resource.BinarySI)}, *resource.NewQuantity(5<<30, resource.BinarySI), false},
{&Capacity{*resource.NewQuantity(10<<30, resource.BinarySI), *resource.NewQuantity(20<<30, resource.BinarySI)}, *resource.NewQuantity(25<<30, resource.BinarySI), false},
{&Capacity{*resource.NewQuantity(0, resource.BinarySI), *resource.NewQuantity(0, resource.BinarySI)}, *resource.NewQuantity(5<<30, resource.BinarySI), true},
{&capacity{*resource.NewQuantity(0, resource.BinarySI), *resource.NewQuantity(10<<30, resource.BinarySI)}, *resource.NewQuantity(5<<30, resource.BinarySI), true},
{&capacity{*resource.NewQuantity(0, resource.BinarySI), *resource.NewQuantity(10<<30, resource.BinarySI)}, *resource.NewQuantity(15<<30, resource.BinarySI), false},
{&capacity{*resource.NewQuantity(20<<30, resource.BinarySI), *resource.NewQuantity(0, resource.DecimalSI)}, *resource.NewQuantity(25<<30, resource.BinarySI), true},
{&capacity{*resource.NewQuantity(20<<30, resource.BinarySI), *resource.NewQuantity(0, resource.DecimalSI)}, *resource.NewQuantity(15<<30, resource.BinarySI), false},
{&capacity{*resource.NewQuantity(10<<30, resource.BinarySI), *resource.NewQuantity(20<<30, resource.BinarySI)}, *resource.NewQuantity(15<<30, resource.BinarySI), true},
{&capacity{*resource.NewQuantity(10<<30, resource.BinarySI), *resource.NewQuantity(20<<30, resource.BinarySI)}, *resource.NewQuantity(5<<30, resource.BinarySI), false},
{&capacity{*resource.NewQuantity(10<<30, resource.BinarySI), *resource.NewQuantity(20<<30, resource.BinarySI)}, *resource.NewQuantity(25<<30, resource.BinarySI), false},
{&capacity{*resource.NewQuantity(0, resource.BinarySI), *resource.NewQuantity(0, resource.BinarySI)}, *resource.NewQuantity(5<<30, resource.BinarySI), true},
}
for _, test := range tests {
@@ -80,7 +80,7 @@ func TestStorageClassConditionMatch(t *testing.T) {
tests := []struct {
name string
condition *storageClassCondition
volume *StructuredVolume
volume *structuredVolume
expectedMatch bool
}{
{
@@ -117,7 +117,7 @@ func TestStorageClassConditionMatch(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
match := tt.condition.Match(tt.volume)
match := tt.condition.match(tt.volume)
if match != tt.expectedMatch {
t.Errorf("expected %v, but got %v", tt.expectedMatch, match)
}
@@ -130,7 +130,7 @@ func TestNFSConditionMatch(t *testing.T) {
tests := []struct {
name string
condition *nfsCondition
volume *StructuredVolume
volume *structuredVolume
expectedMatch bool
}{
{
@@ -172,7 +172,7 @@ func TestNFSConditionMatch(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
match := tt.condition.Match(tt.volume)
match := tt.condition.match(tt.volume)
if match != tt.expectedMatch {
t.Errorf("expected %v, but got %v", tt.expectedMatch, match)
}
@@ -185,7 +185,7 @@ func TestCSIConditionMatch(t *testing.T) {
tests := []struct {
name string
condition *csiCondition
volume *StructuredVolume
volume *structuredVolume
expectedMatch bool
}{
{
@@ -209,7 +209,7 @@ func TestCSIConditionMatch(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
match := tt.condition.Match(tt.volume)
match := tt.condition.match(tt.volume)
if match != tt.expectedMatch {
t.Errorf("expected %v, but got %v", tt.expectedMatch, match)
}
@@ -322,8 +322,8 @@ func TestParsePodVolume(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Call the function
structuredVolume := &StructuredVolume{}
structuredVolume.ParsePodVolume(tc.inputVolume)
structuredVolume := &structuredVolume{}
structuredVolume.parsePodVolume(tc.inputVolume)
// Check the results
if tc.expectedNFS != nil {
@@ -384,11 +384,11 @@ func TestParsePV(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Call the function
structuredVolume := &StructuredVolume{}
structuredVolume.ParsePV(tc.inputVolume)
structuredVolume := &structuredVolume{}
structuredVolume.parsePV(tc.inputVolume)
// Check the results
if structuredVolume.capacity != *tc.inputVolume.Spec.Capacity.Storage() {
t.Errorf("Capacity does not match expected value")
t.Errorf("capacity does not match expected value")
}
if structuredVolume.storageClass != tc.inputVolume.Spec.StorageClassName {
t.Errorf("Storage class does not match expected value")

View File

@@ -29,7 +29,7 @@ type volumeConditions struct {
CSI *csiVolumeSource `yaml:"csi,omitempty"`
}
func (c *capacityCondition) Validate() error {
func (c *capacityCondition) validate() error {
// [0, a]
// [a, b]
// [b, 0]
@@ -42,17 +42,17 @@ func (c *capacityCondition) Validate() error {
}
func (s *storageClassCondition) Validate() error {
func (s *storageClassCondition) validate() error {
// validate by yamlv3
return nil
}
func (c *nfsCondition) Validate() error {
func (c *nfsCondition) validate() error {
// validate by yamlv3
return nil
}
func (c *csiCondition) Validate() error {
func (c *csiCondition) validate() error {
// validate by yamlv3
return nil
}
@@ -64,9 +64,9 @@ func decodeStruct(r io.Reader, s interface{}) error {
return dec.Decode(s)
}
// Validate check action format
func (a *Action) Validate() error {
// Validate Type
// validate check action format
func (a *Action) validate() error {
// validate Type
if a.Type != Skip {
return fmt.Errorf("invalid action type %s", a.Type)
}

View File

@@ -9,47 +9,47 @@ import (
func TestCapacityConditionValidate(t *testing.T) {
testCases := []struct {
name string
capacity *Capacity
capacity *capacity
wantErr bool
}{
{
name: "lower and upper are both zero",
capacity: &Capacity{lower: *resource.NewQuantity(0, resource.DecimalSI), upper: *resource.NewQuantity(0, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(0, resource.DecimalSI), upper: *resource.NewQuantity(0, resource.DecimalSI)},
wantErr: false,
},
{
name: "lower is zero and upper is greater than zero",
capacity: &Capacity{lower: *resource.NewQuantity(0, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(0, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
wantErr: false,
},
{
name: "lower is greater than upper",
capacity: &Capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(50, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(50, resource.DecimalSI)},
wantErr: true,
},
{
name: "lower and upper are equal",
capacity: &Capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
wantErr: false,
},
{
name: "lower is greater than zero and upper is zero",
capacity: &Capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(0, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(0, resource.DecimalSI)},
wantErr: false,
},
{
name: "lower and upper are both not zero and lower is less than upper",
capacity: &Capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(200, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(200, resource.DecimalSI)},
wantErr: false,
},
{
name: "lower and upper are both not zero and lower is equal to upper",
capacity: &Capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(100, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
wantErr: false,
},
{
name: "lower and upper are both not zero and lower is greater than upper",
capacity: &Capacity{lower: *resource.NewQuantity(200, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
capacity: &capacity{lower: *resource.NewQuantity(200, resource.DecimalSI), upper: *resource.NewQuantity(100, resource.DecimalSI)},
wantErr: true,
},
}
@@ -57,7 +57,7 @@ func TestCapacityConditionValidate(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := &capacityCondition{capacity: *tc.capacity}
err := c.Validate()
err := c.validate()
if (err != nil) != tc.wantErr {
t.Fatalf("Expected error %v, but got error %v", tc.wantErr, err)
@@ -76,7 +76,7 @@ func TestValidate(t *testing.T) {
name: "unknown key in yaml",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -97,7 +97,7 @@ func TestValidate(t *testing.T) {
name: "error format of capacity",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -117,7 +117,7 @@ func TestValidate(t *testing.T) {
name: "error format of storageClass",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -137,7 +137,7 @@ func TestValidate(t *testing.T) {
name: "error format of csi",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -154,7 +154,7 @@ func TestValidate(t *testing.T) {
name: "unsupported version",
res: &resourcePolicies{
Version: "v2",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -173,7 +173,7 @@ func TestValidate(t *testing.T) {
name: "unsupported action",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "unsupported"},
Conditions: map[string]interface{}{
@@ -192,7 +192,7 @@ func TestValidate(t *testing.T) {
name: "error format of nfs",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{
@@ -209,7 +209,7 @@ func TestValidate(t *testing.T) {
name: "supported formart volume policies",
res: &resourcePolicies{
Version: "v1",
VolumePolicies: []VolumePolicy{
VolumePolicies: []volumePolicy{
{
Action: Action{Type: "skip"},
Conditions: map[string]interface{}{

View File

@@ -160,9 +160,9 @@ type BackupSpec struct {
// The default value is 1 hour.
// +optional
ItemOperationTimeout metav1.Duration `json:"itemOperationTimeout,omitempty"`
// ResourcePolicies specifies the referenced resource policies that backup should follow
// ResourcePolicy specifies the referenced resource policies that backup should follow
// +optional
ResourcePolicies *v1.TypedLocalObjectReference `json:"resourcePolices,omitempty"`
ResourcePolicy *v1.TypedLocalObjectReference `json:"resourcePolicy,omitempty"`
}
// BackupHooks contains custom behaviors that should be executed at different phases of the backup.

View File

@@ -371,8 +371,8 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) {
}
out.CSISnapshotTimeout = in.CSISnapshotTimeout
out.ItemOperationTimeout = in.ItemOperationTimeout
if in.ResourcePolicies != nil {
in, out := &in.ResourcePolicies, &out.ResourcePolicies
if in.ResourcePolicy != nil {
in, out := &in.ResourcePolicy, &out.ResourcePolicy
*out = new(corev1.TypedLocalObjectReference)
(*in).DeepCopyInto(*out)
}

View File

@@ -324,7 +324,7 @@ func (ib *itemBackupper) executeActions(
}
log.Info("Executing custom action")
if act, err := ib.checkResourcePolicies(obj, &groupResource, action.Name()); err != nil {
if act, err := ib.getMatchAction(obj, groupResource, action.Name()); err != nil {
return nil, itemFiles, errors.WithStack(err)
} else if act != nil && act.Type == resourcepolicies.Skip {
log.Infof("skip snapshot of pvc %s/%s bound pv for the matched resource policies", namespace, name)
@@ -484,10 +484,10 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie
}
if ib.backupRequest.ResPolicies != nil {
structuredVolume := &resourcepolicies.StructuredVolume{}
structuredVolume.ParsePV(pv)
action := ib.backupRequest.ResPolicies.Match(structuredVolume)
if action != nil && action.Type == resourcepolicies.Skip {
if action, err := ib.backupRequest.ResPolicies.GetMatchAction(pv); err != nil {
log.WithError(err).Errorf("Error getting matched resource policies for pv %s", pv.Name)
return nil
} else if action != nil && action.Type == resourcepolicies.Skip {
log.Infof("skip snapshot of pv %s for the matched resource policies", pv.Name)
return nil
}
@@ -579,8 +579,8 @@ func (ib *itemBackupper) takePVSnapshot(obj runtime.Unstructured, log logrus.Fie
return kubeerrs.NewAggregate(errs)
}
func (ib *itemBackupper) checkResourcePolicies(obj runtime.Unstructured, groupResource *schema.GroupResource, actionName string) (*resourcepolicies.Action, error) {
if ib.backupRequest.ResPolicies != nil && groupResource.String() == "persistentvolumeclaims" && actionName == "velero.io/csi-pvc-backupper" {
func (ib *itemBackupper) getMatchAction(obj runtime.Unstructured, groupResource schema.GroupResource, backupItemActionName string) (*resourcepolicies.Action, error) {
if ib.backupRequest.ResPolicies != nil && groupResource == kuberesource.PersistentVolumeClaims && backupItemActionName == "velero.io/csi-pvc-backupper" {
pvc := corev1api.PersistentVolumeClaim{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &pvc); err != nil {
return nil, errors.WithStack(err)
@@ -595,10 +595,7 @@ func (ib *itemBackupper) checkResourcePolicies(obj runtime.Unstructured, groupRe
if err := ib.kbClient.Get(context.Background(), kbClient.ObjectKey{Name: pvName}, pv); err != nil {
return nil, errors.WithStack(err)
}
volume := resourcepolicies.StructuredVolume{}
volume.ParsePV(pv)
return ib.backupRequest.ResPolicies.Match(&volume), nil
return ib.backupRequest.ResPolicies.GetMatchAction(pv)
}
return nil, nil
}

View File

@@ -125,8 +125,8 @@ func (b *BackupBuilder) FromSchedule(schedule *velerov1api.Schedule) *BackupBuil
})
}
if schedule.Spec.Template.ResourcePolicies != nil {
b.ResourcePolicies(schedule.Spec.Template.ResourcePolicies.Name)
if schedule.Spec.Template.ResourcePolicy != nil {
b.ResourcePolicies(schedule.Spec.Template.ResourcePolicy.Name)
}
return b
@@ -282,8 +282,8 @@ func (b *BackupBuilder) ItemOperationTimeout(timeout time.Duration) *BackupBuild
return b
}
// resourcePolicies sets the Backup's resource polices.
// ResourcePolicies sets the Backup's resource polices.
func (b *BackupBuilder) ResourcePolicies(name string) *BackupBuilder {
b.object.Spec.ResourcePolicies = &v1.TypedLocalObjectReference{Kind: resourcepolicies.ConfigmapRefType, Name: name}
b.object.Spec.ResourcePolicy = &v1.TypedLocalObjectReference{Kind: resourcepolicies.ConfigmapRefType, Name: name}
return b
}

View File

@@ -161,7 +161,7 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
}
if o.BackupOptions.ResPoliciesConfigmap != "" {
schedule.Spec.Template.ResourcePolicies = &v1.TypedLocalObjectReference{Kind: resourcepolicies.ConfigmapRefType, Name: o.BackupOptions.ResPoliciesConfigmap}
schedule.Spec.Template.ResourcePolicy = &v1.TypedLocalObjectReference{Kind: resourcepolicies.ConfigmapRefType, Name: o.BackupOptions.ResPoliciesConfigmap}
}
if printed, err := output.PrintWithFormat(c, schedule); printed || err != nil {

View File

@@ -460,17 +460,17 @@ func (b *backupReconciler) prepareBackupRequest(backup *velerov1api.Backup, logg
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("encountered labelSelector as well as orLabelSelectors in backup spec, only one can be specified"))
}
if request.Spec.ResourcePolicies != nil && request.Spec.ResourcePolicies.Kind == resourcepolicies.ConfigmapRefType {
if request.Spec.ResourcePolicy != nil && request.Spec.ResourcePolicy.Kind == resourcepolicies.ConfigmapRefType {
policiesConfigmap := &v1.ConfigMap{}
err := b.kbClient.Get(context.Background(), kbclient.ObjectKey{Namespace: request.Namespace, Name: request.Spec.ResourcePolicies.Name}, policiesConfigmap)
err := b.kbClient.Get(context.Background(), kbclient.ObjectKey{Namespace: request.Namespace, Name: request.Spec.ResourcePolicy.Name}, policiesConfigmap)
if err != nil {
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("failed to get resource policies %s/%s configmap with err %v", request.Namespace, request.Spec.ResourcePolicies.Name, err))
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("failed to get resource policies %s/%s configmap with err %v", request.Namespace, request.Spec.ResourcePolicy.Name, err))
}
res, err := resourcepolicies.GetResourcePoliciesFromConfig(policiesConfigmap)
if err != nil {
request.Status.ValidationErrors = append(request.Status.ValidationErrors, errors.Wrapf(err, fmt.Sprintf("resource policies %s/%s", request.Namespace, request.Spec.ResourcePolicies.Name)).Error())
request.Status.ValidationErrors = append(request.Status.ValidationErrors, errors.Wrapf(err, fmt.Sprintf("resource policies %s/%s", request.Namespace, request.Spec.ResourcePolicy.Name)).Error())
} else if err = res.Validate(); err != nil {
request.Status.ValidationErrors = append(request.Status.ValidationErrors, errors.Wrapf(err, fmt.Sprintf("resource policies %s/%s", request.Namespace, request.Spec.ResourcePolicies.Name)).Error())
request.Status.ValidationErrors = append(request.Status.ValidationErrors, errors.Wrapf(err, fmt.Sprintf("resource policies %s/%s", request.Namespace, request.Spec.ResourcePolicy.Name)).Error())
}
request.ResPolicies = res
}

View File

@@ -111,20 +111,20 @@ func resultsKey(ns, name string) string {
return fmt.Sprintf("%s/%s", ns, name)
}
func (b *backupper) checkResourcePolicies(resPolicies *resourcepolicies.Policies, pvc *corev1api.PersistentVolumeClaim, volume *corev1api.Volume) (*resourcepolicies.Action, error) {
structuredVolume := &resourcepolicies.StructuredVolume{}
func (b *backupper) getMatchAction(resPolicies *resourcepolicies.Policies, pvc *corev1api.PersistentVolumeClaim, volume *corev1api.Volume) (*resourcepolicies.Action, error) {
if pvc != nil {
pv, err := b.pvClient.PersistentVolumes().Get(context.TODO(), pvc.Spec.VolumeName, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrapf(err, "error getting pv for pvc %s", pvc.Spec.VolumeName)
}
structuredVolume.ParsePV(pv)
} else if volume != nil {
structuredVolume.ParsePodVolume(volume)
} else {
return nil, errors.Errorf("failed to check resource policies for empty volume")
return resPolicies.GetMatchAction(pv)
}
return resPolicies.Match(structuredVolume), nil
if volume != nil {
return resPolicies.GetMatchAction(volume)
}
return nil, errors.Errorf("failed to check resource policies for empty volume")
}
func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.Pod, volumesToBackup []string, resPolicies *resourcepolicies.Policies, log logrus.FieldLogger) ([]*velerov1api.PodVolumeBackup, []error) {
@@ -219,7 +219,7 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.
}
if resPolicies != nil {
if action, err := b.checkResourcePolicies(resPolicies, pvc, &volume); err != nil {
if action, err := b.getMatchAction(resPolicies, pvc, &volume); err != nil {
errs = append(errs, errors.Wrapf(err, "error getting pv for pvc %s", pvc.Spec.VolumeName))
continue
} else if action != nil && action.Type == resourcepolicies.Skip {