Skip to content

Commit 55737b9

Browse files
authored
Merge pull request #9574 from blackpiglet/xj014661/main/ephemeral_storage_config
Add ephemeral storage limit and request support for data mover and maintenance job
2 parents 23a3c24 + ffea850 commit 55737b9

14 files changed

Lines changed: 187 additions & 48 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add ephemeral storage limit and request support for data mover and maintenance job

pkg/cmd/cli/install/install.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,21 @@ func (o *Options) AsVeleroOptions() (*install.VeleroOptions, error) {
275275
return nil, err
276276
}
277277
}
278-
veleroPodResources, err := kubeutil.ParseResourceRequirements(o.VeleroPodCPURequest, o.VeleroPodMemRequest, o.VeleroPodCPULimit, o.VeleroPodMemLimit)
278+
veleroPodResources, err := kubeutil.ParseCPUAndMemoryResources(
279+
o.VeleroPodCPURequest,
280+
o.VeleroPodMemRequest,
281+
o.VeleroPodCPULimit,
282+
o.VeleroPodMemLimit,
283+
)
279284
if err != nil {
280285
return nil, err
281286
}
282-
nodeAgentPodResources, err := kubeutil.ParseResourceRequirements(o.NodeAgentPodCPURequest, o.NodeAgentPodMemRequest, o.NodeAgentPodCPULimit, o.NodeAgentPodMemLimit)
287+
nodeAgentPodResources, err := kubeutil.ParseCPUAndMemoryResources(
288+
o.NodeAgentPodCPURequest,
289+
o.NodeAgentPodMemRequest,
290+
o.NodeAgentPodCPULimit,
291+
o.NodeAgentPodMemLimit,
292+
)
283293
if err != nil {
284294
return nil, err
285295
}

pkg/cmd/cli/nodeagent/server.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,25 @@ func (s *nodeAgentServer) run() {
323323

324324
podResources := corev1api.ResourceRequirements{}
325325
if s.dataPathConfigs != nil && s.dataPathConfigs.PodResources != nil {
326-
if res, err := kube.ParseResourceRequirements(s.dataPathConfigs.PodResources.CPURequest, s.dataPathConfigs.PodResources.MemoryRequest, s.dataPathConfigs.PodResources.CPULimit, s.dataPathConfigs.PodResources.MemoryLimit); err != nil {
326+
// To make the PodResources ConfigMap without ephemeral storage request/limit backward compatible,
327+
// need to avoid set value as empty, because empty string will cause parsing error.
328+
ephemeralStorageRequest := constant.DefaultEphemeralStorageRequest
329+
if s.dataPathConfigs.PodResources.EphemeralStorageRequest != "" {
330+
ephemeralStorageRequest = s.dataPathConfigs.PodResources.EphemeralStorageRequest
331+
}
332+
ephemeralStorageLimit := constant.DefaultEphemeralStorageLimit
333+
if s.dataPathConfigs.PodResources.EphemeralStorageLimit != "" {
334+
ephemeralStorageLimit = s.dataPathConfigs.PodResources.EphemeralStorageLimit
335+
}
336+
337+
if res, err := kube.ParseResourceRequirements(
338+
s.dataPathConfigs.PodResources.CPURequest,
339+
s.dataPathConfigs.PodResources.MemoryRequest,
340+
ephemeralStorageRequest,
341+
s.dataPathConfigs.PodResources.CPULimit,
342+
s.dataPathConfigs.PodResources.MemoryLimit,
343+
ephemeralStorageLimit,
344+
); err != nil {
327345
s.logger.WithError(err).Warn("Pod resource requirements are invalid, ignore")
328346
} else {
329347
podResources = res

pkg/constant/constant.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ const (
2323

2424
PluginCSIPVCRestoreRIA = "velero.io/csi-pvc-restorer"
2525
PluginCsiVolumeSnapshotRestoreRIA = "velero.io/csi-volumesnapshot-restorer"
26+
27+
DefaultEphemeralStorageRequest = "0"
28+
DefaultEphemeralStorageLimit = "0"
2629
)

pkg/repository/maintenance/maintenance.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"sigs.k8s.io/controller-runtime/pkg/client"
3939

4040
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
41+
"github.com/vmware-tanzu/velero/pkg/constant"
4142
velerolabel "github.com/vmware-tanzu/velero/pkg/label"
4243
velerotypes "github.com/vmware-tanzu/velero/pkg/types"
4344
"github.com/vmware-tanzu/velero/pkg/util"
@@ -574,15 +575,32 @@ func buildJob(
574575
// Set resource limits and requests
575576
cpuRequest := DefaultMaintenanceJobCPURequest
576577
memRequest := DefaultMaintenanceJobMemRequest
578+
ephemeralStorageRequest := constant.DefaultEphemeralStorageRequest
577579
cpuLimit := DefaultMaintenanceJobCPULimit
578580
memLimit := DefaultMaintenanceJobMemLimit
581+
ephemeralStorageLimit := constant.DefaultEphemeralStorageLimit
579582
if config != nil && config.PodResources != nil {
580583
cpuRequest = config.PodResources.CPURequest
581584
memRequest = config.PodResources.MemoryRequest
582585
cpuLimit = config.PodResources.CPULimit
583586
memLimit = config.PodResources.MemoryLimit
587+
// To make the PodResources ConfigMap without ephemeral storage request/limit backward compatible,
588+
// need to avoid set value as empty, because empty string will cause parsing error.
589+
if config.PodResources.EphemeralStorageRequest != "" {
590+
ephemeralStorageRequest = config.PodResources.EphemeralStorageRequest
591+
}
592+
if config.PodResources.EphemeralStorageLimit != "" {
593+
ephemeralStorageLimit = config.PodResources.EphemeralStorageLimit
594+
}
584595
}
585-
resources, err := kube.ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit)
596+
resources, err := kube.ParseResourceRequirements(
597+
cpuRequest,
598+
memRequest,
599+
ephemeralStorageRequest,
600+
cpuLimit,
601+
memLimit,
602+
ephemeralStorageLimit,
603+
)
586604
if err != nil {
587605
return nil, errors.Wrap(err, "failed to parse resource requirements for maintenance job")
588606
}

pkg/restore/actions/pod_volume_restore_action.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,19 @@ func (a *PodVolumeRestoreAction) Execute(input *velero.RestoreItemActionExecuteI
163163
memLimit = defaultMemRequestLimit
164164
}
165165

166-
resourceReqs, err := kube.ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit)
166+
resourceReqs, err := kube.ParseCPUAndMemoryResources(
167+
cpuRequest,
168+
memRequest,
169+
cpuLimit,
170+
memLimit,
171+
)
167172
if err != nil {
168173
log.Errorf("couldn't parse resource requirements: %s.", err)
169-
resourceReqs, _ = kube.ParseResourceRequirements(
170-
defaultCPURequestLimit, defaultMemRequestLimit, // requests
171-
defaultCPURequestLimit, defaultMemRequestLimit, // limits
174+
resourceReqs, _ = kube.ParseCPUAndMemoryResources(
175+
defaultCPURequestLimit,
176+
defaultMemRequestLimit,
177+
defaultCPURequestLimit,
178+
defaultMemRequestLimit,
172179
)
173180
}
174181

pkg/restore/actions/pod_volume_restore_action_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,11 @@ func TestGetImage(t *testing.T) {
117117

118118
// TestPodVolumeRestoreActionExecute tests the pod volume restore item action plugin's Execute method.
119119
func TestPodVolumeRestoreActionExecute(t *testing.T) {
120-
resourceReqs, _ := kube.ParseResourceRequirements(
121-
defaultCPURequestLimit, defaultMemRequestLimit, // requests
122-
defaultCPURequestLimit, defaultMemRequestLimit, // limits
120+
resourceReqs, _ := kube.ParseCPUAndMemoryResources(
121+
defaultCPURequestLimit,
122+
defaultMemRequestLimit,
123+
defaultCPURequestLimit,
124+
defaultMemRequestLimit,
123125
)
124126
id := int64(1000)
125127
securityContext := corev1api.SecurityContext{

pkg/util/kube/pod.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ type LoadAffinity struct {
4040
}
4141

4242
type PodResources struct {
43-
CPURequest string `json:"cpuRequest,omitempty"`
44-
MemoryRequest string `json:"memoryRequest,omitempty"`
45-
CPULimit string `json:"cpuLimit,omitempty"`
46-
MemoryLimit string `json:"memoryLimit,omitempty"`
43+
CPURequest string `json:"cpuRequest,omitempty"`
44+
CPULimit string `json:"cpuLimit,omitempty"`
45+
MemoryRequest string `json:"memoryRequest,omitempty"`
46+
MemoryLimit string `json:"memoryLimit,omitempty"`
47+
EphemeralStorageRequest string `json:"ephemeralStorageRequest,omitempty"`
48+
EphemeralStorageLimit string `json:"ephemeralStorageLimit,omitempty"`
4749
}
4850

4951
// IsPodRunning does a well-rounded check to make sure the specified pod is running stably.

pkg/util/kube/resource_requirements.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,34 @@ import (
2020
"github.com/pkg/errors"
2121
corev1api "k8s.io/api/core/v1"
2222
"k8s.io/apimachinery/pkg/api/resource"
23+
24+
"github.com/vmware-tanzu/velero/pkg/constant"
2325
)
2426

25-
// ParseResourceRequirements takes a set of CPU and memory requests and limit string
27+
// ParseCPUAndMemoryResources is a helper function that parses CPU and memory requests and limits,
28+
// using default values for ephemeral storage.
29+
func ParseCPUAndMemoryResources(cpuRequest, memRequest, cpuLimit, memLimit string) (corev1api.ResourceRequirements, error) {
30+
return ParseResourceRequirements(
31+
cpuRequest,
32+
memRequest,
33+
constant.DefaultEphemeralStorageRequest,
34+
cpuLimit,
35+
memLimit,
36+
constant.DefaultEphemeralStorageLimit,
37+
)
38+
}
39+
40+
// ParseResourceRequirements takes a set of CPU, memory, ephemeral storage requests and limit string
2641
// values and returns a ResourceRequirements struct to be used in a Container.
2742
// An error is returned if we cannot parse the request/limit.
28-
func ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit string) (corev1api.ResourceRequirements, error) {
43+
func ParseResourceRequirements(
44+
cpuRequest,
45+
memRequest,
46+
ephemeralStorageRequest,
47+
cpuLimit,
48+
memLimit,
49+
ephemeralStorageLimit string,
50+
) (corev1api.ResourceRequirements, error) {
2951
resources := corev1api.ResourceRequirements{
3052
Requests: corev1api.ResourceList{},
3153
Limits: corev1api.ResourceList{},
@@ -41,6 +63,11 @@ func ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit string
4163
return resources, errors.Wrapf(err, `couldn't parse memory request "%s"`, memRequest)
4264
}
4365

66+
parsedEphemeralStorageRequest, err := resource.ParseQuantity(ephemeralStorageRequest)
67+
if err != nil {
68+
return resources, errors.Wrapf(err, `couldn't parse ephemeral storage request "%s"`, ephemeralStorageRequest)
69+
}
70+
4471
parsedCPULimit, err := resource.ParseQuantity(cpuLimit)
4572
if err != nil {
4673
return resources, errors.Wrapf(err, `couldn't parse CPU limit "%s"`, cpuLimit)
@@ -51,6 +78,11 @@ func ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit string
5178
return resources, errors.Wrapf(err, `couldn't parse memory limit "%s"`, memLimit)
5279
}
5380

81+
parsedEphemeralStorageLimit, err := resource.ParseQuantity(ephemeralStorageLimit)
82+
if err != nil {
83+
return resources, errors.Wrapf(err, `couldn't parse ephemeral storage limit "%s"`, ephemeralStorageLimit)
84+
}
85+
5486
// A quantity of 0 is treated as unbounded
5587
unbounded := resource.MustParse("0")
5688

@@ -62,19 +94,29 @@ func ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit string
6294
return resources, errors.WithStack(errors.Errorf(`Memory request "%s" must be less than or equal to Memory limit "%s"`, memRequest, memLimit))
6395
}
6496

97+
if parsedEphemeralStorageLimit != unbounded && parsedEphemeralStorageRequest.Cmp(parsedEphemeralStorageLimit) > 0 {
98+
return resources, errors.WithStack(errors.Errorf(`Ephemeral storage request "%s" must be less than or equal to Ephemeral storage limit "%s"`, ephemeralStorageRequest, ephemeralStorageLimit))
99+
}
100+
65101
// Only set resources if they are not unbounded
66102
if parsedCPURequest != unbounded {
67103
resources.Requests[corev1api.ResourceCPU] = parsedCPURequest
68104
}
69105
if parsedMemRequest != unbounded {
70106
resources.Requests[corev1api.ResourceMemory] = parsedMemRequest
71107
}
108+
if parsedEphemeralStorageRequest != unbounded {
109+
resources.Requests[corev1api.ResourceEphemeralStorage] = parsedEphemeralStorageRequest
110+
}
72111
if parsedCPULimit != unbounded {
73112
resources.Limits[corev1api.ResourceCPU] = parsedCPULimit
74113
}
75114
if parsedMemLimit != unbounded {
76115
resources.Limits[corev1api.ResourceMemory] = parsedMemLimit
77116
}
117+
if parsedEphemeralStorageLimit != unbounded {
118+
resources.Limits[corev1api.ResourceEphemeralStorage] = parsedEphemeralStorageLimit
119+
}
78120

79121
return resources, nil
80122
}

pkg/util/kube/resource_requirements_test.go

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,54 +27,74 @@ import (
2727

2828
func TestParseResourceRequirements(t *testing.T) {
2929
type args struct {
30-
cpuRequest string
31-
memRequest string
32-
cpuLimit string
33-
memLimit string
30+
cpuRequest string
31+
memRequest string
32+
ephemeralStorageRequest string
33+
cpuLimit string
34+
memLimit string
35+
ephemeralStorageLimit string
3436
}
3537
tests := []struct {
3638
name string
3739
args args
3840
wantErr bool
3941
expected *corev1api.ResourceRequirements
4042
}{
41-
{"unbounded quantities", args{"0", "0", "0", "0"}, false, &corev1api.ResourceRequirements{
43+
{"unbounded quantities", args{"0", "0", "0", "0", "0", "0"}, false, &corev1api.ResourceRequirements{
4244
Requests: corev1api.ResourceList{},
4345
Limits: corev1api.ResourceList{},
4446
}},
45-
{"valid quantities", args{"100m", "128Mi", "200m", "256Mi"}, false, nil},
46-
{"CPU request with unbounded limit", args{"100m", "128Mi", "0", "256Mi"}, false, &corev1api.ResourceRequirements{
47+
{"valid quantities", args{"100m", "128Mi", "5Gi", "200m", "256Mi", "10Gi"}, false, nil},
48+
{"CPU request with unbounded limit", args{"100m", "128Mi", "5Gi", "0", "256Mi", "10Gi"}, false, &corev1api.ResourceRequirements{
4749
Requests: corev1api.ResourceList{
48-
corev1api.ResourceCPU: resource.MustParse("100m"),
49-
corev1api.ResourceMemory: resource.MustParse("128Mi"),
50+
corev1api.ResourceCPU: resource.MustParse("100m"),
51+
corev1api.ResourceMemory: resource.MustParse("128Mi"),
52+
corev1api.ResourceEphemeralStorage: resource.MustParse("5Gi"),
5053
},
5154
Limits: corev1api.ResourceList{
52-
corev1api.ResourceMemory: resource.MustParse("256Mi"),
55+
corev1api.ResourceMemory: resource.MustParse("256Mi"),
56+
corev1api.ResourceEphemeralStorage: resource.MustParse("10Gi"),
57+
},
58+
}},
59+
{"Mem request with unbounded limit", args{"100m", "128Mi", "5Gi", "200m", "0", "10Gi"}, false, &corev1api.ResourceRequirements{
60+
Requests: corev1api.ResourceList{
61+
corev1api.ResourceCPU: resource.MustParse("100m"),
62+
corev1api.ResourceMemory: resource.MustParse("128Mi"),
63+
corev1api.ResourceEphemeralStorage: resource.MustParse("5Gi"),
64+
},
65+
Limits: corev1api.ResourceList{
66+
corev1api.ResourceCPU: resource.MustParse("200m"),
67+
corev1api.ResourceEphemeralStorage: resource.MustParse("10Gi"),
5368
},
5469
}},
55-
{"Mem request with unbounded limit", args{"100m", "128Mi", "200m", "0"}, false, &corev1api.ResourceRequirements{
70+
{"Ephemeral storage request with unbounded limit", args{"100m", "128Mi", "5Gi", "200m", "256Mi", "0"}, false, &corev1api.ResourceRequirements{
5671
Requests: corev1api.ResourceList{
57-
corev1api.ResourceCPU: resource.MustParse("100m"),
58-
corev1api.ResourceMemory: resource.MustParse("128Mi"),
72+
corev1api.ResourceCPU: resource.MustParse("100m"),
73+
corev1api.ResourceMemory: resource.MustParse("128Mi"),
74+
corev1api.ResourceEphemeralStorage: resource.MustParse("5Gi"),
5975
},
6076
Limits: corev1api.ResourceList{
61-
corev1api.ResourceCPU: resource.MustParse("200m"),
77+
corev1api.ResourceCPU: resource.MustParse("200m"),
78+
corev1api.ResourceMemory: resource.MustParse("256Mi"),
6279
},
6380
}},
64-
{"CPU/Mem requests with unbounded limits", args{"100m", "128Mi", "0", "0"}, false, &corev1api.ResourceRequirements{
81+
82+
{"CPU/Mem/EphemeralStorage requests with unbounded limits", args{"100m", "128Mi", "5Gi", "0", "0", "0"}, false, &corev1api.ResourceRequirements{
6583
Requests: corev1api.ResourceList{
66-
corev1api.ResourceCPU: resource.MustParse("100m"),
67-
corev1api.ResourceMemory: resource.MustParse("128Mi"),
84+
corev1api.ResourceCPU: resource.MustParse("100m"),
85+
corev1api.ResourceMemory: resource.MustParse("128Mi"),
86+
corev1api.ResourceEphemeralStorage: resource.MustParse("5Gi"),
6887
},
6988
Limits: corev1api.ResourceList{},
7089
}},
71-
{"invalid quantity", args{"100m", "invalid", "200m", "256Mi"}, true, nil},
72-
{"CPU request greater than limit", args{"300m", "128Mi", "200m", "256Mi"}, true, nil},
73-
{"memory request greater than limit", args{"100m", "512Mi", "200m", "256Mi"}, true, nil},
90+
{"invalid quantity", args{"100m", "invalid", "1Gi", "200m", "256Mi", "valid"}, true, nil},
91+
{"CPU request greater than limit", args{"300m", "128Mi", "5Gi", "200m", "256Mi", "10Gi"}, true, nil},
92+
{"memory request greater than limit", args{"100m", "512Mi", "5Gi", "200m", "256Mi", "10Gi"}, true, nil},
93+
{"ephemeral storage request greater than limit", args{"100m", "128Mi", "10Gi", "200m", "256Mi", "5Gi"}, true, nil},
7494
}
7595
for _, tt := range tests {
7696
t.Run(tt.name, func(t *testing.T) {
77-
got, err := ParseResourceRequirements(tt.args.cpuRequest, tt.args.memRequest, tt.args.cpuLimit, tt.args.memLimit)
97+
got, err := ParseResourceRequirements(tt.args.cpuRequest, tt.args.memRequest, tt.args.ephemeralStorageRequest, tt.args.cpuLimit, tt.args.memLimit, tt.args.ephemeralStorageLimit)
7898
if tt.wantErr {
7999
assert.Error(t, err)
80100
return
@@ -85,12 +105,14 @@ func TestParseResourceRequirements(t *testing.T) {
85105
if tt.expected == nil {
86106
expected = corev1api.ResourceRequirements{
87107
Requests: corev1api.ResourceList{
88-
corev1api.ResourceCPU: resource.MustParse(tt.args.cpuRequest),
89-
corev1api.ResourceMemory: resource.MustParse(tt.args.memRequest),
108+
corev1api.ResourceCPU: resource.MustParse(tt.args.cpuRequest),
109+
corev1api.ResourceMemory: resource.MustParse(tt.args.memRequest),
110+
corev1api.ResourceEphemeralStorage: resource.MustParse(tt.args.ephemeralStorageRequest),
90111
},
91112
Limits: corev1api.ResourceList{
92-
corev1api.ResourceCPU: resource.MustParse(tt.args.cpuLimit),
93-
corev1api.ResourceMemory: resource.MustParse(tt.args.memLimit),
113+
corev1api.ResourceCPU: resource.MustParse(tt.args.cpuLimit),
114+
corev1api.ResourceMemory: resource.MustParse(tt.args.memLimit),
115+
corev1api.ResourceEphemeralStorage: resource.MustParse(tt.args.ephemeralStorageLimit),
94116
},
95117
}
96118
} else {

0 commit comments

Comments
 (0)