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  }