Merge pull request #52096 from deahtstroke/50159-migrate-TestAPIStatsNetworkStats-from-integration-cli

integration/container: Migrate TestAPIStatsNetworkStats to integratio…
This commit is contained in:
Paweł Gronowski
2026-06-18 12:54:22 +02:00
committed by GitHub
2 changed files with 89 additions and 80 deletions

View File

@@ -6,9 +6,6 @@ import (
"fmt"
"io"
"net/http"
"os/exec"
"runtime"
"strconv"
"strings"
"testing"
"time"
@@ -99,83 +96,6 @@ func (s *DockerAPISuite) TestAPIStatsStoppedContainerInGoroutines(c *testing.T)
}
}
func (s *DockerAPISuite) TestAPIStatsNetworkStats(c *testing.T) {
skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
testRequires(c, testEnv.IsLocalDaemon)
id := runSleepingContainer(c)
cli.WaitRun(c, id)
// Retrieve the container address
net := "bridge"
if testEnv.DaemonInfo.OSType == "windows" {
net = "nat"
}
contIP := findContainerIP(c, id, net)
numPings := 1
var preRxPackets uint64
var preTxPackets uint64
var postRxPackets uint64
var postTxPackets uint64
// Get the container networking stats before and after pinging the container
nwStatsPre := getNetworkStats(c, id)
for _, v := range nwStatsPre {
preRxPackets += v.RxPackets
preTxPackets += v.TxPackets
}
countParam := "-c"
if runtime.GOOS == "windows" {
countParam = "-n" // Ping count parameter is -n on Windows
}
pingout, err := exec.Command("ping", contIP, countParam, strconv.Itoa(numPings)).CombinedOutput()
if err != nil && runtime.GOOS == "linux" {
// If it fails then try a work-around, but just for linux.
// If this fails too then go back to the old error for reporting.
//
// The ping will sometimes fail due to an apparmor issue where it
// denies access to the libc.so.6 shared library - running it
// via /lib64/ld-linux-x86-64.so.2 seems to work around it.
pingout2, err2 := exec.Command("/lib64/ld-linux-x86-64.so.2", "/bin/ping", contIP, "-c", strconv.Itoa(numPings)).CombinedOutput()
if err2 == nil {
pingout = pingout2
err = err2
}
}
assert.NilError(c, err)
pingouts := string(pingout[:])
nwStatsPost := getNetworkStats(c, id)
for _, v := range nwStatsPost {
postRxPackets += v.RxPackets
postTxPackets += v.TxPackets
}
// Verify the stats contain at least the expected number of packets
// On Linux, account for ARP.
expRxPkts := preRxPackets + uint64(numPings)
expTxPkts := preTxPackets + uint64(numPings)
if testEnv.DaemonInfo.OSType != "windows" {
expRxPkts++
expTxPkts++
}
assert.Assert(c, postTxPackets >= expTxPkts, "Reported less TxPackets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingouts)
assert.Assert(c, postRxPackets >= expRxPkts, "Reported less RxPackets than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingouts)
}
func getNetworkStats(t *testing.T, id string) map[string]container.NetworkStats {
_, body, err := request.Get(testutil.GetContext(t), "/containers/"+id+"/stats?stream=false")
assert.NilError(t, err)
var st container.StatsResponse
err = json.NewDecoder(body).Decode(&st)
assert.NilError(t, err)
_ = body.Close()
return st.Networks
}
func (s *DockerAPISuite) TestAPIStatsNoStreamConnectedContainers(c *testing.T) {
testRequires(c, DaemonIsLinux)

View File

@@ -1,10 +1,15 @@
package container
import (
"context"
"encoding/json"
"io"
"os/exec"
"reflect"
"runtime"
"strconv"
"testing"
"time"
cerrdefs "github.com/containerd/errdefs"
containertypes "github.com/moby/moby/api/types/container"
@@ -12,6 +17,7 @@ import (
"github.com/moby/moby/v2/integration/internal/container"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/poll"
"gotest.tools/v3/skip"
)
@@ -93,3 +99,86 @@ func TestStatsContainerNotFound(t *testing.T) {
})
}
}
func TestStatsNetworkStats(t *testing.T) {
// FIXME(thaJeztah): Broken on Windows + containerd combination, see https://github.com/moby/moby/pull/41479
skip.If(t, testEnv.RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
skip.If(t, testEnv.IsRootless() && testEnv.DaemonInfo.CgroupVersion == "1", "Rootless Mode does not support cgroups v1 stats")
ctx := setupTest(t)
apiClient := testEnv.APIClient()
cID := container.Run(ctx, t, apiClient)
net := "bridge"
if testEnv.DaemonInfo.OSType == "windows" {
net = "nat"
}
res, err := apiClient.ContainerInspect(ctx, cID, client.ContainerInspectOptions{})
assert.NilError(t, err)
containerIP := res.Container.NetworkSettings.Networks[net].IPAddress.String()
// Get the container networking stats before pinging the container
var preRxPackets, preTxPackets uint64
for _, v := range getNetworkStats(ctx, t, apiClient, cID) {
preRxPackets += v.RxPackets
preTxPackets += v.TxPackets
}
countParam := "-c"
if runtime.GOOS == "windows" {
countParam = "-n" // Ping count parameter is -n on Windows
}
numPings := 1
out, err := exec.Command("ping", containerIP, countParam, strconv.Itoa(numPings)).CombinedOutput()
if err != nil && runtime.GOOS == "linux" {
// If it fails then try a work-around, but just for linux.
// If this fails too then go back to the old error for reporting.
//
// The ping will sometimes fail due to an apparmor issue where it
// denies access to the libc.so.6 shared library - running it
// via /lib64/ld-linux-x86-64.so.2 seems to work around it.
out, err = exec.Command("/lib64/ld-linux-x86-64.so.2", "/bin/ping", containerIP, countParam, strconv.Itoa(numPings)).CombinedOutput()
}
pingOutput := string(out)
assert.NilError(t, err, pingOutput)
// Verify the stats contain at least the expected number of packets
expRxPkts := preRxPackets + uint64(numPings)
expTxPkts := preTxPackets + uint64(numPings)
// Poll for both PostTxPackets and PostRxPackets until they have the expected quantity
poll.WaitOn(t, func(l poll.LogT) poll.Result {
var postRxPackets, postTxPackets uint64
for _, v := range getNetworkStats(ctx, t, apiClient, cID) {
postTxPackets += v.TxPackets
postRxPackets += v.RxPackets
}
if postTxPackets < expTxPkts {
return poll.Continue("Reported less Tx packets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingOutput)
}
if postRxPackets < expRxPkts {
return poll.Continue("Reported less Rx packets than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingOutput)
}
return poll.Success()
}, poll.WithDelay(100*time.Millisecond), poll.WithTimeout(2*time.Second))
}
func getNetworkStats(ctx context.Context, t *testing.T, apiClient client.APIClient, id string) map[string]containertypes.NetworkStats {
res, err := apiClient.ContainerStats(ctx, id, client.ContainerStatsOptions{Stream: false})
assert.NilError(t, err)
var st containertypes.StatsResponse
err = json.NewDecoder(res.Body).Decode(&st)
assert.NilError(t, err)
_ = res.Body.Close()
return st.Networks
}