github.com/docker/compose-on-kubernetes@v0.5.0/internal/convert/service_test.go (about) 1 package convert 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/docker/compose-on-kubernetes/api/compose/latest" 9 "github.com/docker/compose-on-kubernetes/internal/stackresources" 10 . "github.com/docker/compose-on-kubernetes/internal/test/builders" 11 "github.com/stretchr/testify/assert" 12 apiv1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/util/intstr" 15 ) 16 17 func services(t *testing.T, stack *latest.Stack, strategy ServiceStrategy) (*apiv1.Service, *apiv1.Service, *apiv1.Service) { 18 s, err := StackToStack(*stack, strategy, stackresources.EmptyStackState) 19 assert.NoError(t, err) 20 var ( 21 headless *apiv1.Service 22 published *apiv1.Service 23 randomPorts *apiv1.Service 24 ) 25 for k, v := range s.Services { 26 local := v 27 if strings.HasSuffix(k, publishedServiceSuffix) { 28 published = &local 29 } else if strings.HasSuffix(k, publishedOnRandomPortSuffix) { 30 randomPorts = &local 31 } else { 32 headless = &local 33 } 34 } 35 return headless, published, randomPorts 36 } 37 38 func TestToServiceWithPublishedPort(t *testing.T) { 39 headless, published, randomPorts := services(t, Stack("demo", 40 WithService("nginx", 41 Image("any"), 42 WithPort(8080, Published(80)), 43 WithLabel("container.key", "container.value"), 44 Deploy(WithDeployLabel("deploy.key", "deploy.value")), 45 ), 46 ), loadBalancerServiceStrategy{}) 47 48 expectedHeadless := &apiv1.Service{ 49 ObjectMeta: metav1.ObjectMeta{ 50 Name: "nginx", 51 Labels: map[string]string{ 52 "com.docker.stack.namespace": "demo", 53 "com.docker.service.name": "nginx", 54 "com.docker.service.id": "demo-nginx", 55 "deploy.key": "deploy.value", 56 }, 57 }, 58 Spec: apiv1.ServiceSpec{ 59 ClusterIP: apiv1.ClusterIPNone, 60 Ports: []apiv1.ServicePort{ 61 { 62 Name: headlessPortName, 63 Port: headlessPort, 64 Protocol: apiv1.ProtocolTCP, 65 TargetPort: intstr.FromInt(headlessPort), 66 }, 67 }, 68 Selector: map[string]string{ 69 "com.docker.stack.namespace": "demo", 70 "com.docker.service.name": "nginx", 71 "com.docker.service.id": "demo-nginx", 72 }, 73 }, 74 } 75 assert.Equal(t, headless, expectedHeadless) 76 77 expectedPublished := &apiv1.Service{ 78 ObjectMeta: metav1.ObjectMeta{ 79 Name: fmt.Sprintf("nginx%s", publishedServiceSuffix), 80 Labels: map[string]string{ 81 "com.docker.stack.namespace": "demo", 82 "com.docker.service.name": "nginx", 83 "com.docker.service.id": "demo-nginx", 84 "deploy.key": "deploy.value", 85 }, 86 }, 87 Spec: apiv1.ServiceSpec{ 88 Type: apiv1.ServiceTypeLoadBalancer, 89 Ports: []apiv1.ServicePort{ 90 { 91 Name: "80-tcp", 92 Port: 80, 93 Protocol: apiv1.ProtocolTCP, 94 TargetPort: intstr.FromInt(8080), 95 }, 96 }, 97 Selector: map[string]string{ 98 "com.docker.stack.namespace": "demo", 99 "com.docker.service.name": "nginx", 100 "com.docker.service.id": "demo-nginx", 101 }, 102 }, 103 } 104 assert.Equal(t, published, expectedPublished) 105 assert.Nil(t, randomPorts) 106 } 107 108 func TestToServiceWithLongPort(t *testing.T) { 109 headless, published, randomPorts := services(t, Stack("demo", 110 WithService("nginx", 111 Image("any"), 112 WithPort(8888, Published(80), ProtocolUDP), 113 ), 114 ), loadBalancerServiceStrategy{}) 115 116 assert.NotNil(t, headless) 117 118 expectedPorts := []apiv1.ServicePort{ 119 { 120 Name: "80-udp", 121 Port: 80, 122 TargetPort: intstr.FromInt(8888), 123 Protocol: apiv1.ProtocolUDP, 124 }, 125 } 126 assert.Equal(t, published.Spec.Ports, expectedPorts) 127 assert.Nil(t, randomPorts) 128 } 129 130 func TestToServiceWithNonPublishedPort(t *testing.T) { 131 headless, published, randomPorts := services(t, Stack("demo", 132 WithService("nginx", 133 Image("any"), 134 ), 135 ), loadBalancerServiceStrategy{}) 136 137 expectedHeadless := &apiv1.Service{ 138 ObjectMeta: metav1.ObjectMeta{ 139 Name: "nginx", 140 Labels: map[string]string{ 141 "com.docker.stack.namespace": "demo", 142 "com.docker.service.name": "nginx", 143 "com.docker.service.id": "demo-nginx", 144 }, 145 }, 146 Spec: apiv1.ServiceSpec{ 147 ClusterIP: apiv1.ClusterIPNone, 148 Ports: []apiv1.ServicePort{ 149 { 150 Name: headlessPortName, 151 Port: headlessPort, 152 TargetPort: intstr.FromInt(headlessPort), 153 Protocol: apiv1.ProtocolTCP, 154 }, 155 }, 156 Selector: map[string]string{ 157 "com.docker.stack.namespace": "demo", 158 "com.docker.service.name": "nginx", 159 "com.docker.service.id": "demo-nginx", 160 }, 161 }, 162 } 163 assert.Equal(t, headless, expectedHeadless) 164 165 assert.Nil(t, published) 166 assert.Nil(t, randomPorts) 167 } 168 169 func TestToServiceWithRandomPublishedPort(t *testing.T) { 170 headless, published, randomPorts := services(t, Stack("demo", 171 WithService("nginx", 172 Image("any"), 173 WithPort(8888), 174 ), 175 ), loadBalancerServiceStrategy{}) 176 expectedHeadless := &apiv1.Service{ 177 ObjectMeta: metav1.ObjectMeta{ 178 Name: "nginx", 179 Labels: map[string]string{ 180 "com.docker.stack.namespace": "demo", 181 "com.docker.service.name": "nginx", 182 "com.docker.service.id": "demo-nginx", 183 }, 184 }, 185 Spec: apiv1.ServiceSpec{ 186 ClusterIP: apiv1.ClusterIPNone, 187 Ports: []apiv1.ServicePort{ 188 { 189 Name: headlessPortName, 190 Port: headlessPort, 191 TargetPort: intstr.FromInt(headlessPort), 192 Protocol: apiv1.ProtocolTCP, 193 }, 194 }, 195 Selector: map[string]string{ 196 "com.docker.stack.namespace": "demo", 197 "com.docker.service.name": "nginx", 198 "com.docker.service.id": "demo-nginx", 199 }, 200 }, 201 } 202 expectedRandomPorts := &apiv1.Service{ 203 ObjectMeta: metav1.ObjectMeta{ 204 Name: fmt.Sprintf("nginx%s", publishedOnRandomPortSuffix), 205 Labels: map[string]string{ 206 "com.docker.stack.namespace": "demo", 207 "com.docker.service.name": "nginx", 208 "com.docker.service.id": "demo-nginx", 209 }, 210 }, 211 Spec: apiv1.ServiceSpec{ 212 Type: apiv1.ServiceTypeNodePort, 213 Ports: []apiv1.ServicePort{ 214 { 215 Name: "8888-tcp", 216 Port: 8888, 217 Protocol: apiv1.ProtocolTCP, 218 TargetPort: intstr.FromInt(8888), 219 }, 220 }, 221 Selector: map[string]string{ 222 "com.docker.stack.namespace": "demo", 223 "com.docker.service.name": "nginx", 224 "com.docker.service.id": "demo-nginx", 225 }, 226 }, 227 } 228 assert.Equal(t, headless, expectedHeadless) 229 assert.Nil(t, published) 230 assert.Equal(t, randomPorts, expectedRandomPorts) 231 } 232 233 func TestToServiceWithoutStack(t *testing.T) { 234 headless, published, randomPorts := services(t, Stack("demo", 235 WithService("nginx", 236 Image("any"), 237 WithPort(8080, Published(8080)), 238 ), 239 ), loadBalancerServiceStrategy{}) 240 241 assert.NotNil(t, headless) 242 243 expectedPublished := &apiv1.Service{ 244 ObjectMeta: metav1.ObjectMeta{ 245 Name: fmt.Sprintf("nginx%s", publishedServiceSuffix), 246 Labels: map[string]string{ 247 "com.docker.stack.namespace": "demo", 248 "com.docker.service.name": "nginx", 249 "com.docker.service.id": "demo-nginx", 250 }, 251 }, 252 Spec: apiv1.ServiceSpec{ 253 Type: apiv1.ServiceTypeLoadBalancer, 254 Ports: []apiv1.ServicePort{ 255 { 256 Name: "8080-tcp", 257 Port: 8080, 258 Protocol: apiv1.ProtocolTCP, 259 TargetPort: intstr.FromInt(8080), 260 }, 261 }, 262 Selector: map[string]string{ 263 "com.docker.stack.namespace": "demo", 264 "com.docker.service.name": "nginx", 265 "com.docker.service.id": "demo-nginx", 266 }, 267 }, 268 } 269 assert.Equal(t, published, expectedPublished) 270 assert.Nil(t, randomPorts) 271 } 272 273 func TestToServiceWithPublishedPortWithNodePorts(t *testing.T) { 274 headless, published, randomPorts := services(t, Stack("demo", 275 WithService("nginx", 276 Image("any"), 277 WithPort(8080, Published(80)), 278 WithLabel("container.key", "container.value"), 279 Deploy(WithDeployLabel("deploy.key", "deploy.value")), 280 ), 281 ), nodePortServiceStrategy{}) 282 283 expectedHeadless := &apiv1.Service{ 284 ObjectMeta: metav1.ObjectMeta{ 285 Name: "nginx", 286 Labels: map[string]string{ 287 "com.docker.stack.namespace": "demo", 288 "com.docker.service.name": "nginx", 289 "com.docker.service.id": "demo-nginx", 290 "deploy.key": "deploy.value", 291 }, 292 }, 293 Spec: apiv1.ServiceSpec{ 294 ClusterIP: apiv1.ClusterIPNone, 295 Ports: []apiv1.ServicePort{ 296 { 297 Name: headlessPortName, 298 Port: headlessPort, 299 Protocol: apiv1.ProtocolTCP, 300 TargetPort: intstr.FromInt(headlessPort), 301 }, 302 }, 303 Selector: map[string]string{ 304 "com.docker.stack.namespace": "demo", 305 "com.docker.service.name": "nginx", 306 "com.docker.service.id": "demo-nginx", 307 }, 308 }, 309 } 310 assert.Equal(t, headless, expectedHeadless) 311 312 expectedPublished := &apiv1.Service{ 313 ObjectMeta: metav1.ObjectMeta{ 314 Name: fmt.Sprintf("nginx%s", publishedServiceSuffix), 315 Labels: map[string]string{ 316 "com.docker.stack.namespace": "demo", 317 "com.docker.service.name": "nginx", 318 "com.docker.service.id": "demo-nginx", 319 "deploy.key": "deploy.value", 320 }, 321 }, 322 Spec: apiv1.ServiceSpec{ 323 Type: apiv1.ServiceTypeNodePort, 324 Ports: []apiv1.ServicePort{ 325 { 326 Name: "80-tcp", 327 NodePort: 80, 328 Port: 8080, 329 Protocol: apiv1.ProtocolTCP, 330 TargetPort: intstr.FromInt(8080), 331 }, 332 }, 333 Selector: map[string]string{ 334 "com.docker.stack.namespace": "demo", 335 "com.docker.service.name": "nginx", 336 "com.docker.service.id": "demo-nginx", 337 }, 338 }, 339 } 340 assert.Equal(t, published, expectedPublished) 341 assert.Nil(t, randomPorts) 342 } 343 344 func TestToServiceWithLongPortWithNodePorts(t *testing.T) { 345 headless, published, randomPorts := services(t, Stack("demo", 346 WithService("nginx", 347 Image("any"), 348 WithPort(8888, Published(80), ProtocolUDP), 349 ), 350 ), nodePortServiceStrategy{}) 351 352 assert.NotNil(t, headless) 353 354 expectedPorts := []apiv1.ServicePort{ 355 { 356 Name: "80-udp", 357 NodePort: 80, 358 Port: 8888, 359 TargetPort: intstr.FromInt(8888), 360 Protocol: apiv1.ProtocolUDP, 361 }, 362 } 363 assert.Equal(t, published.Spec.Ports, expectedPorts) 364 assert.Nil(t, randomPorts) 365 } 366 367 func TestToServiceWithNonPublishedPortWithNodePorts(t *testing.T) { 368 headless, published, randomPorts := services(t, Stack("demo", 369 WithService("nginx", 370 Image("any"), 371 ), 372 ), nodePortServiceStrategy{}) 373 374 expectedHeadless := &apiv1.Service{ 375 ObjectMeta: metav1.ObjectMeta{ 376 Name: "nginx", 377 Labels: map[string]string{ 378 "com.docker.stack.namespace": "demo", 379 "com.docker.service.name": "nginx", 380 "com.docker.service.id": "demo-nginx", 381 }, 382 }, 383 Spec: apiv1.ServiceSpec{ 384 ClusterIP: apiv1.ClusterIPNone, 385 Ports: []apiv1.ServicePort{ 386 { 387 Name: headlessPortName, 388 Port: headlessPort, 389 TargetPort: intstr.FromInt(headlessPort), 390 Protocol: apiv1.ProtocolTCP, 391 }, 392 }, 393 Selector: map[string]string{ 394 "com.docker.stack.namespace": "demo", 395 "com.docker.service.name": "nginx", 396 "com.docker.service.id": "demo-nginx", 397 }, 398 }, 399 } 400 assert.Equal(t, headless, expectedHeadless) 401 402 assert.Nil(t, published) 403 assert.Nil(t, randomPorts) 404 } 405 406 func TestToServiceWithRandomPublishedPortWithNodePorts(t *testing.T) { 407 headless, published, randomPorts := services(t, Stack("demo", 408 WithService("nginx", 409 Image("any"), 410 WithPort(8888), 411 ), 412 ), nodePortServiceStrategy{}) 413 expectedHeadless := &apiv1.Service{ 414 ObjectMeta: metav1.ObjectMeta{ 415 Name: "nginx", 416 Labels: map[string]string{ 417 "com.docker.stack.namespace": "demo", 418 "com.docker.service.name": "nginx", 419 "com.docker.service.id": "demo-nginx", 420 }, 421 }, 422 Spec: apiv1.ServiceSpec{ 423 ClusterIP: apiv1.ClusterIPNone, 424 Ports: []apiv1.ServicePort{ 425 { 426 Name: headlessPortName, 427 Port: headlessPort, 428 TargetPort: intstr.FromInt(headlessPort), 429 Protocol: apiv1.ProtocolTCP, 430 }, 431 }, 432 Selector: map[string]string{ 433 "com.docker.stack.namespace": "demo", 434 "com.docker.service.name": "nginx", 435 "com.docker.service.id": "demo-nginx", 436 }, 437 }, 438 } 439 expectedRandomPorts := &apiv1.Service{ 440 ObjectMeta: metav1.ObjectMeta{ 441 Name: fmt.Sprintf("nginx%s", publishedOnRandomPortSuffix), 442 Labels: map[string]string{ 443 "com.docker.stack.namespace": "demo", 444 "com.docker.service.name": "nginx", 445 "com.docker.service.id": "demo-nginx", 446 }, 447 }, 448 Spec: apiv1.ServiceSpec{ 449 Type: apiv1.ServiceTypeNodePort, 450 Ports: []apiv1.ServicePort{ 451 { 452 Name: "8888-tcp", 453 Protocol: apiv1.ProtocolTCP, 454 Port: 8888, 455 TargetPort: intstr.FromInt(8888), 456 }, 457 }, 458 Selector: map[string]string{ 459 "com.docker.stack.namespace": "demo", 460 "com.docker.service.name": "nginx", 461 "com.docker.service.id": "demo-nginx", 462 }, 463 }, 464 } 465 assert.Equal(t, headless, expectedHeadless) 466 assert.Nil(t, published) 467 assert.Equal(t, randomPorts, expectedRandomPorts) 468 } 469 470 func TestToServiceWithoutStackWithNodePorts(t *testing.T) { 471 headless, published, randomPorts := services(t, Stack("demo", 472 WithService("nginx", 473 Image("any"), 474 WithPort(8080, Published(8080)), 475 ), 476 ), nodePortServiceStrategy{}) 477 478 assert.NotNil(t, headless) 479 480 expectedPublished := &apiv1.Service{ 481 ObjectMeta: metav1.ObjectMeta{ 482 Name: fmt.Sprintf("nginx%s", publishedServiceSuffix), 483 Labels: map[string]string{ 484 "com.docker.stack.namespace": "demo", 485 "com.docker.service.name": "nginx", 486 "com.docker.service.id": "demo-nginx", 487 }, 488 }, 489 Spec: apiv1.ServiceSpec{ 490 Type: apiv1.ServiceTypeNodePort, 491 Ports: []apiv1.ServicePort{ 492 { 493 Name: "8080-tcp", 494 NodePort: 8080, 495 Port: 8080, 496 Protocol: apiv1.ProtocolTCP, 497 TargetPort: intstr.FromInt(8080), 498 }, 499 }, 500 Selector: map[string]string{ 501 "com.docker.stack.namespace": "demo", 502 "com.docker.service.name": "nginx", 503 "com.docker.service.id": "demo-nginx", 504 }, 505 }, 506 } 507 assert.Equal(t, published, expectedPublished) 508 assert.Nil(t, randomPorts) 509 } 510 511 func TestServiceStrategyFor(t *testing.T) { 512 cases := []apiv1.ServiceType{ 513 apiv1.ServiceTypeLoadBalancer, 514 apiv1.ServiceTypeNodePort, 515 } 516 for _, c := range cases { 517 s, err := ServiceStrategyFor(c) 518 assert.NoError(t, err) 519 assert.Equal(t, c, s.publishedServiceType()) 520 } 521 } 522 523 func TestInternalServiceStrategy(t *testing.T) { 524 cases := []struct { 525 name string 526 desiredType latest.InternalServiceType 527 ports []latest.InternalPort 528 expectedClusterIP string 529 original apiv1.Service 530 }{ 531 { 532 name: "auto-no-ports", 533 expectedClusterIP: apiv1.ClusterIPNone, 534 }, 535 { 536 name: "auto-some-ports", 537 ports: []latest.InternalPort{ 538 { 539 Port: 8080, 540 Protocol: apiv1.ProtocolUDP, 541 }, 542 }, 543 }, 544 { 545 name: "headless-some-ports", 546 ports: []latest.InternalPort{ 547 { 548 Port: 8080, 549 Protocol: apiv1.ProtocolUDP, 550 }, 551 }, 552 desiredType: latest.InternalServiceTypeHeadless, 553 expectedClusterIP: apiv1.ClusterIPNone, 554 }, 555 { 556 name: "clusterip-no-ports", 557 desiredType: latest.InternalServiceTypeClusterIP, 558 }, 559 560 { 561 name: "auto-no-ports-existing-with-ip", 562 expectedClusterIP: apiv1.ClusterIPNone, 563 original: apiv1.Service{ 564 Spec: apiv1.ServiceSpec{ 565 ClusterIP: "1.1.1.1", 566 }, 567 }, 568 }, 569 { 570 name: "auto-some-ports-with-ip", 571 ports: []latest.InternalPort{ 572 { 573 Port: 8080, 574 Protocol: apiv1.ProtocolUDP, 575 }, 576 }, 577 original: apiv1.Service{ 578 Spec: apiv1.ServiceSpec{ 579 ClusterIP: "1.1.1.1", 580 }, 581 }, 582 expectedClusterIP: "1.1.1.1", 583 }, 584 { 585 name: "auto-some-ports-no-ip", 586 ports: []latest.InternalPort{ 587 { 588 Port: 8080, 589 Protocol: apiv1.ProtocolUDP, 590 }, 591 }, 592 original: apiv1.Service{ 593 Spec: apiv1.ServiceSpec{ 594 ClusterIP: apiv1.ClusterIPNone, 595 }, 596 }, 597 expectedClusterIP: "", 598 }, 599 } 600 601 for _, c := range cases { 602 t.Run(c.name, func(t *testing.T) { 603 svc := toInternalService(metav1.ObjectMeta{}, nil, c.original, c.ports, c.desiredType) 604 assert.Equal(t, c.expectedClusterIP, svc.Spec.ClusterIP) 605 }) 606 } 607 }