github.com/containerd/nerdctl@v1.7.7/pkg/portutil/portutil_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 portutil 18 19 import ( 20 "reflect" 21 "runtime" 22 "sort" 23 "testing" 24 25 gocni "github.com/containerd/go-cni" 26 "github.com/containerd/nerdctl/pkg/labels" 27 "github.com/containerd/nerdctl/pkg/rootlessutil" 28 ) 29 30 func TestTestParseFlagPWithPlatformSpec(t *testing.T) { 31 if runtime.GOOS != "Linux" || rootlessutil.IsRootless() { 32 t.Skip("no non-Linux platform or rootless mode in Linux are not supported yet") 33 } 34 type args struct { 35 s string 36 } 37 tests := []struct { 38 name string 39 args args 40 want []gocni.PortMapping 41 wantErr bool 42 }{ 43 { 44 name: "without colon", 45 args: args{ 46 s: "3000", 47 }, 48 want: []gocni.PortMapping{ 49 { 50 HostPort: 3000, 51 ContainerPort: 3000, 52 Protocol: "tcp", 53 HostIP: "0.0.0.0", 54 }, 55 }, 56 wantErr: false, 57 }, 58 { 59 name: "Enable auto host port", 60 args: args{ 61 s: "3000-3001", 62 }, 63 want: []gocni.PortMapping{ 64 { 65 HostPort: 3000, 66 ContainerPort: 3000, 67 Protocol: "tcp", 68 HostIP: "0.0.0.0", 69 }, 70 { 71 HostPort: 3001, 72 ContainerPort: 3001, 73 Protocol: "tcp", 74 HostIP: "0.0.0.0", 75 }, 76 }, 77 wantErr: false, 78 }, 79 { 80 name: "Enable auto host port error", 81 args: args{ 82 s: "49153-61000", 83 }, 84 want: nil, 85 wantErr: true, 86 }, 87 { 88 name: "Enable auto host port with tcp protocol", 89 args: args{ 90 s: "3000-3001/tcp", 91 }, 92 want: []gocni.PortMapping{ 93 { 94 HostPort: 3000, 95 ContainerPort: 3000, 96 Protocol: "tcp", 97 HostIP: "0.0.0.0", 98 }, 99 { 100 HostPort: 3001, 101 ContainerPort: 3001, 102 Protocol: "tcp", 103 HostIP: "0.0.0.0", 104 }, 105 }, 106 wantErr: false, 107 }, 108 { 109 name: "Enable auto host port with udp protocol", 110 args: args{ 111 s: "3000-3001/udp", 112 }, 113 want: []gocni.PortMapping{ 114 { 115 HostPort: 3000, 116 ContainerPort: 3000, 117 Protocol: "tcp", 118 HostIP: "0.0.0.0", 119 }, 120 { 121 HostPort: 3001, 122 ContainerPort: 3001, 123 Protocol: "tcp", 124 HostIP: "0.0.0.0", 125 }, 126 }, 127 wantErr: false, 128 }, 129 } 130 for _, tt := range tests { 131 t.Run(tt.name, func(t *testing.T) { 132 got, err := ParseFlagP(tt.args.s) 133 t.Log(err) 134 if (err != nil) != tt.wantErr { 135 t.Errorf("ParseFlagP() error = %v, wantErr %v", err, tt.wantErr) 136 return 137 } 138 if !reflect.DeepEqual(got, tt.want) { 139 if len(got) == len(tt.want) { 140 if len(got) > 1 { 141 var hostPorts []int32 142 var containerPorts []int32 143 for _, value := range got { 144 hostPorts = append(hostPorts, value.HostPort) 145 containerPorts = append(containerPorts, value.ContainerPort) 146 } 147 sort.Slice(hostPorts, func(i, j int) bool { 148 return i < j 149 }) 150 sort.Slice(containerPorts, func(i, j int) bool { 151 return i < j 152 }) 153 if (hostPorts[len(hostPorts)-1] - hostPorts[0]) != (containerPorts[len(hostPorts)-1] - containerPorts[0]) { 154 t.Errorf("ParseFlagP() = %v, want %v", got, tt.want) 155 } 156 } 157 } else { 158 t.Errorf("ParseFlagP() = %v, want %v", got, tt.want) 159 } 160 } 161 }) 162 } 163 164 } 165 166 func TestParsePortsLabel(t *testing.T) { 167 tests := []struct { 168 name string 169 labelMap map[string]string 170 want []gocni.PortMapping 171 wantErr bool 172 }{ 173 { 174 name: "normal", 175 labelMap: map[string]string{ 176 labels.Ports: "[{\"HostPort\":12345,\"ContainerPort\":10000,\"Protocol\":\"tcp\",\"HostIP\":\"0.0.0.0\"}]", 177 }, 178 want: []gocni.PortMapping{ 179 { 180 HostPort: 3000, 181 ContainerPort: 8080, 182 Protocol: "tcp", 183 HostIP: "127.0.0.1", 184 }, 185 }, 186 wantErr: false, 187 }, 188 { 189 name: "empty ports (value empty)", 190 labelMap: map[string]string{ 191 labels.Ports: "", 192 }, 193 want: []gocni.PortMapping{}, 194 wantErr: false, 195 }, 196 { 197 name: "empty ports (key not exists)", 198 labelMap: map[string]string{}, 199 want: []gocni.PortMapping{}, 200 wantErr: false, 201 }, 202 { 203 name: "parse error (wrong format)", 204 labelMap: map[string]string{ 205 labels.Ports: "{\"HostPort\":12345,\"ContainerPort\":10000,\"Protocol\":\"tcp\",\"HostIP\":\"0.0.0.0\"}", 206 }, 207 want: nil, 208 wantErr: true, 209 }, 210 } 211 for _, tt := range tests { 212 t.Run(tt.name, func(t *testing.T) { 213 got, err := ParsePortsLabel(tt.labelMap) 214 t.Log(err) 215 if (err != nil) != tt.wantErr { 216 t.Errorf("ParsePortsLabel() error = %v, wantErr %v", err, tt.wantErr) 217 return 218 } 219 if !reflect.DeepEqual(got, tt.want) { 220 if len(got) == len(tt.want) { 221 if len(got) > 1 { 222 var hostPorts []int32 223 var containerPorts []int32 224 for _, value := range got { 225 hostPorts = append(hostPorts, value.HostPort) 226 containerPorts = append(containerPorts, value.ContainerPort) 227 } 228 sort.Slice(hostPorts, func(i, j int) bool { 229 return i < j 230 }) 231 sort.Slice(containerPorts, func(i, j int) bool { 232 return i < j 233 }) 234 if (hostPorts[len(hostPorts)-1] - hostPorts[0]) != (containerPorts[len(hostPorts)-1] - containerPorts[0]) { 235 t.Errorf("ParsePortsLabel() = %v, want %v", got, tt.want) 236 } 237 } 238 } else { 239 t.Errorf("ParsePortsLabel() = %v, want %v", got, tt.want) 240 } 241 } 242 }) 243 } 244 } 245 246 func TestParseFlagP(t *testing.T) { 247 type args struct { 248 s string 249 } 250 tests := []struct { 251 name string 252 args args 253 want []gocni.PortMapping 254 wantErr bool 255 }{ 256 { 257 name: "normal", 258 args: args{ 259 s: "127.0.0.1:3000:8080/tcp", 260 }, 261 want: []gocni.PortMapping{ 262 { 263 HostPort: 3000, 264 ContainerPort: 8080, 265 Protocol: "tcp", 266 HostIP: "127.0.0.1", 267 }, 268 }, 269 wantErr: false, 270 }, 271 { 272 name: "with port range", 273 args: args{ 274 s: "127.0.0.1:3000-3001:8080-8081/tcp", 275 }, 276 want: []gocni.PortMapping{ 277 { 278 HostPort: 3000, 279 ContainerPort: 8080, 280 Protocol: "tcp", 281 HostIP: "127.0.0.1", 282 }, 283 { 284 HostPort: 3001, 285 ContainerPort: 8081, 286 Protocol: "tcp", 287 HostIP: "127.0.0.1", 288 }, 289 }, 290 wantErr: false, 291 }, 292 { 293 name: "with wrong port range", 294 args: args{ 295 s: "127.0.0.1:3000-3001:8080-8082/tcp", 296 }, 297 want: nil, 298 wantErr: true, 299 }, 300 { 301 name: "without host ip", 302 args: args{ 303 s: "3000:8080/tcp", 304 }, 305 want: []gocni.PortMapping{ 306 { 307 HostPort: 3000, 308 ContainerPort: 8080, 309 Protocol: "tcp", 310 HostIP: "0.0.0.0", 311 }, 312 }, 313 wantErr: false, 314 }, 315 { 316 name: "without protocol", 317 args: args{ 318 s: "3000:8080", 319 }, 320 want: []gocni.PortMapping{ 321 { 322 HostPort: 3000, 323 ContainerPort: 8080, 324 Protocol: "tcp", 325 HostIP: "0.0.0.0", 326 }, 327 }, 328 wantErr: false, 329 }, 330 { 331 name: "with protocol udp", 332 args: args{ 333 s: "3000:8080/udp", 334 }, 335 want: []gocni.PortMapping{ 336 { 337 HostPort: 3000, 338 ContainerPort: 8080, 339 Protocol: "udp", 340 HostIP: "0.0.0.0", 341 }, 342 }, 343 wantErr: false, 344 }, 345 { 346 name: "with protocol udp", 347 args: args{ 348 s: "3000:8080/sctp", 349 }, 350 want: []gocni.PortMapping{ 351 { 352 HostPort: 3000, 353 ContainerPort: 8080, 354 Protocol: "sctp", 355 HostIP: "0.0.0.0", 356 }, 357 }, 358 wantErr: false, 359 }, 360 { 361 name: "with invalid protocol", 362 args: args{ 363 s: "3000:8080/invalid", 364 }, 365 want: nil, 366 wantErr: true, 367 }, 368 { 369 name: "multiple colon", 370 args: args{ 371 s: "127.0.0.1:3000:0.0.0.0:8080", 372 }, 373 want: nil, 374 wantErr: true, 375 }, 376 { 377 name: "multiple slash", 378 args: args{ 379 s: "127.0.0.1:3000:8080/tcp/", 380 }, 381 want: nil, 382 wantErr: true, 383 }, 384 { 385 name: "invalid ip", 386 args: args{ 387 s: "127.0.0.256:3000:8080/tcp", 388 }, 389 want: nil, 390 wantErr: true, 391 }, 392 { 393 name: "large port", 394 args: args{ 395 s: "3000:65536", 396 }, 397 want: nil, 398 wantErr: true, 399 }, 400 { 401 name: "blank", 402 args: args{ 403 s: "", 404 }, 405 want: nil, 406 wantErr: true, 407 }, 408 } 409 for _, tt := range tests { 410 t.Run(tt.name, func(t *testing.T) { 411 got, err := ParseFlagP(tt.args.s) 412 t.Log(err) 413 if (err != nil) != tt.wantErr { 414 t.Errorf("ParseFlagP() error = %v, wantErr %v", err, tt.wantErr) 415 return 416 } 417 if !reflect.DeepEqual(got, tt.want) { 418 if len(got) == len(tt.want) { 419 if len(got) > 1 { 420 var hostPorts []int32 421 var containerPorts []int32 422 for _, value := range got { 423 hostPorts = append(hostPorts, value.HostPort) 424 containerPorts = append(containerPorts, value.ContainerPort) 425 } 426 sort.Slice(hostPorts, func(i, j int) bool { 427 return i < j 428 }) 429 sort.Slice(containerPorts, func(i, j int) bool { 430 return i < j 431 }) 432 if (hostPorts[len(hostPorts)-1] - hostPorts[0]) != (containerPorts[len(hostPorts)-1] - containerPorts[0]) { 433 t.Errorf("ParseFlagP() = %v, want %v", got, tt.want) 434 } 435 } 436 } else { 437 t.Errorf("ParseFlagP() = %v, want %v", got, tt.want) 438 } 439 } 440 }) 441 } 442 }