Skip to content

Commit 3ac0ac7

Browse files
committed
improve validation for ReplicaSet annotations in the deployment controller
1 parent 2c4a863 commit 3ac0ac7

File tree

2 files changed

+100
-8
lines changed

2 files changed

+100
-8
lines changed

pkg/controller/deployment/util/deployment_util.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -378,24 +378,34 @@ func FindActiveOrLatest(newRS *apps.ReplicaSet, oldRSs []*apps.ReplicaSet) *apps
378378

379379
// GetDesiredReplicasAnnotation returns the number of desired replicas
380380
func GetDesiredReplicasAnnotation(logger klog.Logger, rs *apps.ReplicaSet) (int32, bool) {
381-
return getIntFromAnnotation(logger, rs, DesiredReplicasAnnotation)
381+
return getNonNegativeInt32FromAnnotationVerbose(logger, rs, DesiredReplicasAnnotation)
382382
}
383383

384384
func getMaxReplicasAnnotation(logger klog.Logger, rs *apps.ReplicaSet) (int32, bool) {
385-
return getIntFromAnnotation(logger, rs, MaxReplicasAnnotation)
385+
return getNonNegativeInt32FromAnnotationVerbose(logger, rs, MaxReplicasAnnotation)
386386
}
387387

388-
func getIntFromAnnotation(logger klog.Logger, rs *apps.ReplicaSet, annotationKey string) (int32, bool) {
388+
func getNonNegativeInt32FromAnnotationVerbose(logger klog.Logger, rs *apps.ReplicaSet, annotationKey string) (int32, bool) {
389+
value, ok, err := getNonNegativeInt32FromAnnotation(rs, annotationKey)
390+
if err != nil {
391+
logger.V(2).Info("Could not convert the value with annotation key for the replica set", "annotationValue", rs.Annotations[annotationKey], "annotationKey", annotationKey, "replicaSet", klog.KObj(rs))
392+
}
393+
return value, ok
394+
}
395+
396+
func getNonNegativeInt32FromAnnotation(rs *apps.ReplicaSet, annotationKey string) (int32, bool, error) {
389397
annotationValue, ok := rs.Annotations[annotationKey]
390398
if !ok {
391-
return int32(0), false
399+
return int32(0), false, nil
392400
}
393-
intValue, err := strconv.Atoi(annotationValue)
401+
intValue, err := strconv.ParseUint(annotationValue, 10, 32)
394402
if err != nil {
395-
logger.V(2).Info("Could not convert the value with annotation key for the replica set", "annotationValue", annotationValue, "annotationKey", annotationKey, "replicaSet", klog.KObj(rs))
396-
return int32(0), false
403+
return int32(0), false, err
404+
}
405+
if intValue > math.MaxInt32 {
406+
return int32(0), false, fmt.Errorf("value %d is out of range (higher than %d)", intValue, math.MaxInt32)
397407
}
398-
return int32(intValue), true
408+
return int32(intValue), true, nil
399409
}
400410

401411
// SetReplicasAnnotations sets the desiredReplicas and maxReplicas into the annotations

pkg/controller/deployment/util/deployment_util_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"reflect"
2424
"sort"
2525
"strconv"
26+
"strings"
2627
"testing"
2728
"time"
2829

@@ -1020,6 +1021,87 @@ func TestMaxUnavailable(t *testing.T) {
10201021
}
10211022
}
10221023

1024+
func TestGetNonNegativeInt32FromAnnotation(t *testing.T) {
1025+
tests := []struct {
1026+
name string
1027+
annotations map[string]string
1028+
expectedValue int32
1029+
expectedValid bool
1030+
expectedErr string
1031+
}{
1032+
{
1033+
name: "invalid empty",
1034+
},
1035+
{
1036+
name: "invalid",
1037+
annotations: map[string]string{"test": "invalid", "foo": "2"},
1038+
expectedErr: "invalid syntax",
1039+
},
1040+
{
1041+
name: "invalid negative ",
1042+
annotations: map[string]string{"test": "-1", "foo": "2"},
1043+
expectedErr: "invalid syntax",
1044+
},
1045+
{
1046+
name: "valid zero",
1047+
annotations: map[string]string{"test": "0", "foo": "2"},
1048+
expectedValue: 0,
1049+
expectedValid: true,
1050+
},
1051+
{
1052+
name: "valid",
1053+
annotations: map[string]string{"test": "13", "foo": "2"},
1054+
expectedValue: 13,
1055+
expectedValid: true,
1056+
},
1057+
{
1058+
name: "valid max",
1059+
annotations: map[string]string{"test": fmt.Sprintf("%d", math.MaxInt32), "foo": "2"},
1060+
expectedValue: math.MaxInt32,
1061+
expectedValid: true,
1062+
},
1063+
{
1064+
name: "invalid max out of range",
1065+
annotations: map[string]string{"test": fmt.Sprintf("%d", uint32(math.MaxInt32)+1), "foo": "2"},
1066+
expectedErr: "out of range",
1067+
},
1068+
{
1069+
name: "invalid max out of range 2",
1070+
annotations: map[string]string{"test": fmt.Sprintf("%d", uint64(math.MaxUint32)+1), "foo": "2"},
1071+
expectedErr: "out of range",
1072+
},
1073+
}
1074+
1075+
for _, test := range tests {
1076+
t.Run(test.name, func(t *testing.T) {
1077+
tDeployment := generateDeployment("nginx")
1078+
tRS := generateRS(tDeployment)
1079+
tRS.Annotations = test.annotations
1080+
value, valid, err := getNonNegativeInt32FromAnnotation(&tRS, "test")
1081+
if test.expectedValue != value {
1082+
t.Fatalf("expected value:%v, got:%v", test.expectedValue, value)
1083+
}
1084+
if test.expectedValid != valid {
1085+
t.Fatalf("expected valid:%v, got:%v", test.expectedValid, valid)
1086+
}
1087+
if err != nil && !strings.Contains(err.Error(), test.expectedErr) {
1088+
t.Fatalf("unexpected error, expected: %s, got %v", test.expectedErr, err)
1089+
}
1090+
if err == nil && len(test.expectedErr) != 0 {
1091+
t.Fatalf("didn't return error expected %s", test.expectedErr)
1092+
}
1093+
logger, _ := ktesting.NewTestContext(t)
1094+
value, valid = getNonNegativeInt32FromAnnotationVerbose(logger, &tRS, "test")
1095+
if test.expectedValue != value {
1096+
t.Fatalf("expected value:%v, got:%v", test.expectedValue, value)
1097+
}
1098+
if test.expectedValid != valid {
1099+
t.Fatalf("expected valid:%v, got:%v", test.expectedValid, valid)
1100+
}
1101+
})
1102+
}
1103+
}
1104+
10231105
// Set of simple tests for annotation related util functions
10241106
func TestAnnotationUtils(t *testing.T) {
10251107

0 commit comments

Comments
 (0)