github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_run_network_base_test.go (about) 1 //go:build linux || windows 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package main 20 21 import ( 22 "fmt" 23 "io" 24 "net" 25 "regexp" 26 "strings" 27 "testing" 28 29 "github.com/containerd/nerdctl/pkg/testutil" 30 "github.com/containerd/nerdctl/pkg/testutil/nettestutil" 31 "gotest.tools/v3/assert" 32 ) 33 34 // Tests various port mapping argument combinations by starting an nginx container and 35 // verifying its connectivity and that its serves its index.html from the external 36 // host IP as well as through the loopback interface. 37 // `loopbackIsolationEnabled` indicates whether the test should expect connections between 38 // the loopback interface and external host interface to succeed or not. 39 func baseTestRunPort(t *testing.T, nginxImage string, nginxIndexHTMLSnippet string, loopbackIsolationEnabled bool) { 40 expectedIsolationErr := "" 41 if loopbackIsolationEnabled { 42 expectedIsolationErr = testutil.ExpectedConnectionRefusedError 43 } 44 45 hostIP, err := nettestutil.NonLoopbackIPv4() 46 assert.NilError(t, err) 47 type testCase struct { 48 listenIP net.IP 49 connectIP net.IP 50 hostPort string 51 containerPort string 52 connectURLPort int 53 runShouldSuccess bool 54 err string 55 } 56 lo := net.ParseIP("127.0.0.1") 57 zeroIP := net.ParseIP("0.0.0.0") 58 testCases := []testCase{ 59 { 60 listenIP: lo, 61 connectIP: lo, 62 hostPort: "8080", 63 containerPort: "80", 64 connectURLPort: 8080, 65 runShouldSuccess: true, 66 }, 67 { 68 // for https://github.com/containerd/nerdctl/issues/88 69 listenIP: hostIP, 70 connectIP: hostIP, 71 hostPort: "8080", 72 containerPort: "80", 73 connectURLPort: 8080, 74 runShouldSuccess: true, 75 }, 76 { 77 listenIP: hostIP, 78 connectIP: lo, 79 hostPort: "8080", 80 containerPort: "80", 81 connectURLPort: 8080, 82 err: expectedIsolationErr, 83 runShouldSuccess: true, 84 }, 85 { 86 listenIP: lo, 87 connectIP: hostIP, 88 hostPort: "8080", 89 containerPort: "80", 90 connectURLPort: 8080, 91 err: expectedIsolationErr, 92 runShouldSuccess: true, 93 }, 94 { 95 listenIP: zeroIP, 96 connectIP: lo, 97 hostPort: "8080", 98 containerPort: "80", 99 connectURLPort: 8080, 100 runShouldSuccess: true, 101 }, 102 { 103 listenIP: zeroIP, 104 connectIP: hostIP, 105 hostPort: "8080", 106 containerPort: "80", 107 connectURLPort: 8080, 108 runShouldSuccess: true, 109 }, 110 { 111 listenIP: lo, 112 connectIP: lo, 113 hostPort: "7000-7005", 114 containerPort: "79-84", 115 connectURLPort: 7001, 116 runShouldSuccess: true, 117 }, 118 { 119 listenIP: hostIP, 120 connectIP: hostIP, 121 hostPort: "7000-7005", 122 containerPort: "79-84", 123 connectURLPort: 7001, 124 runShouldSuccess: true, 125 }, 126 { 127 listenIP: hostIP, 128 connectIP: lo, 129 hostPort: "7000-7005", 130 containerPort: "79-84", 131 connectURLPort: 7001, 132 err: expectedIsolationErr, 133 runShouldSuccess: true, 134 }, 135 { 136 listenIP: lo, 137 connectIP: hostIP, 138 hostPort: "7000-7005", 139 containerPort: "79-84", 140 connectURLPort: 7001, 141 err: expectedIsolationErr, 142 runShouldSuccess: true, 143 }, 144 { 145 listenIP: zeroIP, 146 connectIP: hostIP, 147 hostPort: "7000-7005", 148 containerPort: "79-84", 149 connectURLPort: 7001, 150 runShouldSuccess: true, 151 }, 152 { 153 listenIP: zeroIP, 154 connectIP: lo, 155 hostPort: "7000-7005", 156 containerPort: "80-85", 157 connectURLPort: 7001, 158 err: "error after 30 attempts", 159 runShouldSuccess: true, 160 }, 161 { 162 listenIP: zeroIP, 163 connectIP: lo, 164 hostPort: "7000-7005", 165 containerPort: "80", 166 connectURLPort: 7000, 167 runShouldSuccess: true, 168 }, 169 { 170 listenIP: zeroIP, 171 connectIP: lo, 172 hostPort: "7000-7005", 173 containerPort: "80", 174 connectURLPort: 7005, 175 err: testutil.ExpectedConnectionRefusedError, 176 runShouldSuccess: true, 177 }, 178 { 179 listenIP: zeroIP, 180 connectIP: lo, 181 hostPort: "7000-7005", 182 containerPort: "79-85", 183 connectURLPort: 7005, 184 err: "invalid ranges specified for container and host Ports", 185 runShouldSuccess: false, 186 }, 187 } 188 189 tID := testutil.Identifier(t) 190 for i, tc := range testCases { 191 i := i 192 tc := tc 193 tcName := fmt.Sprintf("%+v", tc) 194 t.Run(tcName, func(t *testing.T) { 195 testContainerName := fmt.Sprintf("%s-%d", tID, i) 196 base := testutil.NewBase(t) 197 defer base.Cmd("rm", "-f", testContainerName).Run() 198 pFlag := fmt.Sprintf("%s:%s:%s", tc.listenIP.String(), tc.hostPort, tc.containerPort) 199 connectURL := fmt.Sprintf("http://%s:%d", tc.connectIP.String(), tc.connectURLPort) 200 t.Logf("pFlag=%q, connectURL=%q", pFlag, connectURL) 201 cmd := base.Cmd("run", "-d", 202 "--name", testContainerName, 203 "-p", pFlag, 204 nginxImage) 205 if tc.runShouldSuccess { 206 cmd.AssertOK() 207 } else { 208 cmd.AssertFail() 209 return 210 } 211 212 resp, err := nettestutil.HTTPGet(connectURL, 30, false) 213 if tc.err != "" { 214 assert.ErrorContains(t, err, tc.err) 215 return 216 } 217 assert.NilError(t, err) 218 respBody, err := io.ReadAll(resp.Body) 219 assert.NilError(t, err) 220 assert.Assert(t, strings.Contains(string(respBody), nginxIndexHTMLSnippet)) 221 }) 222 } 223 224 } 225 226 func valuesOfMapStringString(m map[string]string) map[string]struct{} { 227 res := make(map[string]struct{}) 228 for _, v := range m { 229 res[v] = struct{}{} 230 } 231 return res 232 } 233 234 func extractHostPort(portMapping string, port string) (string, error) { 235 // Regular expression to extract host port from port mapping information 236 re := regexp.MustCompile(`(?P<containerPort>\d{1,5})/tcp ->.*?0.0.0.0:(?P<hostPort>\d{1,5}).*?`) 237 portMappingLines := strings.Split(portMapping, "\n") 238 for _, portMappingLine := range portMappingLines { 239 // Find the matches 240 matches := re.FindStringSubmatch(portMappingLine) 241 // Check if there is a match 242 if len(matches) >= 3 && matches[1] == port { 243 // Extract the host port number 244 hostPort := matches[2] 245 return hostPort, nil 246 } 247 } 248 return "", fmt.Errorf("could not extract host port from port mapping: %s", portMapping) 249 }