Skip to content

Commit f7c1b3f

Browse files
authored
feat: support vpc-<uuid> in target group binding vpc-id validation (#3845)
* support vpc-<uuid> in target groups binding validation * add VpcID validation associated tests
1 parent 7901857 commit f7c1b3f

File tree

2 files changed

+204
-10
lines changed

2 files changed

+204
-10
lines changed

webhooks/elbv2/targetgroupbinding_validator.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ import (
1919
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2020
)
2121

22-
const apiPathValidateELBv2TargetGroupBinding = "/validate-elbv2-k8s-aws-v1beta1-targetgroupbinding"
22+
const (
23+
apiPathValidateELBv2TargetGroupBinding = "/validate-elbv2-k8s-aws-v1beta1-targetgroupbinding"
24+
vpcIDValidationErr = "ValidationError: vpcID %v failed to satisfy constraint: VPC Id must begin with 'vpc-' followed by 8, 17 or 32 lowercase letters (a-f) or numbers."
25+
vpcIDNotMatchErr = "invalid VpcID %v doesnt match VpcID from TargetGroup %v"
26+
)
2327

24-
var vpcIDPatternRegex = regexp.MustCompile("^(?:vpc-[0-9a-f]{8}|vpc-[0-9a-f]{17})$")
28+
var vpcIDPatternRegex = regexp.MustCompile("^(?:vpc-[0-9a-f]{8}|vpc-[0-9a-f]{17}|vpc-[0-9a-f]{32})$")
2529

2630
// NewTargetGroupBindingValidator returns a validator for TargetGroupBinding CRD.
2731
func NewTargetGroupBindingValidator(k8sClient client.Client, elbv2Client services.ELBV2, vpcID string, logger logr.Logger) *targetGroupBindingValidator {
@@ -169,14 +173,14 @@ func (v *targetGroupBindingValidator) checkTargetGroupVpcID(ctx context.Context,
169173
return nil
170174
}
171175
if !vpcIDPatternRegex.MatchString(tgb.Spec.VpcID) {
172-
return errors.Errorf("ValidationError: vpcID %v failed to satisfy constraint: VPC Id must begin with 'vpc-' followed by 8 or 17 lowercase letters (a-f) or numbers.", tgb.Spec.VpcID)
176+
return errors.Errorf(vpcIDValidationErr, tgb.Spec.VpcID)
173177
}
174178
vpcID, err := v.getVpcIDFromAWS(ctx, tgb.Spec.TargetGroupARN)
175179
if err != nil {
176180
return errors.Wrap(err, "unable to get target group VpcID")
177181
}
178182
if vpcID != tgb.Spec.VpcID {
179-
return errors.Errorf("invalid VpcID %v doesnt match VpcID from TargetGroup %v", tgb.Spec.VpcID, tgb.Spec.TargetGroupARN)
183+
return errors.Errorf(vpcIDNotMatchErr, tgb.Spec.VpcID, tgb.Spec.TargetGroupARN)
180184
}
181185
return nil
182186
}

webhooks/elbv2/targetgroupbinding_validator_test.go

Lines changed: 196 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ package elbv2
22

33
import (
44
"context"
5+
"crypto/rand"
6+
"fmt"
7+
"github.com/google/uuid"
8+
"math/big"
9+
"strings"
510
"testing"
611

712
awssdk "github.com/aws/aws-sdk-go/aws"
@@ -1087,12 +1092,125 @@ func Test_targetGroupBindingValidator_checkTargetGroupVpcID(t *testing.T) {
10871092
type fields struct {
10881093
describeTargetGroupsAsListCalls []describeTargetGroupsAsListCall
10891094
}
1095+
var (
1096+
vpcID8Chars = fmt.Sprintf("vpc-%s", generateRandomString(8))
1097+
vpcID17Chars = fmt.Sprintf("vpc-%s", generateRandomString(17))
1098+
vpcIDUUID = fmt.Sprintf("vpc-%s", generateVpcUUID())
1099+
vpcID8CharsWrongPrefix = fmt.Sprintf("vpcid-%s", generateRandomString(8))
1100+
vpcID8CharsIllegalChars = fmt.Sprintf("vpc-%s", generateRandomString(6, '@', '!'))
1101+
vpcID17CharsWrongPrefix = fmt.Sprintf("vpcid-%s", generateRandomString(17))
1102+
vpcID17CharsIllegalChars = fmt.Sprintf("vpc-%s", generateRandomString(15, 'G', 'L'))
1103+
vpcIDUUIDWrongPrefix = fmt.Sprintf("vpcid-%s", generateRandomString(32))
1104+
vpcIDUUIDIllegalChars = fmt.Sprintf("vpc-%s", generateRandomString(30, 'z', 'Y'))
1105+
)
1106+
10901107
tests := []struct {
10911108
name string
10921109
fields fields
10931110
args args
10941111
wantErr error
10951112
}{
1113+
{
1114+
name: "[ok] Valid VpcID with 8 Characters",
1115+
fields: fields{
1116+
describeTargetGroupsAsListCalls: []describeTargetGroupsAsListCall{
1117+
{
1118+
req: &elbv2sdk.DescribeTargetGroupsInput{
1119+
TargetGroupArns: awssdk.StringSlice([]string{"tg-2"}),
1120+
},
1121+
resp: []*elbv2sdk.TargetGroup{
1122+
{
1123+
VpcId: awssdk.String(vpcID8Chars),
1124+
},
1125+
},
1126+
},
1127+
},
1128+
},
1129+
args: args{
1130+
obj: &elbv2api.TargetGroupBinding{
1131+
Spec: elbv2api.TargetGroupBindingSpec{
1132+
TargetGroupARN: "tg-2",
1133+
VpcID: vpcID8Chars,
1134+
},
1135+
},
1136+
},
1137+
},
1138+
{
1139+
name: "[ok] Valid VpcID with 17 Characters",
1140+
fields: fields{
1141+
describeTargetGroupsAsListCalls: []describeTargetGroupsAsListCall{
1142+
{
1143+
req: &elbv2sdk.DescribeTargetGroupsInput{
1144+
TargetGroupArns: awssdk.StringSlice([]string{"tg-2"}),
1145+
},
1146+
resp: []*elbv2sdk.TargetGroup{
1147+
{
1148+
VpcId: awssdk.String(vpcID17Chars),
1149+
},
1150+
},
1151+
},
1152+
},
1153+
},
1154+
args: args{
1155+
obj: &elbv2api.TargetGroupBinding{
1156+
Spec: elbv2api.TargetGroupBindingSpec{
1157+
TargetGroupARN: "tg-2",
1158+
VpcID: vpcID17Chars,
1159+
},
1160+
},
1161+
},
1162+
},
1163+
{
1164+
name: "[ok] Valid VpcID with UUID",
1165+
fields: fields{
1166+
describeTargetGroupsAsListCalls: []describeTargetGroupsAsListCall{
1167+
{
1168+
req: &elbv2sdk.DescribeTargetGroupsInput{
1169+
TargetGroupArns: awssdk.StringSlice([]string{"tg-2"}),
1170+
},
1171+
resp: []*elbv2sdk.TargetGroup{
1172+
{
1173+
VpcId: awssdk.String(vpcIDUUID),
1174+
},
1175+
},
1176+
},
1177+
},
1178+
},
1179+
args: args{
1180+
obj: &elbv2api.TargetGroupBinding{
1181+
Spec: elbv2api.TargetGroupBindingSpec{
1182+
TargetGroupARN: "tg-2",
1183+
VpcID: vpcIDUUID,
1184+
},
1185+
},
1186+
},
1187+
},
1188+
{
1189+
name: "[err] Provided VpcID doesn't match VpcID from TargetGroup",
1190+
fields: fields{
1191+
describeTargetGroupsAsListCalls: []describeTargetGroupsAsListCall{
1192+
{
1193+
req: &elbv2sdk.DescribeTargetGroupsInput{
1194+
TargetGroupArns: awssdk.StringSlice([]string{"tg-2"}),
1195+
},
1196+
resp: []*elbv2sdk.TargetGroup{
1197+
{
1198+
VpcId: awssdk.String(fmt.Sprintf("vpc-%s", generateRandomString(17))),
1199+
},
1200+
},
1201+
},
1202+
},
1203+
},
1204+
args: args{
1205+
obj: &elbv2api.TargetGroupBinding{
1206+
Spec: elbv2api.TargetGroupBindingSpec{
1207+
TargetGroupARN: "tg-2",
1208+
VpcID: vpcID17Chars,
1209+
},
1210+
},
1211+
},
1212+
wantErr: fmt.Errorf(vpcIDNotMatchErr, vpcID17Chars, "tg-2"),
1213+
},
10961214
{
10971215
name: "[ok] VpcID is not set",
10981216
args: args{
@@ -1125,28 +1243,76 @@ func Test_targetGroupBindingValidator_checkTargetGroupVpcID(t *testing.T) {
11251243
wantErr: errors.New("unable to get target group VpcID: vpcid not found"),
11261244
},
11271245
{
1128-
name: "[err] vpcID is not valid",
1246+
name: "[err] vpcID 8 chars is not valid - invalid prefix",
1247+
args: args{
1248+
obj: &elbv2api.TargetGroupBinding{
1249+
Spec: elbv2api.TargetGroupBindingSpec{
1250+
TargetGroupARN: "tg-2",
1251+
VpcID: vpcID8CharsWrongPrefix,
1252+
},
1253+
},
1254+
},
1255+
wantErr: errors.New(fmt.Sprintf(vpcIDValidationErr, vpcID8CharsWrongPrefix)),
1256+
},
1257+
{
1258+
name: "[err] vpcID 8 chars is not valid - illegal chars",
1259+
args: args{
1260+
obj: &elbv2api.TargetGroupBinding{
1261+
Spec: elbv2api.TargetGroupBindingSpec{
1262+
TargetGroupARN: "tg-2",
1263+
VpcID: vpcID8CharsIllegalChars,
1264+
},
1265+
},
1266+
},
1267+
wantErr: errors.New(fmt.Sprintf(vpcIDValidationErr, vpcID8CharsIllegalChars)),
1268+
},
1269+
{
1270+
name: "[err] vpcID 17 chars is not valid - invalid prefix",
11291271
args: args{
11301272
obj: &elbv2api.TargetGroupBinding{
11311273
Spec: elbv2api.TargetGroupBindingSpec{
11321274
TargetGroupARN: "tg-2",
1133-
VpcID: "vpcid-123",
1275+
VpcID: vpcID17CharsWrongPrefix,
11341276
},
11351277
},
11361278
},
1137-
wantErr: errors.New("ValidationError: vpcID vpcid-123 failed to satisfy constraint: VPC Id must begin with 'vpc-' followed by 8 or 17 lowercase letters (a-f) or numbers."),
1279+
wantErr: errors.New(fmt.Sprintf(vpcIDValidationErr, vpcID17CharsWrongPrefix)),
11381280
},
11391281
{
1140-
name: "[err] vpcID is not valid - non alphanumeric value",
1282+
name: "[err] vpcID 17 chars is not valid - illegal chars",
11411283
args: args{
11421284
obj: &elbv2api.TargetGroupBinding{
11431285
Spec: elbv2api.TargetGroupBindingSpec{
11441286
TargetGroupARN: "tg-2",
1145-
VpcID: "vpcid-@34!dv",
1287+
VpcID: vpcID17CharsIllegalChars,
11461288
},
11471289
},
11481290
},
1149-
wantErr: errors.New("ValidationError: vpcID vpcid-@34!dv failed to satisfy constraint: VPC Id must begin with 'vpc-' followed by 8 or 17 lowercase letters (a-f) or numbers."),
1291+
wantErr: errors.New(fmt.Sprintf(vpcIDValidationErr, vpcID17CharsIllegalChars)),
1292+
},
1293+
{
1294+
name: "[err] vpcID UUID is not valid - invalid prefix",
1295+
args: args{
1296+
obj: &elbv2api.TargetGroupBinding{
1297+
Spec: elbv2api.TargetGroupBindingSpec{
1298+
TargetGroupARN: "tg-2",
1299+
VpcID: vpcIDUUIDWrongPrefix,
1300+
},
1301+
},
1302+
},
1303+
wantErr: errors.New(fmt.Sprintf(vpcIDValidationErr, vpcIDUUIDWrongPrefix)),
1304+
},
1305+
{
1306+
name: "[err] vpcID UUID is not valid - illegal chars",
1307+
args: args{
1308+
obj: &elbv2api.TargetGroupBinding{
1309+
Spec: elbv2api.TargetGroupBindingSpec{
1310+
TargetGroupARN: "tg-2",
1311+
VpcID: vpcIDUUIDIllegalChars,
1312+
},
1313+
},
1314+
},
1315+
wantErr: errors.New(fmt.Sprintf(vpcIDValidationErr, vpcIDUUIDIllegalChars)),
11501316
},
11511317
}
11521318
for _, tt := range tests {
@@ -1175,3 +1341,27 @@ func Test_targetGroupBindingValidator_checkTargetGroupVpcID(t *testing.T) {
11751341
})
11761342
}
11771343
}
1344+
1345+
func generateRandomString(n int, addChars ...rune) string {
1346+
const letters = "0123456789abcdef"
1347+
1348+
ret := make([]byte, n)
1349+
for i := 0; i < n; i++ {
1350+
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
1351+
if err != nil {
1352+
return ""
1353+
}
1354+
ret[i] = letters[num.Int64()]
1355+
}
1356+
1357+
w := string(ret)
1358+
for _, c := range addChars {
1359+
w += string(c)
1360+
}
1361+
return w
1362+
}
1363+
1364+
func generateVpcUUID() string {
1365+
u := strings.ToLower(uuid.New().String())
1366+
return strings.Replace(u, "-", "", -1)
1367+
}

0 commit comments

Comments
 (0)