Signed-off-by: Daniel Villavicencio <dvm3099@pm.me>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -6,9 +6,6 @@ import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"io" |
| 8 | 8 |
"net/http" |
| 9 |
- "os/exec" |
|
| 10 |
- "runtime" |
|
| 11 |
- "strconv" |
|
| 12 | 9 |
"strings" |
| 13 | 10 |
"testing" |
| 14 | 11 |
"time" |
| ... | ... |
@@ -99,83 +96,6 @@ func (s *DockerAPISuite) TestAPIStatsStoppedContainerInGoroutines(c *testing.T) |
| 99 | 99 |
} |
| 100 | 100 |
} |
| 101 | 101 |
|
| 102 |
-func (s *DockerAPISuite) TestAPIStatsNetworkStats(c *testing.T) {
|
|
| 103 |
- skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination") |
|
| 104 |
- testRequires(c, testEnv.IsLocalDaemon) |
|
| 105 |
- |
|
| 106 |
- id := runSleepingContainer(c) |
|
| 107 |
- cli.WaitRun(c, id) |
|
| 108 |
- |
|
| 109 |
- // Retrieve the container address |
|
| 110 |
- net := "bridge" |
|
| 111 |
- if testEnv.DaemonInfo.OSType == "windows" {
|
|
| 112 |
- net = "nat" |
|
| 113 |
- } |
|
| 114 |
- contIP := findContainerIP(c, id, net) |
|
| 115 |
- numPings := 1 |
|
| 116 |
- |
|
| 117 |
- var preRxPackets uint64 |
|
| 118 |
- var preTxPackets uint64 |
|
| 119 |
- var postRxPackets uint64 |
|
| 120 |
- var postTxPackets uint64 |
|
| 121 |
- |
|
| 122 |
- // Get the container networking stats before and after pinging the container |
|
| 123 |
- nwStatsPre := getNetworkStats(c, id) |
|
| 124 |
- for _, v := range nwStatsPre {
|
|
| 125 |
- preRxPackets += v.RxPackets |
|
| 126 |
- preTxPackets += v.TxPackets |
|
| 127 |
- } |
|
| 128 |
- |
|
| 129 |
- countParam := "-c" |
|
| 130 |
- if runtime.GOOS == "windows" {
|
|
| 131 |
- countParam = "-n" // Ping count parameter is -n on Windows |
|
| 132 |
- } |
|
| 133 |
- pingout, err := exec.Command("ping", contIP, countParam, strconv.Itoa(numPings)).CombinedOutput()
|
|
| 134 |
- if err != nil && runtime.GOOS == "linux" {
|
|
| 135 |
- // If it fails then try a work-around, but just for linux. |
|
| 136 |
- // If this fails too then go back to the old error for reporting. |
|
| 137 |
- // |
|
| 138 |
- // The ping will sometimes fail due to an apparmor issue where it |
|
| 139 |
- // denies access to the libc.so.6 shared library - running it |
|
| 140 |
- // via /lib64/ld-linux-x86-64.so.2 seems to work around it. |
|
| 141 |
- pingout2, err2 := exec.Command("/lib64/ld-linux-x86-64.so.2", "/bin/ping", contIP, "-c", strconv.Itoa(numPings)).CombinedOutput()
|
|
| 142 |
- if err2 == nil {
|
|
| 143 |
- pingout = pingout2 |
|
| 144 |
- err = err2 |
|
| 145 |
- } |
|
| 146 |
- } |
|
| 147 |
- assert.NilError(c, err) |
|
| 148 |
- pingouts := string(pingout[:]) |
|
| 149 |
- nwStatsPost := getNetworkStats(c, id) |
|
| 150 |
- for _, v := range nwStatsPost {
|
|
| 151 |
- postRxPackets += v.RxPackets |
|
| 152 |
- postTxPackets += v.TxPackets |
|
| 153 |
- } |
|
| 154 |
- |
|
| 155 |
- // Verify the stats contain at least the expected number of packets |
|
| 156 |
- // On Linux, account for ARP. |
|
| 157 |
- expRxPkts := preRxPackets + uint64(numPings) |
|
| 158 |
- expTxPkts := preTxPackets + uint64(numPings) |
|
| 159 |
- if testEnv.DaemonInfo.OSType != "windows" {
|
|
| 160 |
- expRxPkts++ |
|
| 161 |
- expTxPkts++ |
|
| 162 |
- } |
|
| 163 |
- assert.Assert(c, postTxPackets >= expTxPkts, "Reported less TxPackets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingouts) |
|
| 164 |
- assert.Assert(c, postRxPackets >= expRxPkts, "Reported less RxPackets than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingouts) |
|
| 165 |
-} |
|
| 166 |
- |
|
| 167 |
-func getNetworkStats(t *testing.T, id string) map[string]container.NetworkStats {
|
|
| 168 |
- _, body, err := request.Get(testutil.GetContext(t), "/containers/"+id+"/stats?stream=false") |
|
| 169 |
- assert.NilError(t, err) |
|
| 170 |
- |
|
| 171 |
- var st container.StatsResponse |
|
| 172 |
- err = json.NewDecoder(body).Decode(&st) |
|
| 173 |
- assert.NilError(t, err) |
|
| 174 |
- _ = body.Close() |
|
| 175 |
- |
|
| 176 |
- return st.Networks |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 | 102 |
func (s *DockerAPISuite) TestAPIStatsNoStreamConnectedContainers(c *testing.T) {
|
| 180 | 103 |
testRequires(c, DaemonIsLinux) |
| 181 | 104 |
|
| ... | ... |
@@ -1,10 +1,15 @@ |
| 1 | 1 |
package container |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "context" |
|
| 4 | 5 |
"encoding/json" |
| 5 | 6 |
"io" |
| 7 |
+ "os/exec" |
|
| 6 | 8 |
"reflect" |
| 9 |
+ "runtime" |
|
| 10 |
+ "strconv" |
|
| 7 | 11 |
"testing" |
| 12 |
+ "time" |
|
| 8 | 13 |
|
| 9 | 14 |
cerrdefs "github.com/containerd/errdefs" |
| 10 | 15 |
containertypes "github.com/moby/moby/api/types/container" |
| ... | ... |
@@ -12,6 +17,7 @@ import ( |
| 12 | 12 |
"github.com/moby/moby/v2/integration/internal/container" |
| 13 | 13 |
"gotest.tools/v3/assert" |
| 14 | 14 |
is "gotest.tools/v3/assert/cmp" |
| 15 |
+ "gotest.tools/v3/poll" |
|
| 15 | 16 |
"gotest.tools/v3/skip" |
| 16 | 17 |
) |
| 17 | 18 |
|
| ... | ... |
@@ -93,3 +99,86 @@ func TestStatsContainerNotFound(t *testing.T) {
|
| 93 | 93 |
}) |
| 94 | 94 |
} |
| 95 | 95 |
} |
| 96 |
+ |
|
| 97 |
+func TestStatsNetworkStats(t *testing.T) {
|
|
| 98 |
+ // FIXME(thaJeztah): Broken on Windows + containerd combination, see https://github.com/moby/moby/pull/41479 |
|
| 99 |
+ skip.If(t, testEnv.RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination") |
|
| 100 |
+ skip.If(t, testEnv.IsRootless() && testEnv.DaemonInfo.CgroupVersion == "1", "Rootless Mode does not support cgroups v1 stats") |
|
| 101 |
+ |
|
| 102 |
+ ctx := setupTest(t) |
|
| 103 |
+ |
|
| 104 |
+ apiClient := testEnv.APIClient() |
|
| 105 |
+ |
|
| 106 |
+ cID := container.Run(ctx, t, apiClient) |
|
| 107 |
+ |
|
| 108 |
+ net := "bridge" |
|
| 109 |
+ if testEnv.DaemonInfo.OSType == "windows" {
|
|
| 110 |
+ net = "nat" |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ res, err := apiClient.ContainerInspect(ctx, cID, client.ContainerInspectOptions{})
|
|
| 114 |
+ assert.NilError(t, err) |
|
| 115 |
+ containerIP := res.Container.NetworkSettings.Networks[net].IPAddress.String() |
|
| 116 |
+ |
|
| 117 |
+ // Get the container networking stats before pinging the container |
|
| 118 |
+ var preRxPackets, preTxPackets uint64 |
|
| 119 |
+ for _, v := range getNetworkStats(ctx, t, apiClient, cID) {
|
|
| 120 |
+ preRxPackets += v.RxPackets |
|
| 121 |
+ preTxPackets += v.TxPackets |
|
| 122 |
+ } |
|
| 123 |
+ |
|
| 124 |
+ countParam := "-c" |
|
| 125 |
+ if runtime.GOOS == "windows" {
|
|
| 126 |
+ countParam = "-n" // Ping count parameter is -n on Windows |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ numPings := 1 |
|
| 130 |
+ out, err := exec.Command("ping", containerIP, countParam, strconv.Itoa(numPings)).CombinedOutput()
|
|
| 131 |
+ if err != nil && runtime.GOOS == "linux" {
|
|
| 132 |
+ // If it fails then try a work-around, but just for linux. |
|
| 133 |
+ // If this fails too then go back to the old error for reporting. |
|
| 134 |
+ // |
|
| 135 |
+ // The ping will sometimes fail due to an apparmor issue where it |
|
| 136 |
+ // denies access to the libc.so.6 shared library - running it |
|
| 137 |
+ // via /lib64/ld-linux-x86-64.so.2 seems to work around it. |
|
| 138 |
+ out, err = exec.Command("/lib64/ld-linux-x86-64.so.2", "/bin/ping", containerIP, countParam, strconv.Itoa(numPings)).CombinedOutput()
|
|
| 139 |
+ } |
|
| 140 |
+ pingOutput := string(out) |
|
| 141 |
+ assert.NilError(t, err, pingOutput) |
|
| 142 |
+ |
|
| 143 |
+ // Verify the stats contain at least the expected number of packets |
|
| 144 |
+ expRxPkts := preRxPackets + uint64(numPings) |
|
| 145 |
+ expTxPkts := preTxPackets + uint64(numPings) |
|
| 146 |
+ |
|
| 147 |
+ // Poll for both PostTxPackets and PostRxPackets until they have the expected quantity |
|
| 148 |
+ poll.WaitOn(t, func(l poll.LogT) poll.Result {
|
|
| 149 |
+ var postRxPackets, postTxPackets uint64 |
|
| 150 |
+ for _, v := range getNetworkStats(ctx, t, apiClient, cID) {
|
|
| 151 |
+ postTxPackets += v.TxPackets |
|
| 152 |
+ postRxPackets += v.RxPackets |
|
| 153 |
+ } |
|
| 154 |
+ |
|
| 155 |
+ if postTxPackets < expTxPkts {
|
|
| 156 |
+ return poll.Continue("Reported less Tx packets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingOutput)
|
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ if postRxPackets < expRxPkts {
|
|
| 160 |
+ return poll.Continue("Reported less Rx packets than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingOutput)
|
|
| 161 |
+ } |
|
| 162 |
+ |
|
| 163 |
+ return poll.Success() |
|
| 164 |
+ }, poll.WithDelay(100*time.Millisecond), poll.WithTimeout(2*time.Second)) |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+func getNetworkStats(ctx context.Context, t *testing.T, apiClient client.APIClient, id string) map[string]containertypes.NetworkStats {
|
|
| 168 |
+ res, err := apiClient.ContainerStats(ctx, id, client.ContainerStatsOptions{Stream: false})
|
|
| 169 |
+ assert.NilError(t, err) |
|
| 170 |
+ |
|
| 171 |
+ var st containertypes.StatsResponse |
|
| 172 |
+ err = json.NewDecoder(res.Body).Decode(&st) |
|
| 173 |
+ |
|
| 174 |
+ assert.NilError(t, err) |
|
| 175 |
+ _ = res.Body.Close() |
|
| 176 |
+ |
|
| 177 |
+ return st.Networks |
|
| 178 |
+} |