velero install: support velero/restic pod cpu/mem requests with unbounded limits (#1745)

* support velero/restic pod cpu/mem requests with unbounded limits

explicitly treat "0" value as unbounded

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* update flag documentation

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* changelog

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>

* update restic configmap docs

Signed-off-by: Adnan Abdulhussein <aadnan@vmware.com>
pull/1753/head
Adnan Abdulhussein 2019-08-09 11:54:04 -07:00 committed by Steve Kriss
parent ad026107c9
commit 7b7b96de74
5 changed files with 86 additions and 38 deletions

View File

@ -0,0 +1 @@
support setting CPU/memory requests with unbounded limits using velero install

View File

@ -72,14 +72,14 @@ func (o *InstallOptions) BindFlags(flags *pflag.FlagSet) {
flags.StringVar(&o.Image, "image", o.Image, "image to use for the Velero and restic server pods. Optional.")
flags.StringVar(&o.Prefix, "prefix", o.Prefix, "prefix under which all Velero data should be stored within the bucket. Optional.")
flags.Var(&o.PodAnnotations, "pod-annotations", "annotations to add to the Velero and restic pods. Optional. Format is key1=value1,key2=value2")
flags.StringVar(&o.VeleroPodCPURequest, "velero-pod-cpu-request", o.VeleroPodCPURequest, "CPU request for Velero pod. Optional.")
flags.StringVar(&o.VeleroPodMemRequest, "velero-pod-mem-request", o.VeleroPodMemRequest, "memory request for Velero pod. Optional.")
flags.StringVar(&o.VeleroPodCPULimit, "velero-pod-cpu-limit", o.VeleroPodCPULimit, "CPU limit for Velero pod. Optional.")
flags.StringVar(&o.VeleroPodMemLimit, "velero-pod-mem-limit", o.VeleroPodMemLimit, "memory limit for Velero pod. Optional.")
flags.StringVar(&o.ResticPodCPURequest, "restic-pod-cpu-request", o.ResticPodCPURequest, "CPU request for restic pods. Optional.")
flags.StringVar(&o.ResticPodMemRequest, "restic-pod-mem-request", o.ResticPodMemRequest, "memory request for restic pods. Optional.")
flags.StringVar(&o.ResticPodCPULimit, "restic-pod-cpu-limit", o.ResticPodCPULimit, "CPU limit for restic pods. Optional.")
flags.StringVar(&o.ResticPodMemLimit, "restic-pod-mem-limit", o.ResticPodMemLimit, "memory limit for restic pods. Optional.")
flags.StringVar(&o.VeleroPodCPURequest, "velero-pod-cpu-request", o.VeleroPodCPURequest, `CPU request for Velero pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.VeleroPodMemRequest, "velero-pod-mem-request", o.VeleroPodMemRequest, `memory request for Velero pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.VeleroPodCPULimit, "velero-pod-cpu-limit", o.VeleroPodCPULimit, `CPU limit for Velero pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.VeleroPodMemLimit, "velero-pod-mem-limit", o.VeleroPodMemLimit, `memory limit for Velero pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.ResticPodCPURequest, "restic-pod-cpu-request", o.ResticPodCPURequest, `CPU request for restic pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.ResticPodMemRequest, "restic-pod-mem-request", o.ResticPodMemRequest, `memory request for restic pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.ResticPodCPULimit, "restic-pod-cpu-limit", o.ResticPodCPULimit, `CPU limit for restic pod. A value of "0" is treated as unbounded. Optional.`)
flags.StringVar(&o.ResticPodMemLimit, "restic-pod-mem-limit", o.ResticPodMemLimit, `memory limit for restic pod. A value of "0" is treated as unbounded. Optional.`)
flags.Var(&o.BackupStorageConfig, "backup-location-config", "configuration to use for the backup storage location. Format is key1=value1,key2=value2")
flags.Var(&o.VolumeSnapshotConfig, "snapshot-location-config", "configuration to use for the volume snapshot location. Format is key1=value1,key2=value2")
flags.BoolVar(&o.UseVolumeSnapshots, "use-volume-snapshots", o.UseVolumeSnapshots, "whether or not to create snapshot location automatically. Set to false if you do not plan to create volume snapshots via a storage provider.")

View File

@ -26,7 +26,10 @@ import (
// values and returns a ResourceRequirements struct to be used in a Container.
// An error is returned if we cannot parse the request/limit.
func ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit string) (corev1.ResourceRequirements, error) {
var resources corev1.ResourceRequirements
resources := corev1.ResourceRequirements{
Requests: corev1.ResourceList{},
Limits: corev1.ResourceList{},
}
parsedCPURequest, err := resource.ParseQuantity(cpuRequest)
if err != nil {
@ -48,21 +51,29 @@ func ParseResourceRequirements(cpuRequest, memRequest, cpuLimit, memLimit string
return resources, errors.Wrapf(err, `couldn't parse memory limit "%s"`, memLimit)
}
if parsedCPURequest.Cmp(parsedCPULimit) > 0 {
// A quantity of 0 is treated as unbounded
unbounded := resource.MustParse("0")
if parsedCPULimit != unbounded && parsedCPURequest.Cmp(parsedCPULimit) > 0 {
return resources, errors.WithStack(errors.Errorf(`CPU request "%s" must be less than or equal to CPU limit "%s"`, cpuRequest, cpuLimit))
}
if parsedMemRequest.Cmp(parsedMemLimit) > 0 {
if parsedMemLimit != unbounded && parsedMemRequest.Cmp(parsedMemLimit) > 0 {
return resources, errors.WithStack(errors.Errorf(`Memory request "%s" must be less than or equal to Memory limit "%s"`, memRequest, memLimit))
}
resources.Requests = corev1.ResourceList{
corev1.ResourceCPU: parsedCPURequest,
corev1.ResourceMemory: parsedMemRequest,
// Only set resources if they are not unbounded
if parsedCPURequest != unbounded {
resources.Requests[corev1.ResourceCPU] = parsedCPURequest
}
resources.Limits = corev1.ResourceList{
corev1.ResourceCPU: parsedCPULimit,
corev1.ResourceMemory: parsedMemLimit,
if parsedMemRequest != unbounded {
resources.Requests[corev1.ResourceMemory] = parsedMemRequest
}
if parsedCPULimit != unbounded {
resources.Limits[corev1.ResourceCPU] = parsedCPULimit
}
if parsedMemLimit != unbounded {
resources.Limits[corev1.ResourceMemory] = parsedMemLimit
}
return resources, nil

View File

@ -32,15 +32,44 @@ func TestParseResourceRequirements(t *testing.T) {
memLimit string
}
tests := []struct {
name string
args args
wantErr bool
name string
args args
wantErr bool
expected *corev1.ResourceRequirements
}{
{"unbounded quantities", args{"0", "0", "0", "0"}, false},
{"valid quantities", args{"100m", "128Mi", "200m", "256Mi"}, false},
{"invalid quantity", args{"100m", "invalid", "200m", "256Mi"}, true},
{"CPU request greater than limit", args{"300m", "128Mi", "200m", "256Mi"}, true},
{"memory request greater than limit", args{"100m", "512Mi", "200m", "256Mi"}, true},
{"unbounded quantities", args{"0", "0", "0", "0"}, false, &corev1.ResourceRequirements{
Requests: corev1.ResourceList{},
Limits: corev1.ResourceList{},
}},
{"valid quantities", args{"100m", "128Mi", "200m", "256Mi"}, false, nil},
{"CPU request with unbounded limit", args{"100m", "128Mi", "0", "256Mi"}, false, &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("256Mi"),
},
}},
{"Mem request with unbounded limit", args{"100m", "128Mi", "200m", "0"}, false, &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
},
}},
{"CPU/Mem requests with unbounded limits", args{"100m", "128Mi", "0", "0"}, false, &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
},
Limits: corev1.ResourceList{},
}},
{"invalid quantity", args{"100m", "invalid", "200m", "256Mi"}, true, nil},
{"CPU request greater than limit", args{"300m", "128Mi", "200m", "256Mi"}, true, nil},
{"memory request greater than limit", args{"100m", "512Mi", "200m", "256Mi"}, true, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -49,17 +78,24 @@ func TestParseResourceRequirements(t *testing.T) {
assert.Error(t, err)
return
}
assert.NoError(t, err)
expected := corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse(tt.args.cpuRequest),
corev1.ResourceMemory: resource.MustParse(tt.args.memRequest),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse(tt.args.cpuLimit),
corev1.ResourceMemory: resource.MustParse(tt.args.memLimit),
},
var expected corev1.ResourceRequirements
if tt.expected == nil {
expected = corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse(tt.args.cpuRequest),
corev1.ResourceMemory: resource.MustParse(tt.args.memRequest),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse(tt.args.cpuLimit),
corev1.ResourceMemory: resource.MustParse(tt.args.memLimit),
},
}
} else {
expected = *tt.expected
}
assert.Equal(t, expected, got)
})
}

View File

@ -235,19 +235,19 @@ data:
image: myregistry.io/my-custom-helper-image[:OPTIONAL_TAG]
# "cpuRequest" sets the request.cpu value on the restic init containers during restore.
# If not set, it will default to "100m".
# If not set, it will default to "100m". A value of "0" is treated as unbounded.
cpuRequest: 200m
# "memRequest" sets the request.memory value on the restic init containers during restore.
# If not set, it will default to "128Mi".
# If not set, it will default to "128Mi". A value of "0" is treated as unbounded.
memRequest: 128Mi
# "cpuLimit" sets the request.cpu value on the restic init containers during restore.
# If not set, it will default to "100m".
# If not set, it will default to "100m". A value of "0" is treated as unbounded.
cpuLimit: 200m
# "memLimit" sets the request.memory value on the restic init containers during restore.
# If not set, it will default to "128Mi".
# If not set, it will default to "128Mi". A value of "0" is treated as unbounded.
memLimit: 128Mi