Skip to content

Commit 65422c8

Browse files
Merge pull request #29777 from openshift-cherrypick-robot/cherry-pick-29727-to-release-4.19
[release-4.19] OCPBUGS-56000, CORENET-5875, CORENET-5876: Add EgressIP and L2 UDN route advertisements tests
2 parents 393f905 + 78cd6c7 commit 65422c8

File tree

5 files changed

+715
-128
lines changed

5 files changed

+715
-128
lines changed

test/extended/networking/egressip.go

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -742,35 +742,40 @@ func applyEgressIPObject(oc *exutil.CLI, cloudNetworkClientset cloudnetwork.Inte
742742
_, err := runOcWithRetry(oc.AsAdmin(), "apply", "-f", egressIPYamlPath)
743743
o.Expect(err).NotTo(o.HaveOccurred())
744744

745-
framework.Logf(fmt.Sprintf("Waiting for CloudPrivateIPConfig creation for a maximum of %d seconds", timeout))
746-
var exists bool
747-
var isAssigned bool
748-
o.Eventually(func() bool {
749-
for eip := range egressIPSet {
750-
exists, isAssigned, err = cloudPrivateIpConfigExists(oc, cloudNetworkClientset, eip)
751-
o.Expect(err).NotTo(o.HaveOccurred())
752-
if !exists {
753-
framework.Logf("CloudPrivateIPConfig for %s not found.", eip)
754-
return false
755-
}
756-
if !isAssigned {
757-
framework.Logf("CloudPrivateIPConfig for %s not assigned.", eip)
758-
return false
745+
if cloudNetworkClientset != nil {
746+
framework.Logf(fmt.Sprintf("Waiting for CloudPrivateIPConfig creation for a maximum of %d seconds", timeout))
747+
var exists bool
748+
var isAssigned bool
749+
o.Eventually(func() bool {
750+
for eip := range egressIPSet {
751+
exists, isAssigned, err = cloudPrivateIpConfigExists(oc, cloudNetworkClientset, eip)
752+
o.Expect(err).NotTo(o.HaveOccurred())
753+
if !exists {
754+
framework.Logf("CloudPrivateIPConfig for %s not found.", eip)
755+
return false
756+
}
757+
if !isAssigned {
758+
framework.Logf("CloudPrivateIPConfig for %s not assigned.", eip)
759+
return false
760+
}
759761
}
760-
}
761-
framework.Logf("CloudPrivateIPConfigs for %v found.", egressIPSet)
762-
return true
763-
}, time.Duration(timeout)*time.Second, 5*time.Second).Should(o.BeTrue())
762+
framework.Logf("CloudPrivateIPConfigs for %v found.", egressIPSet)
763+
return true
764+
}, time.Duration(timeout)*time.Second, 5*time.Second).Should(o.BeTrue())
765+
}
764766

765767
framework.Logf(fmt.Sprintf("Waiting for EgressIP addresses inside status of EgressIP CR %s for a maximum of %d seconds", egressIPObjectName, timeout))
766768
var hasIP bool
769+
var nodeName string
767770
o.Eventually(func() bool {
768771
for eip := range egressIPSet {
769-
hasIP, err = egressIPStatusHasIP(oc, egressIPObjectName, eip)
772+
hasIP, nodeName, err = egressIPStatusHasIP(oc, egressIPObjectName, eip)
770773
o.Expect(err).NotTo(o.HaveOccurred())
771774
if !hasIP {
772775
framework.Logf("EgressIP object %s does not have IP %s in its status field.", egressIPObjectName, eip)
773776
return false
777+
} else {
778+
egressIPSet[eip] = nodeName
774779
}
775780
}
776781
framework.Logf("Egress IP object %s does have all IPs for %v.", egressIPObjectName, egressIPSet)

test/extended/networking/egressip_helpers.go

Lines changed: 57 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ func createPacketSnifferDaemonSet(oc *exutil.CLI, namespace string, scheduleOnHo
404404
}
405405

406406
var ds *appsv1.DaemonSet
407-
retries := 12
407+
retries := 48
408408
pollInterval := 5
409409
for i := 0; i < retries; i++ {
410410
// Get the DS
@@ -415,7 +415,9 @@ func createPacketSnifferDaemonSet(oc *exutil.CLI, namespace string, scheduleOnHo
415415

416416
// Check if NumberReady == DesiredNumberScheduled.
417417
// In that case, simply return as all went well.
418-
if ds.Status.NumberReady == ds.Status.DesiredNumberScheduled {
418+
if ds.Status.NumberReady == ds.Status.DesiredNumberScheduled &&
419+
ds.Status.CurrentNumberScheduled == ds.Status.DesiredNumberScheduled &&
420+
ds.Status.DesiredNumberScheduled > 0 {
419421
return ds, nil
420422
}
421423
// If no port conflict error was found, simply sleep for pollInterval and then
@@ -426,15 +428,14 @@ func createPacketSnifferDaemonSet(oc *exutil.CLI, namespace string, scheduleOnHo
426428
// The DaemonSet is not ready, but this is not because of a port conflict.
427429
// This shouldn't happen and other parts of the code will likely report this error
428430
// as a CI failure.
429-
return ds, fmt.Errorf("Daemonset still not ready after %d tries", retries)
431+
return ds, fmt.Errorf("Daemonset still not ready after %d tries: ready=%d, scheduled=%d, desired=%d", retries, ds.Status.NumberReady, ds.Status.CurrentNumberScheduled, ds.Status.DesiredNumberScheduled)
430432
}
431433

432434
const (
433435
// The tcpCaptureScript runs tcpdump and extracts all GET request strings from the packets.
434436
// The resulting lines will be something like:
435-
// 10.128.2.15.36749 /f8f721fa-53c9-444f-bc96-69c7388fcb5a
436-
tcpCaptureScript = `#!/bin/bash
437-
tcpdump -nn -i %s -l -s 0 -A 'tcp and port %d' | awk '/IP / || /IP6 / {ip=$3} /GET \// {print ip, $2}'
437+
// Parsed 05:38:34.307832 10.128.2.15.36749 /f8f721fa-53c9-444f-bc96-69c7388fcb5a
438+
tcpCaptureScript = `tcpdump -nn -i %s -l -s 0 -A 'tcp and port %d' | awk 'match($0,/IP6?[[:space:]]+([0-9a-fA-F:\.]+[0-9a-fA-F])/,arr) {ts=$1; ip=arr[1]} $0 !~ /HTTP.*GET/ && match($0,/GET[[:space:]]+([^[:space:]]+)/,arr) {print "Parsed", ts, ip, arr[1]} // {print $0}'
438439
`
439440

440441
// The udpCaptureScript runs tcpdump with option -xx and then decodes the hexadecimal information.
@@ -443,8 +444,8 @@ tcpdump -nn -i %s -l -s 0 -A 'tcp and port %d' | awk '/IP / || /IP6 / {ip=$3} /G
443444
// that's captured).
444445
// tshark would definitely be the better tool here, but that would introduce another dependency. Hence,
445446
// decode the hexadecimal information and look for payload that is marked with 'START(.*)EOF$' and extract
446-
// the '(.*)' part. The resulting lines will be `sourceIP + " " + z.group(1)`, hence something like:
447-
// 10.128.2.15.36749 f8f721fa-53c9-444f-bc96-69c7388fcb5a
447+
// the '(.*)' part. The resulting lines will be `"Parsed " + timestamp + " " + sourceIP + " " + z.group(1) + "_" + z.group(2)`, hence something like:
448+
// Parsed 05:38:34.307832 10.128.2.15.36749 f8f721fa-53c9-444f-bc96-69c7388fcb5a_1
448449
udpCaptureScript = `#!/bin/bash
449450
450451
cat <<'EOF' > capture-python.py
@@ -466,6 +467,7 @@ udpPayloadOffset = 0
466467
# globals
467468
fullHex = []
468469
sourceIP = ""
470+
timeStamp = ""
469471
470472
def decodePayload(hexArray):
471473
payloadStr = ""
@@ -482,9 +484,9 @@ def printLine():
482484
global fullHex
483485
if sourceIP != "" and fullHex != []:
484486
decodedPayload = decodePayload(fullHex)
485-
z = re.search(r'START(.*)EOF$', decodedPayload)
487+
z = re.search(r'START(.*)EOF_(\d+)', decodedPayload)
486488
if z:
487-
print(sourceIP + " " + z.group(1))
489+
print("Parsed " + timeStamp + " " + sourceIP + " " + z.group(1) + "_" + z.group(2))
488490
fullHex = []
489491
sourceIP = ""
490492
@@ -497,6 +499,7 @@ for line in sys.stdin:
497499
printLine()
498500
elif not re.match(r'^$', line):
499501
printLine()
502+
timeStamp = line.split()[0]
500503
sourceIP = line.split()[sourceIPOffset]
501504
502505
printLine()
@@ -545,16 +548,6 @@ func createHostNetworkedPacketSnifferDaemonSet(clientset kubernetes.Interface, n
545548
},
546549
},
547550
}
548-
readinessProbe := &v1.Probe{
549-
ProbeHandler: v1.ProbeHandler{
550-
Exec: &v1.ExecAction{
551-
Command: []string{
552-
"echo",
553-
"ready",
554-
},
555-
},
556-
},
557-
}
558551
runAsUser := int64(0)
559552
securityContext := &v1.SecurityContext{
560553
RunAsUser: &runAsUser,
@@ -582,14 +575,19 @@ func createHostNetworkedPacketSnifferDaemonSet(clientset kubernetes.Interface, n
582575
Labels: podLabels,
583576
},
584577
Spec: corev1.PodSpec{
578+
Tolerations: []v1.Toleration{
579+
{
580+
Key: "node-role.kubernetes.io/master",
581+
Effect: corev1.TaintEffectNoSchedule,
582+
},
583+
},
585584
Affinity: &nodeAffinity,
586585
HostNetwork: true,
587586
Containers: []v1.Container{
588587
{
589588
Name: "tcpdump",
590589
Image: networkPacketSnifferImage,
591590
Command: podCommand,
592-
ReadinessProbe: readinessProbe,
593591
SecurityContext: securityContext,
594592
TTY: true, // needed for immediate log propagation
595593
Stdin: true, // needed for immediate log propagation
@@ -651,31 +649,33 @@ func scanPacketSnifferDaemonSetPodLogs(oc *exutil.CLI, ds *appsv1.DaemonSet, tar
651649
scanner := bufio.NewScanner(buf)
652650
for scanner.Scan() {
653651
logLine := scanner.Text()
654-
if strings.Contains(logLine, searchString) {
655-
// Currently, it is not necessary to discriminate by protocol.
656-
// a log line should look like this for http:
657-
// 10.0.144.5.33226 /bed729aa-4e83-482d-a433-db798e569147
658-
// a log line should look like this for udp:
659-
// 10.0.144.5.33226 bed729aa-4e83-482d-a433-db798e569147
660-
// Should it ever be necessary, the targetProtocol to this method (which is currently
661-
// not used) serves this purpose.
662-
framework.Logf("Found hit in log line: %s", logLine)
663-
logLineExploded := strings.Fields(logLine)
664-
if len(logLineExploded) != 2 {
665-
return nil, fmt.Errorf("Unexpected logline content: %s", logLine)
666-
}
667-
ipAddressPortExploded := strings.Split(logLineExploded[0], ".")
668-
if len(ipAddressPortExploded) == 2 {
669-
// ipv6
670-
ip = ipAddressPortExploded[0]
671-
} else if len(ipAddressPortExploded) == 5 {
672-
// ipv4
673-
ip = strings.Join(ipAddressPortExploded[:len(ipAddressPortExploded)-1], ".")
674-
} else {
675-
return nil, fmt.Errorf("Unexpected logline content, invalid IP/Port: %s", logLine)
676-
}
677-
matchedIPs[ip]++
652+
653+
if !strings.HasPrefix(logLine, "Parsed") || !strings.Contains(logLine, searchString) {
654+
continue
655+
}
656+
// Currently, it is not necessary to discriminate by protocol.
657+
// a log line should look like this for http:
658+
// 10.0.144.5.33226 /bed729aa-4e83-482d-a433-db798e569147
659+
// a log line should look like this for udp:
660+
// 10.0.144.5.33226 bed729aa-4e83-482d-a433-db798e569147
661+
// Should it ever be necessary, the targetProtocol to this method (which is currently
662+
// not used) serves this purpose.
663+
framework.Logf("Found hit in log line for node %s: %s", pod.Spec.NodeName, logLine)
664+
logLineExploded := strings.Fields(logLine)
665+
if len(logLineExploded) != 4 {
666+
return nil, fmt.Errorf("Unexpected logline content %s", logLine)
667+
}
668+
ipAddressPortExploded := strings.Split(logLineExploded[2], ".")
669+
if len(ipAddressPortExploded) == 2 {
670+
// ipv6
671+
ip = ipAddressPortExploded[0]
672+
} else if len(ipAddressPortExploded) == 5 {
673+
// ipv4
674+
ip = strings.Join(ipAddressPortExploded[:len(ipAddressPortExploded)-1], ".")
675+
} else {
676+
return nil, fmt.Errorf("Unexpected logline content, invalid IP/Port: %s", logLine)
678677
}
678+
matchedIPs[ip]++
679679
}
680680
}
681681
return matchedIPs, nil
@@ -1271,26 +1271,28 @@ func sendProbesToHostPort(oc *exutil.CLI, proberPod *v1.Pod, url, targetProtocol
12711271
request := fmt.Sprintf("http://%s/dial?protocol=%s&host=%s&port=%d&request=%s", url, targetProtocol, targetHost, targetPort, randomIDStr)
12721272
var wg sync.WaitGroup
12731273
errChan := make(chan error, iterations)
1274+
12741275
for i := 0; i < iterations; i++ {
12751276
// Make sure that we don´t reuse the i variable when passing it to the go func.
1276-
i := i
1277+
interval := i
12771278
// Randomize the start time a little bit per go routine.
12781279
// Max of 250 ms * current iteration counter
1279-
n := rand.Intn(250) * i
1280-
framework.Logf("Sleeping for %d ms for iteration %d", n, i)
1280+
n := rand.Intn(250) * interval
1281+
framework.Logf("Sleeping for %d ms for iteration %d", n, interval)
12811282
wg.Add(1)
12821283
go func() {
12831284
defer wg.Done()
12841285
time.Sleep(time.Duration(n) * time.Millisecond)
1285-
output, err := runOcWithRetry(oc.AsAdmin(), "exec", proberPod.Name, "--", "curl", "--max-time", "15", "-s", request)
1286+
output, err := runOcWithRetry(oc.AsAdmin(), "exec", proberPod.Name, "--", "curl", "--max-time", "15", "-s", fmt.Sprintf("%s_%d", request, i))
1287+
framework.Logf("Probed with output: %s", output)
12861288
// Report errors.
12871289
if err != nil {
12881290
errChan <- fmt.Errorf("Query failed. Request: %s, Output: %s, Error: %v", request, output, err)
12891291
}
1290-
return
12911292
}()
12921293
}
12931294
wg.Wait()
1295+
close(errChan) // Close the channel after all goroutines finish
12941296

12951297
// If the above yielded any errors, then append them to a list and report them.
12961298
if len(errChan) > 0 {
@@ -1729,21 +1731,21 @@ func cloudPrivateIpConfigExists(oc *exutil.CLI, cloudNetworkClientset cloudnetwo
17291731
}
17301732

17311733
// egressIPStatusHasIP returns if a given ip was found in a given EgressIP object's status field.
1732-
func egressIPStatusHasIP(oc *exutil.CLI, egressIPObjectName string, ip string) (bool, error) {
1734+
func egressIPStatusHasIP(oc *exutil.CLI, egressIPObjectName string, ip string) (bool, string, error) {
17331735
eip, err := getEgressIP(oc, egressIPObjectName)
17341736
if err != nil {
17351737
if errors.IsNotFound(err) {
1736-
return false, nil
1738+
return false, "", nil
17371739
}
1738-
return false, fmt.Errorf("Error looking up EgressIP %s, err: %v", egressIPObjectName, err)
1740+
return false, "", fmt.Errorf("Error looking up EgressIP %s, err: %v", egressIPObjectName, err)
17391741
}
17401742
for _, egressIPStatusItem := range eip.Status.Items {
17411743
if egressIPStatusItem.EgressIP == ip {
1742-
return true, nil
1744+
return true, egressIPStatusItem.Node, nil
17431745
}
17441746
}
17451747

1746-
return false, nil
1748+
return false, "", nil
17471749
}
17481750

17491751
// sdnNamespaceAddEgressIP adds EgressIP <egressip> to netnamespace <namespace>.

0 commit comments

Comments
 (0)