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 }