github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration/container/nat_test.go (about)

     1  package container // import "github.com/docker/docker/integration/container"
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/docker/docker/api/types"
    14  	"github.com/docker/docker/integration/internal/container"
    15  	"github.com/docker/go-connections/nat"
    16  	"gotest.tools/v3/assert"
    17  	is "gotest.tools/v3/assert/cmp"
    18  	"gotest.tools/v3/poll"
    19  	"gotest.tools/v3/skip"
    20  )
    21  
    22  func TestNetworkNat(t *testing.T) {
    23  	skip.If(t, testEnv.OSType == "windows", "FIXME")
    24  	skip.If(t, testEnv.IsRemoteDaemon)
    25  
    26  	defer setupTest(t)()
    27  
    28  	msg := "it works"
    29  	startServerContainer(t, msg, 8080)
    30  
    31  	endpoint := getExternalAddress(t)
    32  	conn, err := net.Dial("tcp", net.JoinHostPort(endpoint.String(), "8080"))
    33  	assert.NilError(t, err)
    34  	defer conn.Close()
    35  
    36  	data, err := io.ReadAll(conn)
    37  	assert.NilError(t, err)
    38  	assert.Check(t, is.Equal(msg, strings.TrimSpace(string(data))))
    39  }
    40  
    41  func TestNetworkLocalhostTCPNat(t *testing.T) {
    42  	skip.If(t, testEnv.IsRemoteDaemon)
    43  
    44  	defer setupTest(t)()
    45  
    46  	msg := "hi yall"
    47  	startServerContainer(t, msg, 8081)
    48  
    49  	conn, err := net.Dial("tcp", "localhost:8081")
    50  	assert.NilError(t, err)
    51  	defer conn.Close()
    52  
    53  	data, err := io.ReadAll(conn)
    54  	assert.NilError(t, err)
    55  	assert.Check(t, is.Equal(msg, strings.TrimSpace(string(data))))
    56  }
    57  
    58  func TestNetworkLoopbackNat(t *testing.T) {
    59  	skip.If(t, testEnv.GitHubActions, "FIXME: https://github.com/moby/moby/issues/41561")
    60  	skip.If(t, testEnv.OSType == "windows", "FIXME")
    61  	skip.If(t, testEnv.IsRemoteDaemon)
    62  
    63  	defer setupTest(t)()
    64  
    65  	msg := "it works"
    66  	serverContainerID := startServerContainer(t, msg, 8080)
    67  
    68  	endpoint := getExternalAddress(t)
    69  
    70  	client := testEnv.APIClient()
    71  	ctx := context.Background()
    72  
    73  	cID := container.Run(ctx, t, client,
    74  		container.WithCmd("sh", "-c", fmt.Sprintf("stty raw && nc -w 1 %s 8080", endpoint.String())),
    75  		container.WithTty(true),
    76  		container.WithNetworkMode("container:"+serverContainerID),
    77  	)
    78  
    79  	poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond))
    80  
    81  	body, err := client.ContainerLogs(ctx, cID, types.ContainerLogsOptions{
    82  		ShowStdout: true,
    83  	})
    84  	assert.NilError(t, err)
    85  	defer body.Close()
    86  
    87  	var b bytes.Buffer
    88  	_, err = io.Copy(&b, body)
    89  	assert.NilError(t, err)
    90  
    91  	assert.Check(t, is.Equal(msg, strings.TrimSpace(b.String())))
    92  }
    93  
    94  func startServerContainer(t *testing.T, msg string, port int) string {
    95  	t.Helper()
    96  	client := testEnv.APIClient()
    97  	ctx := context.Background()
    98  
    99  	cID := container.Run(ctx, t, client,
   100  		container.WithName("server-"+t.Name()),
   101  		container.WithCmd("sh", "-c", fmt.Sprintf("echo %q | nc -lp %d", msg, port)),
   102  		container.WithExposedPorts(fmt.Sprintf("%d/tcp", port)),
   103  		func(c *container.TestContainerConfig) {
   104  			c.HostConfig.PortBindings = nat.PortMap{
   105  				nat.Port(fmt.Sprintf("%d/tcp", port)): []nat.PortBinding{
   106  					{
   107  						HostPort: fmt.Sprintf("%d", port),
   108  					},
   109  				},
   110  			}
   111  		})
   112  
   113  	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
   114  
   115  	return cID
   116  }
   117  
   118  // getExternalAddress() returns the external IP-address from eth0. If eth0 has
   119  // multiple IP-addresses, it returns the first IPv4 IP-address; if no IPv4
   120  // address is present, it returns the first IP-address found.
   121  func getExternalAddress(t *testing.T) net.IP {
   122  	t.Helper()
   123  	iface, err := net.InterfaceByName("eth0")
   124  	skip.If(t, err != nil, "Test not running with `make test-integration`. Interface eth0 not found: %s", err)
   125  
   126  	ifaceAddrs, err := iface.Addrs()
   127  	assert.NilError(t, err)
   128  	assert.Check(t, 0 != len(ifaceAddrs))
   129  
   130  	if len(ifaceAddrs) > 1 {
   131  		// Prefer IPv4 address if multiple addresses found, as rootlesskit
   132  		// does not handle IPv6 currently https://github.com/moby/moby/pull/41908#issuecomment-774200001
   133  		for _, a := range ifaceAddrs {
   134  			ifaceIP, _, err := net.ParseCIDR(a.String())
   135  			assert.NilError(t, err)
   136  			if ifaceIP.To4() != nil {
   137  				return ifaceIP
   138  			}
   139  		}
   140  	}
   141  	ifaceIP, _, err := net.ParseCIDR(ifaceAddrs[0].String())
   142  	assert.NilError(t, err)
   143  
   144  	return ifaceIP
   145  }