2018-02-28 16:59:04 +00:00
// Copyright 2018 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
2023-02-28 22:27:25 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2018-02-28 16:59:04 +00:00
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package integration_test
import (
2023-02-28 22:27:25 +00:00
"bytes"
2018-02-28 16:59:04 +00:00
"fmt"
2023-02-28 22:27:25 +00:00
"io"
2023-06-28 17:07:23 +00:00
"log"
2018-02-28 16:59:04 +00:00
"math/rand"
2023-02-28 22:27:25 +00:00
"net"
2018-02-28 16:59:04 +00:00
"os"
"os/exec"
"path/filepath"
2018-03-12 22:08:53 +00:00
"regexp"
"strconv"
"strings"
"time"
2023-01-18 10:30:11 +00:00
. "github.com/onsi/ginkgo/v2"
2018-02-28 16:59:04 +00:00
. "github.com/onsi/gomega"
2018-03-12 22:08:53 +00:00
"github.com/onsi/gomega/gbytes"
2018-02-28 16:59:04 +00:00
"github.com/onsi/gomega/gexec"
)
var _ = Describe ( "Basic PTP using cnitool" , func ( ) {
var (
2018-02-28 21:28:06 +00:00
cnitoolBin string
2018-03-12 22:08:53 +00:00
cniPath string
2018-02-28 16:59:04 +00:00
)
BeforeEach ( func ( ) {
2018-03-12 22:08:53 +00:00
var err error
cniPath , err = filepath . Abs ( "../bin" )
2018-02-28 16:59:04 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
cnitoolBin , err = exec . LookPath ( "cnitool" )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "expected to find cnitool in your PATH" )
2018-03-12 22:08:53 +00:00
} )
Context ( "basic cases" , func ( ) {
var (
env TestEnv
hostNS Namespace
contNS Namespace
)
BeforeEach ( func ( ) {
var err error
netConfPath , err := filepath . Abs ( "./testdata" )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2023-06-28 17:07:23 +00:00
// Flush ipam stores to avoid conflicts
err = os . RemoveAll ( "/tmp/chained-ptp-bandwidth-test" )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
err = os . RemoveAll ( "/tmp/basic-ptp-test" )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2018-03-12 22:08:53 +00:00
env = TestEnv ( [ ] string {
"CNI_PATH=" + cniPath ,
"NETCONFPATH=" + netConfPath ,
"PATH=" + os . Getenv ( "PATH" ) ,
} )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
hostNS = Namespace ( fmt . Sprintf ( "cni-test-host-%x" , rand . Int31 ( ) ) )
hostNS . Add ( )
contNS = Namespace ( fmt . Sprintf ( "cni-test-cont-%x" , rand . Int31 ( ) ) )
contNS . Add ( )
} )
AfterEach ( func ( ) {
contNS . Del ( )
hostNS . Del ( )
2018-02-28 16:59:04 +00:00
} )
2018-03-12 22:08:53 +00:00
basicAssertion := func ( netName , expectedIPPrefix string ) {
env . runInNS ( hostNS , cnitoolBin , "add" , netName , contNS . LongName ( ) )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
addrOutput := env . runInNS ( contNS , "ip" , "addr" )
2023-06-28 17:07:23 +00:00
2018-03-12 22:08:53 +00:00
Expect ( addrOutput ) . To ( ContainSubstring ( expectedIPPrefix ) )
env . runInNS ( hostNS , cnitoolBin , "del" , netName , contNS . LongName ( ) )
}
It ( "supports basic network add and del operations" , func ( ) {
basicAssertion ( "basic-ptp" , "10.1.2." )
} )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
It ( "supports add and del with ptp + bandwidth" , func ( ) {
basicAssertion ( "chained-ptp-bandwidth" , "10.9.2." )
} )
2018-02-28 21:28:06 +00:00
} )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
Context ( "when the bandwidth plugin is chained with a plugin that returns multiple adapters" , func ( ) {
var (
hostNS Namespace
contNS1 Namespace
contNS2 Namespace
basicBridgeEnv TestEnv
chainedBridgeBandwidthEnv TestEnv
chainedBridgeBandwidthSession , basicBridgeSession * gexec . Session
)
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
BeforeEach ( func ( ) {
hostNS = Namespace ( fmt . Sprintf ( "cni-test-host-%x" , rand . Int31 ( ) ) )
hostNS . Add ( )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
contNS1 = Namespace ( fmt . Sprintf ( "cni-test-cont1-%x" , rand . Int31 ( ) ) )
contNS1 . Add ( )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
contNS2 = Namespace ( fmt . Sprintf ( "cni-test-cont2-%x" , rand . Int31 ( ) ) )
contNS2 . Add ( )
2018-02-28 16:59:04 +00:00
2018-03-12 22:08:53 +00:00
basicBridgeNetConfPath , err := filepath . Abs ( "./testdata/basic-bridge" )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
basicBridgeEnv = TestEnv ( [ ] string {
"CNI_PATH=" + cniPath ,
"NETCONFPATH=" + basicBridgeNetConfPath ,
"PATH=" + os . Getenv ( "PATH" ) ,
} )
chainedBridgeBandwidthNetConfPath , err := filepath . Abs ( "./testdata/chained-bridge-bandwidth" )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
chainedBridgeBandwidthEnv = TestEnv ( [ ] string {
"CNI_PATH=" + cniPath ,
"NETCONFPATH=" + chainedBridgeBandwidthNetConfPath ,
"PATH=" + os . Getenv ( "PATH" ) ,
} )
} )
AfterEach ( func ( ) {
if chainedBridgeBandwidthSession != nil {
chainedBridgeBandwidthSession . Kill ( )
}
if basicBridgeSession != nil {
basicBridgeSession . Kill ( )
}
chainedBridgeBandwidthEnv . runInNS ( hostNS , cnitoolBin , "del" , "network-chain-test" , contNS1 . LongName ( ) )
basicBridgeEnv . runInNS ( hostNS , cnitoolBin , "del" , "network-chain-test" , contNS2 . LongName ( ) )
2023-06-28 17:07:23 +00:00
contNS1 . Del ( )
contNS2 . Del ( )
hostNS . Del ( )
2018-03-12 22:08:53 +00:00
} )
2023-06-28 17:07:23 +00:00
It ( "limits traffic only on the restricted bandwidth veth device" , func ( ) {
2023-02-28 22:27:25 +00:00
ipRegexp := regexp . MustCompile ( ` 10\.1[12]\.2\.\d { 1,3} ` )
2018-03-12 22:08:53 +00:00
2018-03-12 22:53:23 +00:00
By ( fmt . Sprintf ( "adding %s to %s\n\n" , "chained-bridge-bandwidth" , contNS1 . ShortName ( ) ) )
chainedBridgeBandwidthEnv . runInNS ( hostNS , cnitoolBin , "add" , "network-chain-test" , contNS1 . LongName ( ) )
chainedBridgeIP := ipRegexp . FindString ( chainedBridgeBandwidthEnv . runInNS ( contNS1 , "ip" , "addr" ) )
2019-11-11 12:30:02 +00:00
Expect ( chainedBridgeIP ) . To ( ContainSubstring ( "10.12.2." ) )
2018-02-28 16:59:04 +00:00
2018-03-12 22:53:23 +00:00
By ( fmt . Sprintf ( "adding %s to %s\n\n" , "basic-bridge" , contNS2 . ShortName ( ) ) )
basicBridgeEnv . runInNS ( hostNS , cnitoolBin , "add" , "network-chain-test" , contNS2 . LongName ( ) )
basicBridgeIP := ipRegexp . FindString ( basicBridgeEnv . runInNS ( contNS2 , "ip" , "addr" ) )
Expect ( basicBridgeIP ) . To ( ContainSubstring ( "10.11.2." ) )
2018-03-12 22:08:53 +00:00
2018-03-12 22:53:23 +00:00
var chainedBridgeBandwidthPort , basicBridgePort int
2018-03-12 22:08:53 +00:00
2018-03-12 22:53:23 +00:00
By ( fmt . Sprintf ( "starting echo server in %s\n\n" , contNS1 . ShortName ( ) ) )
2023-04-11 10:07:04 +00:00
chainedBridgeBandwidthPort , chainedBridgeBandwidthSession = startEchoServerInNamespace ( contNS1 )
2018-03-12 22:08:53 +00:00
2018-03-12 22:53:23 +00:00
By ( fmt . Sprintf ( "starting echo server in %s\n\n" , contNS2 . ShortName ( ) ) )
2023-04-11 10:07:04 +00:00
basicBridgePort , basicBridgeSession = startEchoServerInNamespace ( contNS2 )
2018-03-12 22:08:53 +00:00
2023-06-28 17:07:23 +00:00
packetInBytes := 3000
2018-03-12 22:08:53 +00:00
2018-03-12 22:53:23 +00:00
By ( fmt . Sprintf ( "sending tcp traffic to the chained, bridged, traffic shaped container on ip address '%s:%d'\n\n" , chainedBridgeIP , chainedBridgeBandwidthPort ) )
2023-06-28 17:07:23 +00:00
start := time . Now ( )
makeTCPClientInNS ( hostNS . ShortName ( ) , chainedBridgeIP , chainedBridgeBandwidthPort , packetInBytes )
runtimeWithLimit := time . Since ( start )
log . Printf ( "Runtime with qos limit %.2f seconds" , runtimeWithLimit . Seconds ( ) )
2018-03-12 22:08:53 +00:00
2018-03-12 22:53:23 +00:00
By ( fmt . Sprintf ( "sending tcp traffic to the basic bridged container on ip address '%s:%d'\n\n" , basicBridgeIP , basicBridgePort ) )
2023-06-28 17:07:23 +00:00
start = time . Now ( )
makeTCPClientInNS ( hostNS . ShortName ( ) , basicBridgeIP , basicBridgePort , packetInBytes )
runtimeWithoutLimit := time . Since ( start )
log . Printf ( "Runtime without qos limit %.2f seconds" , runtimeWithLimit . Seconds ( ) )
2018-03-12 22:08:53 +00:00
Expect ( runtimeWithLimit ) . To ( BeNumerically ( ">" , runtimeWithoutLimit + 1000 * time . Millisecond ) )
2023-06-28 17:07:23 +00:00
} )
2018-02-28 16:59:04 +00:00
} )
} )
type TestEnv [ ] string
func ( e TestEnv ) run ( bin string , args ... string ) string {
cmd := exec . Command ( bin , args ... )
cmd . Env = e
session , err := gexec . Start ( cmd , GinkgoWriter , GinkgoWriter )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
Eventually ( session , "5s" ) . Should ( gexec . Exit ( 0 ) )
return string ( session . Out . Contents ( ) )
}
2018-02-28 21:28:06 +00:00
2018-03-12 22:08:53 +00:00
func ( e TestEnv ) runInNS ( nsShortName Namespace , bin string , args ... string ) string {
2018-02-28 21:28:06 +00:00
a := append ( [ ] string { "netns" , "exec" , string ( nsShortName ) , bin } , args ... )
return e . run ( "ip" , a ... )
}
2018-03-12 22:08:53 +00:00
type Namespace string
2018-02-28 21:28:06 +00:00
2018-03-12 22:08:53 +00:00
func ( n Namespace ) LongName ( ) string {
2018-02-28 21:28:06 +00:00
return fmt . Sprintf ( "/var/run/netns/%s" , n )
}
2018-03-12 22:08:53 +00:00
func ( n Namespace ) ShortName ( ) string {
return string ( n )
}
func ( n Namespace ) Add ( ) {
2018-02-28 21:28:06 +00:00
( TestEnv { } ) . run ( "ip" , "netns" , "add" , string ( n ) )
}
2018-03-12 22:08:53 +00:00
func ( n Namespace ) Del ( ) {
2018-02-28 21:28:06 +00:00
( TestEnv { } ) . run ( "ip" , "netns" , "del" , string ( n ) )
}
2018-03-12 22:08:53 +00:00
2023-03-12 10:25:53 +00:00
func makeTCPClientInNS ( netns string , address string , port int , numBytes int ) {
2020-10-02 13:56:39 +00:00
payload := bytes . Repeat ( [ ] byte { 'a' } , numBytes )
message := string ( payload )
2018-03-12 22:08:53 +00:00
var cmd * exec . Cmd
if netns != "" {
netns = filepath . Base ( netns )
2020-10-02 13:56:39 +00:00
cmd = exec . Command ( "ip" , "netns" , "exec" , netns , echoClientBinaryPath , "--target" , fmt . Sprintf ( "%s:%d" , address , port ) , "--message" , message )
2018-03-12 22:08:53 +00:00
} else {
2020-10-02 13:56:39 +00:00
cmd = exec . Command ( echoClientBinaryPath , "--target" , fmt . Sprintf ( "%s:%d" , address , port ) , "--message" , message )
2018-03-12 22:08:53 +00:00
}
cmd . Stdin = bytes . NewBuffer ( [ ] byte ( message ) )
cmd . Stderr = GinkgoWriter
out , err := cmd . Output ( )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2020-10-02 13:56:39 +00:00
Expect ( string ( out ) ) . To ( Equal ( message ) )
2018-03-12 22:08:53 +00:00
}
2023-04-11 10:07:04 +00:00
func startEchoServerInNamespace ( netNS Namespace ) ( int , * gexec . Session ) {
2018-03-12 22:08:53 +00:00
session , err := startInNetNS ( echoServerBinaryPath , netNS )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
// wait for it to print it's address on stdout
Eventually ( session . Out ) . Should ( gbytes . Say ( "\n" ) )
_ , portString , err := net . SplitHostPort ( strings . TrimSpace ( string ( session . Out . Contents ( ) ) ) )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
port , err := strconv . Atoi ( portString )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
go func ( ) {
// print out echoserver output to ginkgo to capture any errors that might be occurring.
io . Copy ( GinkgoWriter , io . MultiReader ( session . Out , session . Err ) )
} ( )
2023-04-11 10:07:04 +00:00
return port , session
2018-03-12 22:08:53 +00:00
}
func startInNetNS ( binPath string , namespace Namespace ) ( * gexec . Session , error ) {
cmd := exec . Command ( "ip" , "netns" , "exec" , namespace . ShortName ( ) , binPath )
return gexec . Start ( cmd , GinkgoWriter , GinkgoWriter )
}