github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/container/nat_test.go (about)

     1  package container // import "github.com/Prakhar-Agarwal-byte/moby/integration/container"
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	containertypes "github.com/Prakhar-Agarwal-byte/moby/api/types/container"
    14  	"github.com/Prakhar-Agarwal-byte/moby/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.DaemonInfo.OSType == "windows", "FIXME")
    24  	skip.If(t, testEnv.IsRemoteDaemon)
    25  
    26  	ctx := setupTest(t)
    27  
    28  	msg := "it works"
    29  	startServerContainer(ctx, 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  	ctx := setupTest(t)
    45  
    46  	msg := "hi yall"
    47  	startServerContainer(ctx, 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.DaemonInfo.OSType == "windows", "FIXME")
    61  	skip.If(t, testEnv.IsRemoteDaemon)
    62  
    63  	ctx := setupTest(t)
    64  
    65  	msg := "it works"
    66  	serverContainerID := startServerContainer(ctx, t, msg, 8080)
    67  
    68  	endpoint := getExternalAddress(t)
    69  
    70  	apiClient := testEnv.APIClient()
    71  
    72  	cID := container.Run(ctx, t, apiClient,
    73  		container.WithCmd("sh", "-c", fmt.Sprintf("stty raw && nc -w 1 %s 8080", endpoint.String())),
    74  		container.WithTty(true),
    75  		container.WithNetworkMode("container:"+serverContainerID),
    76  	)
    77  
    78  	poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond))
    79  
    80  	body, err := apiClient.ContainerLogs(ctx, cID, containertypes.LogsOptions{
    81  		ShowStdout: true,
    82  	})
    83  	assert.NilError(t, err)
    84  	defer body.Close()
    85  
    86  	var b bytes.Buffer
    87  	_, err = io.Copy(&b, body)
    88  	assert.NilError(t, err)
    89  
    90  	assert.Check(t, is.Equal(msg, strings.TrimSpace(b.String())))
    91  }
    92  
    93  func startServerContainer(ctx context.Context, t *testing.T, msg string, port int) string {
    94  	t.Helper()
    95  	apiClient := testEnv.APIClient()
    96  
    97  	return container.Run(ctx, t, apiClient,
    98  		container.WithName("server-"+t.Name()),
    99  		container.WithCmd("sh", "-c", fmt.Sprintf("echo %q | nc -lp %d", msg, port)),
   100  		container.WithExposedPorts(fmt.Sprintf("%d/tcp", port)),
   101  		func(c *container.TestContainerConfig) {
   102  			c.HostConfig.PortBindings = nat.PortMap{
   103  				nat.Port(fmt.Sprintf("%d/tcp", port)): []nat.PortBinding{
   104  					{
   105  						HostPort: fmt.Sprintf("%d", port),
   106  					},
   107  				},
   108  			}
   109  		},
   110  	)
   111  }
   112  
   113  // getExternalAddress() returns the external IP-address from eth0. If eth0 has
   114  // multiple IP-addresses, it returns the first IPv4 IP-address; if no IPv4
   115  // address is present, it returns the first IP-address found.
   116  func getExternalAddress(t *testing.T) net.IP {
   117  	t.Helper()
   118  	iface, err := net.InterfaceByName("eth0")
   119  	skip.If(t, err != nil, "Test not running with `make test-integration`. Interface eth0 not found: %s", err)
   120  
   121  	ifaceAddrs, err := iface.Addrs()
   122  	assert.NilError(t, err)
   123  	assert.Check(t, 0 != len(ifaceAddrs))
   124  
   125  	if len(ifaceAddrs) > 1 {
   126  		// Prefer IPv4 address if multiple addresses found, as rootlesskit
   127  		// does not handle IPv6 currently https://github.com/moby/moby/pull/41908#issuecomment-774200001
   128  		for _, a := range ifaceAddrs {
   129  			ifaceIP, _, err := net.ParseCIDR(a.String())
   130  			assert.NilError(t, err)
   131  			if ifaceIP.To4() != nil {
   132  				return ifaceIP
   133  			}
   134  		}
   135  	}
   136  	ifaceIP, _, err := net.ParseCIDR(ifaceAddrs[0].String())
   137  	assert.NilError(t, err)
   138  
   139  	return ifaceIP
   140  }