github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/opts/port_test.go (about)

     1  package opts
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/docker/docker/api/types/swarm"
     9  	"github.com/khulnasoft-lab/go-connections/nat"
    10  	"github.com/sirupsen/logrus"
    11  	"gotest.tools/v3/assert"
    12  	is "gotest.tools/v3/assert/cmp"
    13  )
    14  
    15  func TestPortOptValidSimpleSyntax(t *testing.T) {
    16  	testCases := []struct {
    17  		value    string
    18  		expected []swarm.PortConfig
    19  	}{
    20  		{
    21  			value: "80",
    22  			expected: []swarm.PortConfig{
    23  				{
    24  					Protocol:    "tcp",
    25  					TargetPort:  80,
    26  					PublishMode: swarm.PortConfigPublishModeIngress,
    27  				},
    28  			},
    29  		},
    30  		{
    31  			value: "80:8080",
    32  			expected: []swarm.PortConfig{
    33  				{
    34  					Protocol:      "tcp",
    35  					TargetPort:    8080,
    36  					PublishedPort: 80,
    37  					PublishMode:   swarm.PortConfigPublishModeIngress,
    38  				},
    39  			},
    40  		},
    41  		{
    42  			value: "8080:80/tcp",
    43  			expected: []swarm.PortConfig{
    44  				{
    45  					Protocol:      "tcp",
    46  					TargetPort:    80,
    47  					PublishedPort: 8080,
    48  					PublishMode:   swarm.PortConfigPublishModeIngress,
    49  				},
    50  			},
    51  		},
    52  		{
    53  			value: "80:8080/udp",
    54  			expected: []swarm.PortConfig{
    55  				{
    56  					Protocol:      "udp",
    57  					TargetPort:    8080,
    58  					PublishedPort: 80,
    59  					PublishMode:   swarm.PortConfigPublishModeIngress,
    60  				},
    61  			},
    62  		},
    63  		{
    64  			value: "80-81:8080-8081/tcp",
    65  			expected: []swarm.PortConfig{
    66  				{
    67  					Protocol:      "tcp",
    68  					TargetPort:    8080,
    69  					PublishedPort: 80,
    70  					PublishMode:   swarm.PortConfigPublishModeIngress,
    71  				},
    72  				{
    73  					Protocol:      "tcp",
    74  					TargetPort:    8081,
    75  					PublishedPort: 81,
    76  					PublishMode:   swarm.PortConfigPublishModeIngress,
    77  				},
    78  			},
    79  		},
    80  		{
    81  			value: "80-82:8080-8082/udp",
    82  			expected: []swarm.PortConfig{
    83  				{
    84  					Protocol:      "udp",
    85  					TargetPort:    8080,
    86  					PublishedPort: 80,
    87  					PublishMode:   swarm.PortConfigPublishModeIngress,
    88  				},
    89  				{
    90  					Protocol:      "udp",
    91  					TargetPort:    8081,
    92  					PublishedPort: 81,
    93  					PublishMode:   swarm.PortConfigPublishModeIngress,
    94  				},
    95  				{
    96  					Protocol:      "udp",
    97  					TargetPort:    8082,
    98  					PublishedPort: 82,
    99  					PublishMode:   swarm.PortConfigPublishModeIngress,
   100  				},
   101  			},
   102  		},
   103  		{
   104  			value: "80-82:8080/udp",
   105  			expected: []swarm.PortConfig{
   106  				{
   107  					Protocol:      "udp",
   108  					TargetPort:    8080,
   109  					PublishedPort: 80,
   110  					PublishMode:   swarm.PortConfigPublishModeIngress,
   111  				},
   112  				{
   113  					Protocol:      "udp",
   114  					TargetPort:    8080,
   115  					PublishedPort: 81,
   116  					PublishMode:   swarm.PortConfigPublishModeIngress,
   117  				},
   118  				{
   119  					Protocol:      "udp",
   120  					TargetPort:    8080,
   121  					PublishedPort: 82,
   122  					PublishMode:   swarm.PortConfigPublishModeIngress,
   123  				},
   124  			},
   125  		},
   126  		{
   127  			value: "80-80:8080/tcp",
   128  			expected: []swarm.PortConfig{
   129  				{
   130  					Protocol:      "tcp",
   131  					TargetPort:    8080,
   132  					PublishedPort: 80,
   133  					PublishMode:   swarm.PortConfigPublishModeIngress,
   134  				},
   135  			},
   136  		},
   137  	}
   138  	for _, tc := range testCases {
   139  		var port PortOpt
   140  		assert.NilError(t, port.Set(tc.value))
   141  		assert.Check(t, is.Len(port.Value(), len(tc.expected)))
   142  		for _, expectedPortConfig := range tc.expected {
   143  			assertContains(t, port.Value(), expectedPortConfig)
   144  		}
   145  	}
   146  }
   147  
   148  func TestPortOptValidComplexSyntax(t *testing.T) {
   149  	testCases := []struct {
   150  		value    string
   151  		expected []swarm.PortConfig
   152  	}{
   153  		{
   154  			value: "target=80",
   155  			expected: []swarm.PortConfig{
   156  				{
   157  					TargetPort:  80,
   158  					Protocol:    "tcp",
   159  					PublishMode: swarm.PortConfigPublishModeIngress,
   160  				},
   161  			},
   162  		},
   163  		{
   164  			value: "target=80,protocol=tcp",
   165  			expected: []swarm.PortConfig{
   166  				{
   167  					Protocol:    "tcp",
   168  					TargetPort:  80,
   169  					PublishMode: swarm.PortConfigPublishModeIngress,
   170  				},
   171  			},
   172  		},
   173  		{
   174  			value: "target=80,published=8080,protocol=tcp",
   175  			expected: []swarm.PortConfig{
   176  				{
   177  					Protocol:      "tcp",
   178  					TargetPort:    80,
   179  					PublishedPort: 8080,
   180  					PublishMode:   swarm.PortConfigPublishModeIngress,
   181  				},
   182  			},
   183  		},
   184  		{
   185  			value: "published=80,target=8080,protocol=tcp",
   186  			expected: []swarm.PortConfig{
   187  				{
   188  					Protocol:      "tcp",
   189  					TargetPort:    8080,
   190  					PublishedPort: 80,
   191  					PublishMode:   swarm.PortConfigPublishModeIngress,
   192  				},
   193  			},
   194  		},
   195  		{
   196  			value: "target=80,published=8080,protocol=tcp,mode=host",
   197  			expected: []swarm.PortConfig{
   198  				{
   199  					Protocol:      "tcp",
   200  					TargetPort:    80,
   201  					PublishedPort: 8080,
   202  					PublishMode:   "host",
   203  				},
   204  			},
   205  		},
   206  		{
   207  			value: "target=80,published=8080,mode=host",
   208  			expected: []swarm.PortConfig{
   209  				{
   210  					TargetPort:    80,
   211  					PublishedPort: 8080,
   212  					PublishMode:   "host",
   213  					Protocol:      "tcp",
   214  				},
   215  			},
   216  		},
   217  		{
   218  			value: "target=80,published=8080,mode=ingress",
   219  			expected: []swarm.PortConfig{
   220  				{
   221  					TargetPort:    80,
   222  					PublishedPort: 8080,
   223  					PublishMode:   "ingress",
   224  					Protocol:      "tcp",
   225  				},
   226  			},
   227  		},
   228  	}
   229  	for _, tc := range testCases {
   230  		var port PortOpt
   231  		assert.NilError(t, port.Set(tc.value))
   232  		assert.Check(t, is.Len(port.Value(), len(tc.expected)))
   233  		for _, expectedPortConfig := range tc.expected {
   234  			assertContains(t, port.Value(), expectedPortConfig)
   235  		}
   236  	}
   237  }
   238  
   239  func TestPortOptInvalidComplexSyntax(t *testing.T) {
   240  	testCases := []struct {
   241  		value         string
   242  		expectedError string
   243  	}{
   244  		{
   245  			value:         "invalid,target=80",
   246  			expectedError: "invalid field",
   247  		},
   248  		{
   249  			value:         "invalid=field",
   250  			expectedError: "invalid field",
   251  		},
   252  		{
   253  			value:         "protocol=invalid",
   254  			expectedError: "invalid protocol value",
   255  		},
   256  		{
   257  			value:         "target=invalid",
   258  			expectedError: "invalid syntax",
   259  		},
   260  		{
   261  			value:         "published=invalid",
   262  			expectedError: "invalid syntax",
   263  		},
   264  		{
   265  			value:         "mode=invalid",
   266  			expectedError: "invalid publish mode value",
   267  		},
   268  		{
   269  			value:         "published=8080,protocol=tcp,mode=ingress",
   270  			expectedError: "missing mandatory field",
   271  		},
   272  		{
   273  			value:         `target=80,protocol="tcp,mode=ingress"`,
   274  			expectedError: "non-quoted-field",
   275  		},
   276  		{
   277  			value:         `target=80,"protocol=tcp,mode=ingress"`,
   278  			expectedError: "invalid protocol value",
   279  		},
   280  	}
   281  	for _, tc := range testCases {
   282  		var port PortOpt
   283  		assert.ErrorContains(t, port.Set(tc.value), tc.expectedError)
   284  	}
   285  }
   286  
   287  func TestPortOptInvalidSimpleSyntax(t *testing.T) {
   288  	testCases := []struct {
   289  		value         string
   290  		expectedError string
   291  	}{
   292  		{
   293  			value:         "9999999",
   294  			expectedError: "invalid containerPort: 9999999",
   295  		},
   296  		{
   297  			value:         "80/xyz",
   298  			expectedError: "invalid proto: xyz",
   299  		},
   300  		{
   301  			value:         "tcp",
   302  			expectedError: "invalid containerPort: tcp",
   303  		},
   304  		{
   305  			value:         "udp",
   306  			expectedError: "invalid containerPort: udp",
   307  		},
   308  		{
   309  			value:         "",
   310  			expectedError: "no port specified: <empty>",
   311  		},
   312  		{
   313  			value:         "1.1.1.1:80:80",
   314  			expectedError: "hostip is not supported",
   315  		},
   316  	}
   317  	for _, tc := range testCases {
   318  		var port PortOpt
   319  		assert.Error(t, port.Set(tc.value), tc.expectedError)
   320  	}
   321  }
   322  
   323  func TestConvertPortToPortConfigWithIP(t *testing.T) {
   324  	testCases := []struct {
   325  		value           string
   326  		expectedWarning string
   327  	}{
   328  		{
   329  			value: "0.0.0.0",
   330  		},
   331  		{
   332  			value: "::",
   333  		},
   334  		{
   335  			value:           "192.168.1.5",
   336  			expectedWarning: `ignoring IP-address (192.168.1.5:2345:80/tcp) service will listen on '0.0.0.0'`,
   337  		},
   338  		{
   339  			value:           "::2",
   340  			expectedWarning: `ignoring IP-address ([::2]:2345:80/tcp) service will listen on '0.0.0.0'`,
   341  		},
   342  	}
   343  
   344  	var b bytes.Buffer
   345  	logrus.SetOutput(&b)
   346  	for _, tc := range testCases {
   347  		tc := tc
   348  		t.Run(tc.value, func(t *testing.T) {
   349  			_, err := ConvertPortToPortConfig("80/tcp", map[nat.Port][]nat.PortBinding{
   350  				"80/tcp": {{HostIP: tc.value, HostPort: "2345"}},
   351  			})
   352  			assert.NilError(t, err)
   353  			if tc.expectedWarning == "" {
   354  				assert.Equal(t, b.String(), "")
   355  			} else {
   356  				assert.Assert(t, is.Contains(b.String(), tc.expectedWarning))
   357  			}
   358  		})
   359  	}
   360  	logrus.SetOutput(os.Stderr)
   361  }
   362  
   363  func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) {
   364  	t.Helper()
   365  	contains := false
   366  	for _, portConfig := range portConfigs {
   367  		if portConfig == expected {
   368  			contains = true
   369  			break
   370  		}
   371  	}
   372  	if !contains {
   373  		t.Errorf("expected %v to contain %v, did not", portConfigs, expected)
   374  	}
   375  }