github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_run_network_windows_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/Microsoft/hcsshim"
    26  	"github.com/containerd/nerdctl/pkg/defaults"
    27  	"github.com/containerd/nerdctl/pkg/netutil"
    28  	"github.com/containerd/nerdctl/pkg/testutil"
    29  	"gotest.tools/v3/assert"
    30  )
    31  
    32  // TestRunInternetConnectivity tests Internet connectivity by pinging github.com.
    33  func TestRunInternetConnectivity(t *testing.T) {
    34  	base := testutil.NewBase(t)
    35  
    36  	type testCase struct {
    37  		args []string
    38  	}
    39  	testCases := []testCase{
    40  		{
    41  			args: []string{"--net", "nat"},
    42  		},
    43  	}
    44  	for _, tc := range testCases {
    45  		tc := tc // IMPORTANT
    46  		name := "default"
    47  		if len(tc.args) > 0 {
    48  			name = strings.Join(tc.args, "_")
    49  		}
    50  		t.Run(name, func(t *testing.T) {
    51  			args := []string{"run", "--rm"}
    52  			args = append(args, tc.args...)
    53  			// TODO(aznashwan): smarter way to ensure internet connectivity is working.
    54  			// ping doesn't seem to work on GitHub Actions ("Request timed out.")
    55  			args = append(args, testutil.CommonImage, "curl.exe -sSL https://github.com")
    56  			cmd := base.Cmd(args...)
    57  			cmd.AssertOutContains("<!DOCTYPE html>")
    58  		})
    59  	}
    60  }
    61  
    62  func TestRunPort(t *testing.T) {
    63  	// NOTE: currently no isolation between the loopback and host namespaces on Windows.
    64  	baseTestRunPort(t, testutil.NginxAlpineImage, testutil.NginxAlpineIndexHTMLSnippet, false)
    65  }
    66  
    67  // Checks whether an HNS endpoint with a name matching exists.
    68  func listHnsEndpointsRegex(hnsEndpointNameRegex string) ([]hcsshim.HNSEndpoint, error) {
    69  	r, err := regexp.Compile(hnsEndpointNameRegex)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	hnsEndpoints, err := hcsshim.HNSListEndpointRequest()
    74  	if err != nil {
    75  		return nil, fmt.Errorf("failed to list HNS endpoints for request: %s", err)
    76  	}
    77  
    78  	res := []hcsshim.HNSEndpoint{}
    79  	for _, endp := range hnsEndpoints {
    80  		if r.Match([]byte(endp.Name)) {
    81  			res = append(res, endp)
    82  		}
    83  	}
    84  	return res, nil
    85  }
    86  
    87  // Asserts whether the container with the provided has any HNS endpoints with the expected
    88  // naming format (`${container_id}_${network_name}`) for all of the provided network names.
    89  // The container ID can be a regex.
    90  func assertHnsEndpointsExistence(t *testing.T, shouldExist bool, containerIDRegex string, networkNames ...string) {
    91  	for _, netName := range networkNames {
    92  		endpointName := fmt.Sprintf("%s_%s", containerIDRegex, netName)
    93  
    94  		testName := fmt.Sprintf("hns_endpoint_%s_shouldExist_%t", endpointName, shouldExist)
    95  		t.Run(testName, func(t *testing.T) {
    96  			matchingEndpoints, err := listHnsEndpointsRegex(endpointName)
    97  			assert.NilError(t, err)
    98  			if shouldExist {
    99  				assert.Equal(t, len(matchingEndpoints), 1)
   100  				assert.Equal(t, matchingEndpoints[0].Name, endpointName)
   101  			} else {
   102  				assert.Equal(t, len(matchingEndpoints), 0)
   103  			}
   104  		})
   105  	}
   106  }
   107  
   108  // Tests whether HNS endpoints are properly created and managed throughout the lifecycle of a container.
   109  func TestHnsEndpointsExistDuringContainerLifecycle(t *testing.T) {
   110  	base := testutil.NewBase(t)
   111  
   112  	testNet, err := getTestingNetwork()
   113  	assert.NilError(t, err)
   114  
   115  	tID := testutil.Identifier(t)
   116  	defer base.Cmd("rm", "-f", tID).Run()
   117  	cmd := base.Cmd(
   118  		"create",
   119  		"--name", tID,
   120  		"--net", testNet.Name,
   121  		testutil.CommonImage,
   122  		"bash", "-c",
   123  		// NOTE: the BusyBox image used in Windows testing's `sleep` binary
   124  		// does not support the `infinity` argument.
   125  		"tail", "-f",
   126  	)
   127  	t.Logf("Creating HNS lifecycle test container with command: %q", strings.Join(cmd.Command, " "))
   128  	containerId := strings.TrimSpace(cmd.Run().Stdout())
   129  	t.Logf("HNS endpoint lifecycle test container ID: %q", containerId)
   130  
   131  	// HNS endpoints should be allocated on container creation.
   132  	assertHnsEndpointsExistence(t, true, containerId, testNet.Name)
   133  
   134  	// Starting and stopping the container should NOT affect/change the endpoints.
   135  	base.Cmd("start", containerId).AssertOK()
   136  	assertHnsEndpointsExistence(t, true, containerId, testNet.Name)
   137  
   138  	base.Cmd("stop", containerId).AssertOK()
   139  	assertHnsEndpointsExistence(t, true, containerId, testNet.Name)
   140  
   141  	// Removing the container should remove the HNS endpoints.
   142  	base.Cmd("rm", containerId).AssertOK()
   143  	assertHnsEndpointsExistence(t, false, containerId, testNet.Name)
   144  }
   145  
   146  // Returns a network to be used for testing.
   147  // Note: currently hardcoded to return the default network, as `network create`
   148  // does not work on Windows.
   149  func getTestingNetwork() (*netutil.NetworkConfig, error) {
   150  	// NOTE: cannot currently `nerdctl network create` on Windows so we use a pre-existing network:
   151  	cniEnv, err := netutil.NewCNIEnv(defaults.CNIPath(), defaults.CNINetConfPath())
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	return cniEnv.GetDefaultNetworkConfig()
   157  }
   158  
   159  // Tests whether HNS endpoints are properly removed when running `run --rm`.
   160  func TestHnsEndpointsRemovedAfterAttachedRun(t *testing.T) {
   161  	base := testutil.NewBase(t)
   162  
   163  	testNet, err := getTestingNetwork()
   164  	assert.NilError(t, err)
   165  
   166  	// NOTE: because we cannot set/obtain the ID of the container to check for the exact HNS
   167  	// endpoint name, we record the number of HNS endpoints on the testing network and
   168  	// ensure it remains constant until after the test.
   169  	existingEndpoints, err := listHnsEndpointsRegex(fmt.Sprintf(".*_%s", testNet.Name))
   170  	assert.NilError(t, err)
   171  	originalEndpointsCount := len(existingEndpoints)
   172  
   173  	tID := testutil.Identifier(t)
   174  	base.Cmd(
   175  		"run",
   176  		"--name",
   177  		tID,
   178  		"--rm",
   179  		"--net", testNet.Name,
   180  		testutil.CommonImage,
   181  		"ipconfig", "/all",
   182  	).AssertOK()
   183  
   184  	existingEndpoints, err = listHnsEndpointsRegex(fmt.Sprintf(".*_%s", testNet.Name))
   185  	assert.NilError(t, err)
   186  	assert.Equal(t, originalEndpointsCount, len(existingEndpoints), "the number of HNS endpoints should equal pre-test amount")
   187  }