bandwidth plugin: split unit tests in several files
Signed-off-by: Raphael <oOraph@users.noreply.github.com>
This commit is contained in:

committed by
Tomofumi Hayashi

parent
ab0b386b4e
commit
c666d1400d
824
plugins/meta/bandwidth/bandwidth_measure_linux_test.go
Normal file
824
plugins/meta/bandwidth/bandwidth_measure_linux_test.go
Normal file
@ -0,0 +1,824 @@
|
||||
// Copyright 2023 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
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
types100 "github.com/containernetworking/cni/pkg/types/100"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
)
|
||||
|
||||
var _ = Describe("bandwidth measure test", func() {
|
||||
var (
|
||||
hostNs ns.NetNS
|
||||
containerNs ns.NetNS
|
||||
hostIfname string
|
||||
containerIfname string
|
||||
hostIP net.IP
|
||||
containerIP net.IP
|
||||
hostIfaceMTU int
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
|
||||
hostIfname = "host-veth"
|
||||
containerIfname = "container-veth"
|
||||
|
||||
hostNs, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerNs, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
hostIP = net.IP{169, 254, 0, 1}
|
||||
containerIP = net.IP{10, 254, 0, 1}
|
||||
hostIfaceMTU = 1024
|
||||
|
||||
createVeth(hostNs, hostIfname, containerNs, containerIfname, hostIP, containerIP, hostIfaceMTU)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(containerNs.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerNs)).To(Succeed())
|
||||
Expect(hostNs.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(hostNs)).To(Succeed())
|
||||
})
|
||||
|
||||
// Bandwidth requires host-side interface info, and thus only
|
||||
// supports 0.3.0 and later CNI versions
|
||||
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
|
||||
Describe(fmt.Sprintf("[%s] QoS effective", ver), func() {
|
||||
Context(fmt.Sprintf("[%s] when chaining bandwidth plugin with PTP", ver), func() {
|
||||
var ptpConf string
|
||||
var rateInBits uint64
|
||||
var burstInBits uint64
|
||||
var packetInBytes int
|
||||
var containerWithoutQoSNS ns.NetNS
|
||||
var containerWithQoSNS ns.NetNS
|
||||
var portServerWithQoS int
|
||||
var portServerWithoutQoS int
|
||||
|
||||
var containerWithQoSRes types.Result
|
||||
var containerWithoutQoSRes types.Result
|
||||
var echoServerWithQoS *gexec.Session
|
||||
var echoServerWithoutQoS *gexec.Session
|
||||
var dataDir string
|
||||
|
||||
BeforeEach(func() {
|
||||
rateInBytes := 1000
|
||||
rateInBits = uint64(rateInBytes * 8)
|
||||
burstInBits = rateInBits * 2
|
||||
|
||||
// NOTE: Traffic shapping is not that precise at low rates, would be better to use higher rates + simple time+netcat for data transfer, rather than the provided
|
||||
// client/server bin (limited to small amount of data)
|
||||
packetInBytes = rateInBytes * 3
|
||||
|
||||
var err error
|
||||
dataDir, err = os.MkdirTemp("", "bandwidth_linux_test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ptpConf = fmt.Sprintf(`{
|
||||
"cniVersion": "%s",
|
||||
"name": "myBWnet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 512,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s"
|
||||
}
|
||||
}`, ver, dataDir)
|
||||
|
||||
const (
|
||||
containerWithQoSIFName = "ptp0"
|
||||
containerWithoutQoSIFName = "ptp1"
|
||||
)
|
||||
|
||||
containerWithQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("create two containers, and use the bandwidth plugin on one of them")
|
||||
Expect(hostNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
containerWithQoSRes, _, err = testutils.CmdAdd(containerWithQoSNS.Path(), "dummy", containerWithQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSRes, _, err = testutils.CmdAdd(containerWithoutQoSNS.Path(), "dummy2", containerWithoutQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithQoSResult, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &bandwidthPluginConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
}
|
||||
bandwidthPluginConf.Type = "bandwidth"
|
||||
newConfBytes, err := buildOneConfig(ver, bandwidthPluginConf, containerWithQoSResult)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newConfBytes,
|
||||
}
|
||||
|
||||
result, out, err := testutils.CmdAdd(containerWithQoSNS.Path(), args.ContainerID, "", newConfBytes, func() error { return cmdAdd(args) })
|
||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||
|
||||
if testutils.SpecVersionHasCHECK(ver) {
|
||||
// Do CNI Check
|
||||
checkConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &checkConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
checkConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
}
|
||||
checkConf.Type = "bandwidth"
|
||||
|
||||
newCheckBytes, err := buildOneConfig(ver, checkConf, result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args = &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newCheckBytes,
|
||||
}
|
||||
|
||||
err = testutils.CmdCheck(containerWithQoSNS.Path(), args.ContainerID, "", func() error { return cmdCheck(args) })
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
|
||||
By("starting a tcp server on both containers")
|
||||
portServerWithQoS, echoServerWithQoS = startEchoServerInNamespace(containerWithQoSNS)
|
||||
portServerWithoutQoS, echoServerWithoutQoS = startEchoServerInNamespace(containerWithoutQoSNS)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||
|
||||
Expect(containerWithQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithQoSNS)).To(Succeed())
|
||||
Expect(containerWithoutQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithoutQoSNS)).To(Succeed())
|
||||
|
||||
if echoServerWithoutQoS != nil {
|
||||
echoServerWithoutQoS.Kill()
|
||||
}
|
||||
if echoServerWithQoS != nil {
|
||||
echoServerWithQoS.Kill()
|
||||
}
|
||||
})
|
||||
|
||||
It("limits ingress traffic on veth device", func() {
|
||||
var runtimeWithLimit time.Duration
|
||||
var runtimeWithoutLimit time.Duration
|
||||
|
||||
By("gather timing statistics about both containers")
|
||||
|
||||
By("sending tcp traffic to the container that has traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithLimit = end.Sub(start)
|
||||
log.Printf("Elapsed with qos %.2f", runtimeWithLimit.Seconds())
|
||||
})
|
||||
|
||||
By("sending tcp traffic to the container that does not have traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithoutQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithoutLimit = end.Sub(start)
|
||||
log.Printf("Elapsed without qos %.2f", runtimeWithoutLimit.Seconds())
|
||||
})
|
||||
|
||||
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context(fmt.Sprintf("[%s] when chaining bandwidth plugin with PTP and excluding specific subnets from traffic", ver), func() {
|
||||
var ptpConf string
|
||||
var rateInBits uint64
|
||||
var burstInBits uint64
|
||||
var packetInBytes int
|
||||
var containerWithoutQoSNS ns.NetNS
|
||||
var containerWithQoSNS ns.NetNS
|
||||
var portServerWithQoS int
|
||||
var portServerWithoutQoS int
|
||||
|
||||
var containerWithQoSRes types.Result
|
||||
var containerWithoutQoSRes types.Result
|
||||
var echoServerWithQoS *gexec.Session
|
||||
var echoServerWithoutQoS *gexec.Session
|
||||
var dataDir string
|
||||
|
||||
BeforeEach(func() {
|
||||
rateInBytes := 1000
|
||||
rateInBits = uint64(rateInBytes * 8)
|
||||
burstInBits = rateInBits * 2
|
||||
unshapedSubnets := []string{"10.1.2.0/24"}
|
||||
// NOTE: Traffic shapping is not that precise at low rates, would be better to use higher rates + simple time+netcat for data transfer, rather than the provided
|
||||
// client/server bin (limited to small amount of data)
|
||||
packetInBytes = rateInBytes * 3
|
||||
|
||||
var err error
|
||||
dataDir, err = os.MkdirTemp("", "bandwidth_linux_test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ptpConf = fmt.Sprintf(`{
|
||||
"cniVersion": "%s",
|
||||
"name": "myBWnet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 512,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s"
|
||||
}
|
||||
}`, ver, dataDir)
|
||||
|
||||
const (
|
||||
containerWithQoSIFName = "ptp0"
|
||||
containerWithoutQoSIFName = "ptp1"
|
||||
)
|
||||
|
||||
containerWithQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("create two containers, and use the bandwidth plugin on one of them")
|
||||
Expect(hostNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
containerWithQoSRes, _, err = testutils.CmdAdd(containerWithQoSNS.Path(), "dummy", containerWithQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSRes, _, err = testutils.CmdAdd(containerWithoutQoSNS.Path(), "dummy2", containerWithoutQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithQoSResult, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &bandwidthPluginConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
UnshapedSubnets: unshapedSubnets,
|
||||
}
|
||||
bandwidthPluginConf.Type = "bandwidth"
|
||||
newConfBytes, err := buildOneConfig(ver, bandwidthPluginConf, containerWithQoSResult)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newConfBytes,
|
||||
}
|
||||
|
||||
result, out, err := testutils.CmdAdd(containerWithQoSNS.Path(), args.ContainerID, "", newConfBytes, func() error { return cmdAdd(args) })
|
||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||
|
||||
if testutils.SpecVersionHasCHECK(ver) {
|
||||
// Do CNI Check
|
||||
checkConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &checkConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
checkConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
UnshapedSubnets: unshapedSubnets,
|
||||
}
|
||||
checkConf.Type = "bandwidth"
|
||||
|
||||
newCheckBytes, err := buildOneConfig(ver, checkConf, result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args = &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newCheckBytes,
|
||||
}
|
||||
|
||||
err = testutils.CmdCheck(containerWithQoSNS.Path(), args.ContainerID, "", func() error { return cmdCheck(args) })
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
|
||||
By("starting a tcp server on both containers")
|
||||
portServerWithQoS, echoServerWithQoS = startEchoServerInNamespace(containerWithQoSNS)
|
||||
portServerWithoutQoS, echoServerWithoutQoS = startEchoServerInNamespace(containerWithoutQoSNS)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||
|
||||
Expect(containerWithQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithQoSNS)).To(Succeed())
|
||||
Expect(containerWithoutQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithoutQoSNS)).To(Succeed())
|
||||
|
||||
if echoServerWithoutQoS != nil {
|
||||
echoServerWithoutQoS.Kill()
|
||||
}
|
||||
if echoServerWithQoS != nil {
|
||||
echoServerWithQoS.Kill()
|
||||
}
|
||||
})
|
||||
|
||||
It("does not limits ingress traffic on veth device coming from 10.1.2.0/24", func() {
|
||||
var runtimeWithLimit time.Duration
|
||||
var runtimeWithoutLimit time.Duration
|
||||
|
||||
By("gather timing statistics about both containers")
|
||||
|
||||
By("sending tcp traffic to the container that has traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithLimit = end.Sub(start)
|
||||
log.Printf("Elapsed with qos %.2f", runtimeWithLimit.Seconds())
|
||||
})
|
||||
|
||||
By("sending tcp traffic to the container that does not have traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithoutQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithoutLimit = end.Sub(start)
|
||||
log.Printf("Elapsed without qos %.2f", runtimeWithoutLimit.Seconds())
|
||||
})
|
||||
|
||||
Expect(runtimeWithLimit - runtimeWithoutLimit).To(BeNumerically("<", 100*time.Millisecond))
|
||||
})
|
||||
})
|
||||
|
||||
Context(fmt.Sprintf("[%s] when chaining bandwidth plugin with PTP and only including specific subnets in traffic shapping (not including the main ns one)", ver), func() {
|
||||
var ptpConf string
|
||||
var rateInBits uint64
|
||||
var burstInBits uint64
|
||||
var packetInBytes int
|
||||
var containerWithoutQoSNS ns.NetNS
|
||||
var containerWithQoSNS ns.NetNS
|
||||
var portServerWithQoS int
|
||||
var portServerWithoutQoS int
|
||||
|
||||
var containerWithQoSRes types.Result
|
||||
var containerWithoutQoSRes types.Result
|
||||
var echoServerWithQoS *gexec.Session
|
||||
var echoServerWithoutQoS *gexec.Session
|
||||
var dataDir string
|
||||
|
||||
BeforeEach(func() {
|
||||
rateInBytes := 1000
|
||||
rateInBits = uint64(rateInBytes * 8)
|
||||
burstInBits = rateInBits * 2
|
||||
shapedSubnets := []string{"10.2.2.0/24"}
|
||||
// NOTE: Traffic shapping is not that precise at low rates, would be better to use higher rates + simple time+netcat for data transfer, rather than the provided
|
||||
// client/server bin (limited to small amount of data)
|
||||
packetInBytes = rateInBytes * 3
|
||||
|
||||
var err error
|
||||
dataDir, err = os.MkdirTemp("", "bandwidth_linux_test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ptpConf = fmt.Sprintf(`{
|
||||
"cniVersion": "%s",
|
||||
"name": "myBWnet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 512,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s"
|
||||
}
|
||||
}`, ver, dataDir)
|
||||
|
||||
const (
|
||||
containerWithQoSIFName = "ptp0"
|
||||
containerWithoutQoSIFName = "ptp1"
|
||||
)
|
||||
|
||||
containerWithQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("create two containers, and use the bandwidth plugin on one of them")
|
||||
|
||||
Expect(hostNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
containerWithQoSRes, _, err = testutils.CmdAdd(containerWithQoSNS.Path(), "dummy", containerWithQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSRes, _, err = testutils.CmdAdd(containerWithoutQoSNS.Path(), "dummy2", containerWithoutQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithQoSResult, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &bandwidthPluginConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
ShapedSubnets: shapedSubnets,
|
||||
}
|
||||
bandwidthPluginConf.Type = "bandwidth"
|
||||
newConfBytes, err := buildOneConfig(ver, bandwidthPluginConf, containerWithQoSResult)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newConfBytes,
|
||||
}
|
||||
|
||||
result, out, err := testutils.CmdAdd(containerWithQoSNS.Path(), args.ContainerID, "", newConfBytes, func() error { return cmdAdd(args) })
|
||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||
|
||||
if testutils.SpecVersionHasCHECK(ver) {
|
||||
// Do CNI Check
|
||||
checkConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &checkConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
checkConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
ShapedSubnets: shapedSubnets,
|
||||
}
|
||||
checkConf.Type = "bandwidth"
|
||||
|
||||
newCheckBytes, err := buildOneConfig(ver, checkConf, result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args = &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newCheckBytes,
|
||||
}
|
||||
|
||||
err = testutils.CmdCheck(containerWithQoSNS.Path(), args.ContainerID, "", func() error { return cmdCheck(args) })
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
|
||||
By("starting a tcp server on both containers")
|
||||
portServerWithQoS, echoServerWithQoS = startEchoServerInNamespace(containerWithQoSNS)
|
||||
portServerWithoutQoS, echoServerWithoutQoS = startEchoServerInNamespace(containerWithoutQoSNS)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||
|
||||
Expect(containerWithQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithQoSNS)).To(Succeed())
|
||||
Expect(containerWithoutQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithoutQoSNS)).To(Succeed())
|
||||
|
||||
if echoServerWithoutQoS != nil {
|
||||
echoServerWithoutQoS.Kill()
|
||||
}
|
||||
if echoServerWithQoS != nil {
|
||||
echoServerWithQoS.Kill()
|
||||
}
|
||||
})
|
||||
|
||||
It("does not limit ingress traffic on veth device coming from non included subnets", func() {
|
||||
var runtimeWithLimit time.Duration
|
||||
var runtimeWithoutLimit time.Duration
|
||||
|
||||
By("gather timing statistics about both containers")
|
||||
|
||||
By("sending tcp traffic to the container that has traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithLimit = end.Sub(start)
|
||||
log.Printf("Elapsed with qos %.2f", runtimeWithLimit.Seconds())
|
||||
})
|
||||
|
||||
By("sending tcp traffic to the container that does not have traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithoutQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithoutLimit = end.Sub(start)
|
||||
log.Printf("Elapsed without qos %.2f", runtimeWithoutLimit.Seconds())
|
||||
})
|
||||
|
||||
Expect(runtimeWithLimit - runtimeWithoutLimit).To(BeNumerically("<", 100*time.Millisecond))
|
||||
})
|
||||
})
|
||||
|
||||
Context(fmt.Sprintf("[%s] when chaining bandwidth plugin with PTP and only including specific subnets in traffic shapping (including the main ns one)", ver), func() {
|
||||
var ptpConf string
|
||||
var rateInBits uint64
|
||||
var burstInBits uint64
|
||||
var packetInBytes int
|
||||
var containerWithoutQoSNS ns.NetNS
|
||||
var containerWithQoSNS ns.NetNS
|
||||
var portServerWithQoS int
|
||||
var portServerWithoutQoS int
|
||||
|
||||
var containerWithQoSRes types.Result
|
||||
var containerWithoutQoSRes types.Result
|
||||
var echoServerWithQoS *gexec.Session
|
||||
var echoServerWithoutQoS *gexec.Session
|
||||
var dataDir string
|
||||
|
||||
BeforeEach(func() {
|
||||
rateInBytes := 1000
|
||||
rateInBits = uint64(rateInBytes * 8)
|
||||
burstInBits = rateInBits * 2
|
||||
shapedSubnets := []string{"10.1.2.1/32"}
|
||||
// NOTE: Traffic shapping is not that precise at low rates, would be better to use higher rates + simple time+netcat for data transfer, rather than the provided
|
||||
// client/server bin (limited to small amount of data)
|
||||
packetInBytes = rateInBytes * 3
|
||||
|
||||
var err error
|
||||
dataDir, err = os.MkdirTemp("", "bandwidth_linux_test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ptpConf = fmt.Sprintf(`{
|
||||
"cniVersion": "%s",
|
||||
"name": "myBWnet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 512,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s"
|
||||
}
|
||||
}`, ver, dataDir)
|
||||
|
||||
const (
|
||||
containerWithQoSIFName = "ptp0"
|
||||
containerWithoutQoSIFName = "ptp1"
|
||||
)
|
||||
|
||||
containerWithQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("create two containers, and use the bandwidth plugin on one of them")
|
||||
|
||||
Expect(hostNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
containerWithQoSRes, _, err = testutils.CmdAdd(containerWithQoSNS.Path(), "dummy", containerWithQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithoutQoSRes, _, err = testutils.CmdAdd(containerWithoutQoSNS.Path(), "dummy2", containerWithoutQoSIFName, []byte(ptpConf), func() error {
|
||||
r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(r.Print()).To(Succeed())
|
||||
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerWithQoSResult, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &bandwidthPluginConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
bandwidthPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
ShapedSubnets: shapedSubnets,
|
||||
}
|
||||
bandwidthPluginConf.Type = "bandwidth"
|
||||
newConfBytes, err := buildOneConfig(ver, bandwidthPluginConf, containerWithQoSResult)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newConfBytes,
|
||||
}
|
||||
|
||||
result, out, err := testutils.CmdAdd(containerWithQoSNS.Path(), args.ContainerID, "", newConfBytes, func() error { return cmdAdd(args) })
|
||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||
|
||||
if testutils.SpecVersionHasCHECK(ver) {
|
||||
// Do CNI Check
|
||||
checkConf := &PluginConf{}
|
||||
err = json.Unmarshal([]byte(ptpConf), &checkConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
checkConf.RuntimeConfig.Bandwidth = &BandwidthEntry{
|
||||
IngressBurst: burstInBits,
|
||||
IngressRate: rateInBits,
|
||||
EgressBurst: burstInBits,
|
||||
EgressRate: rateInBits,
|
||||
ShapedSubnets: shapedSubnets,
|
||||
}
|
||||
checkConf.Type = "bandwidth"
|
||||
|
||||
newCheckBytes, err := buildOneConfig(ver, checkConf, result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
args = &skel.CmdArgs{
|
||||
ContainerID: "dummy3",
|
||||
Netns: containerWithQoSNS.Path(),
|
||||
IfName: containerWithQoSIFName,
|
||||
StdinData: newCheckBytes,
|
||||
}
|
||||
|
||||
err = testutils.CmdCheck(containerWithQoSNS.Path(), args.ContainerID, "", func() error { return cmdCheck(args) })
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
|
||||
By("starting a tcp server on both containers")
|
||||
portServerWithQoS, echoServerWithQoS = startEchoServerInNamespace(containerWithQoSNS)
|
||||
portServerWithoutQoS, echoServerWithoutQoS = startEchoServerInNamespace(containerWithoutQoSNS)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||
|
||||
Expect(containerWithQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithQoSNS)).To(Succeed())
|
||||
Expect(containerWithoutQoSNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(containerWithoutQoSNS)).To(Succeed())
|
||||
|
||||
if echoServerWithoutQoS != nil {
|
||||
echoServerWithoutQoS.Kill()
|
||||
}
|
||||
if echoServerWithQoS != nil {
|
||||
echoServerWithQoS.Kill()
|
||||
}
|
||||
})
|
||||
|
||||
It("limits ingress traffic on veth device coming from included subnets", func() {
|
||||
var runtimeWithLimit time.Duration
|
||||
var runtimeWithoutLimit time.Duration
|
||||
|
||||
By("gather timing statistics about both containers")
|
||||
|
||||
By("sending tcp traffic to the container that has traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithLimit = end.Sub(start)
|
||||
log.Printf("Elapsed with qos %.2f", runtimeWithLimit.Seconds())
|
||||
})
|
||||
|
||||
By("sending tcp traffic to the container that does not have traffic shaped", func() {
|
||||
start := time.Now()
|
||||
result, err := types100.GetResult(containerWithoutQoSRes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
makeTCPClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutQoS, packetInBytes)
|
||||
end := time.Now()
|
||||
runtimeWithoutLimit = end.Sub(start)
|
||||
log.Printf("Elapsed without qos %.2f", runtimeWithoutLimit.Seconds())
|
||||
})
|
||||
|
||||
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user