@@ -2338,74 +2338,171 @@ func (task *Task) dockerLinks(container *apicontainer.Container, dockerContainer
2338
2338
}
2339
2339
2340
2340
var getHostPortRange = utils .GetHostPortRange
2341
+ var getHostPort = utils .GetHostPort
2341
2342
2343
+ // In buildPortMapWithSCIngressConfig, the dockerPortMap and the containerPortSet will be constructed
2344
+ // for ingress listeners under two service connect bridge mode cases:
2345
+ // (1) non-default bridge mode service connect experience: customers specify host ports for listeners in the ingress config.
2346
+ // (2) default bridge mode service connect experience: customers do not specify host ports for listeners in the ingress config.
2347
+ //
2348
+ // Instead, ECS Agent finds host ports within the given dynamic host port range. An error will be returned for case (2) if
2349
+ // ECS Agent cannot find an available host port within range.
2350
+ func (task * Task ) buildPortMapWithSCIngressConfig (dynamicHostPortRange string ) (nat.PortMap , error ) {
2351
+ var err error
2352
+ ingressDockerPortMap := nat.PortMap {}
2353
+ ingressContainerPortSet := make (map [int ]struct {})
2354
+ protocolStr := "tcp"
2355
+ scContainer := task .GetServiceConnectContainer ()
2356
+ for _ , ic := range task .ServiceConnectConfig .IngressConfig {
2357
+ listenerPortInt := int (ic .ListenerPort )
2358
+ dockerPort := nat .Port (strconv .Itoa (listenerPortInt ) + "/" + protocolStr )
2359
+ hostPortStr := ""
2360
+ if ic .HostPort != nil {
2361
+ // For non-default bridge mode service connect experience, a host port is specified by customers
2362
+ // Note that service connect ingress config has been validated in service_connect_validator.go,
2363
+ // where host ports will be validated to ensure user-definied ports are within a valid port range (1 to 65535)
2364
+ // and do not have port collisions.
2365
+ hostPortStr = strconv .Itoa (int (* ic .HostPort ))
2366
+ } else {
2367
+ // For default bridge mode service connect experience, customers do not specify a host port
2368
+ // thus the host port will be assigned by ECS Agent.
2369
+ // ECS Agent will find an available host port within the given dynamic host port range,
2370
+ // or return an error if no host port is available within the range.
2371
+ hostPortStr , err = getHostPort (protocolStr , dynamicHostPortRange )
2372
+ if err != nil {
2373
+ return nil , err
2374
+ }
2375
+ }
2376
+
2377
+ ingressDockerPortMap [dockerPort ] = append (ingressDockerPortMap [dockerPort ], nat.PortBinding {HostPort : hostPortStr })
2378
+ // Append non-range, singular container port to the ingressContainerPortSet
2379
+ ingressContainerPortSet [listenerPortInt ] = struct {}{}
2380
+ // Set taskContainer.ContainerPortSet to be used during network binding creation
2381
+ scContainer .SetContainerPortSet (ingressContainerPortSet )
2382
+ }
2383
+ return ingressDockerPortMap , err
2384
+ }
2385
+
2386
+ // dockerPortMap creates a port binding map for
2387
+ // (1) Ingress listeners for the service connect AppNet container in the service connect bridge network mode task.
2388
+ // (2) Port mapping configured by customers in the task definition.
2389
+ //
2390
+ // For service connect bridge mode task, we will create port bindings for customers' application containers
2391
+ // and service connect AppNet container, and let them to be published by the associated pause containers.
2392
+ // (a) For default bridge service connect experience, ECS Agent will assign a host port within the
2393
+ // default/user-specified dynamic host port range for the ingress listener. If no available host port can be
2394
+ // found by ECS Agent, an error will be returned.
2395
+ // (b) For non-default bridge service connect experience, ECS Agent will use the user-defined host port for the ingress listener.
2396
+ //
2397
+ // For non-service connect bridge network mode task, ECS Agent will assign a host port or a host port range
2398
+ // within the default/user-specified dynamic host port range. If no available host port or host port range can be
2399
+ // found by ECS Agent, an error will be returned.
2400
+ //
2401
+ // Note that
2402
+ // (a) ECS Agent will not assign a new host port within the dynamic host port range for awsvpc network mode task
2403
+ // (b) ECS Agent will not assign a new host port within the dynamic host port range if the user-specified host port exists
2342
2404
func (task * Task ) dockerPortMap (container * apicontainer.Container , dynamicHostPortRange string ) (nat.PortMap , error ) {
2405
+ hostPortStr := ""
2343
2406
dockerPortMap := nat.PortMap {}
2344
- scContainer := task .GetServiceConnectContainer ()
2345
2407
containerToCheck := container
2346
2408
containerPortSet := make (map [int ]struct {})
2347
2409
containerPortRangeMap := make (map [string ]string )
2410
+
2411
+ // For service connect bridge network mode task, we will create port bindings for task containers,
2412
+ // including both application containers and service connect AppNet container, and let them to be published
2413
+ // by the associated pause containers.
2348
2414
if task .IsServiceConnectEnabled () && task .IsNetworkModeBridge () {
2349
2415
if container .Type == apicontainer .ContainerCNIPause {
2350
- // we will create bindings for task containers (including both customer containers and SC Appnet container)
2351
- // and let them be published by the associated pause container.
2352
- // Note - for SC bridge mode we do not allow customer to specify a host port for their containers. Additionally,
2353
- // When an ephemeral host port is assigned, Appnet will NOT proxy traffic to that port
2416
+ // Find the task container associated with this particular pause container
2354
2417
taskContainer , err := task .getBridgeModeTaskContainerForPauseContainer (container )
2355
2418
if err != nil {
2356
2419
return nil , err
2357
2420
}
2421
+
2422
+ scContainer := task .GetServiceConnectContainer ()
2358
2423
if taskContainer == scContainer {
2359
- // create bindings for all ingress listener ports
2360
- // no need to create binding for egress listener port as it won't be access from host level or from outside
2361
- for _ , ic := range task .ServiceConnectConfig .IngressConfig {
2362
- listenerPortInt := int (ic .ListenerPort )
2363
- dockerPort := nat .Port (strconv .Itoa (listenerPortInt )) + "/tcp"
2364
- hostPort := 0 // default bridge-mode SC experience - host port will be an ephemeral port assigned by docker
2365
- if ic .HostPort != nil { // non-default bridge-mode SC experience - host port specified by customer
2366
- hostPort = int (* ic .HostPort )
2367
- }
2368
- dockerPortMap [dockerPort ] = append (dockerPortMap [dockerPort ], nat.PortBinding {HostPort : strconv .Itoa (hostPort )})
2369
- // append non-range, singular container port to the containerPortSet
2370
- containerPortSet [listenerPortInt ] = struct {}{}
2371
- // set taskContainer.ContainerPortSet to be used during network binding creation
2372
- taskContainer .SetContainerPortSet (containerPortSet )
2424
+ // If the associated task container to this pause container is the service connect AppNet container,
2425
+ // create port binding(s) for ingress listener ports based on its ingress config.
2426
+ // Note that there is no need to do this for egress listener ports as they won't be accessed
2427
+ // from host level or from outside.
2428
+ dockerPortMap , err := task .buildPortMapWithSCIngressConfig (dynamicHostPortRange )
2429
+ if err != nil {
2430
+ logger .Error ("Failed to build a port map with service connect ingress config" , logger.Fields {
2431
+ field .TaskID : task .GetID (),
2432
+ field .Container : taskContainer .Name ,
2433
+ "dynamicHostPortRange" : dynamicHostPortRange ,
2434
+ field .Error : err ,
2435
+ })
2436
+ return nil , err
2373
2437
}
2374
2438
return dockerPortMap , nil
2375
2439
}
2440
+ // If the associated task container to this pause container is NOT the service connect AppNet container,
2441
+ // we will continue to update the dockerPortMap for the pause container using the port bindings
2442
+ // configured for the application container since port bindings will be published by the pasue container.
2376
2443
containerToCheck = taskContainer
2377
2444
} else {
2378
- // If container is neither SC container nor pause container, it's a regular task container. Its port bindings(s)
2379
- // are published by the associated pause container, and we leave the map empty here (docker would actually complain
2380
- // otherwise) .
2445
+ // If the container is not a pause container, then it is a regular customers' application container
2446
+ // or a service connect AppNet container. We will leave the map empty and return it as its port bindings(s)
2447
+ // are published by the associated pause container .
2381
2448
return dockerPortMap , nil
2382
2449
}
2383
2450
}
2384
2451
2452
+ // For each port binding config, either one of containerPort or containerPortRange is set.
2453
+ // (1) containerPort is the port number on the container that's bound to the user-specified host port or the
2454
+ // host port assigned by ECS Agent.
2455
+ // (2) containerPortRange is the port number range on the container that's bound to the mapped host port range
2456
+ // found by ECS Agent.
2457
+ var err error
2385
2458
for _ , portBinding := range containerToCheck .Ports {
2386
- // for each port binding config, either one of containerPort or containerPortRange is set
2387
2459
if portBinding .ContainerPort != 0 {
2388
2460
containerPort := int (portBinding .ContainerPort )
2461
+ protocolStr := portBinding .Protocol .String ()
2462
+ dockerPort := nat .Port (strconv .Itoa (containerPort ) + "/" + protocolStr )
2463
+
2464
+ if portBinding .HostPort != 0 {
2465
+ // An user-specified host port exists.
2466
+ // Note that the host port value has been validated by ECS front end service;
2467
+ // thus only an valid host port value will be streamed down to ECS Agent.
2468
+ hostPortStr = strconv .Itoa (int (portBinding .HostPort ))
2469
+ } else {
2470
+ // If there is no user-specified host port, ECS Agent will find an available host port
2471
+ // within the given dynamic host port range. And if no host port is available within the range,
2472
+ // an error will be returned.
2473
+ logger .Debug ("No user-specified host port, ECS Agent will find an available host port within the given dynamic host port range" , logger.Fields {
2474
+ field .Container : containerToCheck .Name ,
2475
+ "dynamicHostPortRange" : dynamicHostPortRange ,
2476
+ })
2477
+ hostPortStr , err = getHostPort (protocolStr , dynamicHostPortRange )
2478
+ if err != nil {
2479
+ logger .Error ("Unable to find a host port for container within the given dynamic host port range" , logger.Fields {
2480
+ field .TaskID : task .GetID (),
2481
+ field .Container : container .Name ,
2482
+ "dynamicHostPortRange" : dynamicHostPortRange ,
2483
+ field .Error : err ,
2484
+ })
2485
+ return nil , err
2486
+ }
2487
+ }
2488
+ dockerPortMap [dockerPort ] = append (dockerPortMap [dockerPort ], nat.PortBinding {HostPort : hostPortStr })
2389
2489
2390
- dockerPort := nat .Port (strconv .Itoa (containerPort ) + "/" + portBinding .Protocol .String ())
2391
- dockerPortMap [dockerPort ] = append (dockerPortMap [dockerPort ], nat.PortBinding {HostPort : strconv .Itoa (int (portBinding .HostPort ))})
2392
-
2393
- // append non-range, singular container port to the containerPortSet
2490
+ // For the containerPort case, append a non-range, singular container port to the containerPortSet.
2394
2491
containerPortSet [containerPort ] = struct {}{}
2395
2492
} else if portBinding .ContainerPortRange != "" {
2396
2493
containerToCheck .SetContainerHasPortRange (true )
2397
2494
2398
2495
containerPortRange := portBinding .ContainerPortRange
2399
- // nat.ParsePortRangeToInt validates a port range; if valid, it returns start and end ports as integers
2496
+ // nat.ParsePortRangeToInt validates a port range; if valid, it returns start and end ports as integers.
2400
2497
startContainerPort , endContainerPort , err := nat .ParsePortRangeToInt (containerPortRange )
2401
2498
if err != nil {
2402
2499
return nil , err
2403
2500
}
2404
2501
2405
2502
numberOfPorts := endContainerPort - startContainerPort + 1
2406
2503
protocol := portBinding .Protocol .String ()
2407
- // we will try to get a contiguous set of host ports from the ephemeral host port range.
2408
- // this is to ensure that docker maps host ports in a contiguous manner, and
2504
+ // We will try to get a contiguous set of host ports from the ephemeral host port range.
2505
+ // This is to ensure that docker maps host ports in a contiguous manner, and
2409
2506
// we are guaranteed to have the entire hostPortRange in a single network binding while sending this info to ECS;
2410
2507
// therefore, an error will be returned if we cannot find a contiguous set of host ports.
2411
2508
hostPortRange , err := getHostPortRange (numberOfPorts , protocol , dynamicHostPortRange )
@@ -2419,8 +2516,8 @@ func (task *Task) dockerPortMap(container *apicontainer.Container, dynamicHostPo
2419
2516
return nil , err
2420
2517
}
2421
2518
2422
- // append ranges to the dockerPortMap
2423
- // nat.ParsePortSpec returns a list of port mappings in a format that docker likes
2519
+ // For the ContainerPortRange case, append ranges to the dockerPortMap.
2520
+ // nat.ParsePortSpec returns a list of port mappings in a format that Docker likes.
2424
2521
mappings , err := nat .ParsePortSpec (hostPortRange + ":" + containerPortRange + "/" + protocol )
2425
2522
if err != nil {
2426
2523
return nil , err
@@ -2430,13 +2527,13 @@ func (task *Task) dockerPortMap(container *apicontainer.Container, dynamicHostPo
2430
2527
dockerPortMap [mapping .Port ] = append (dockerPortMap [mapping .Port ], mapping .Binding )
2431
2528
}
2432
2529
2433
- // append containerPortRange and associated hostPortRange to the containerPortRangeMap
2434
- // this will ensure that we consolidate range into 1 network binding while sending it to ECS
2530
+ // For the ContainerPortRange case, append containerPortRange and associated hostPortRange to the containerPortRangeMap.
2531
+ // This will ensure that we consolidate range into 1 network binding while sending it to ECS.
2435
2532
containerPortRangeMap [containerPortRange ] = hostPortRange
2436
2533
}
2437
2534
}
2438
2535
2439
- // set Container.ContainerPortSet and Container.ContainerPortRangeMap to be used during network binding creation
2536
+ // Set Container.ContainerPortSet and Container.ContainerPortRangeMap to be used during network binding creation.
2440
2537
containerToCheck .SetContainerPortSet (containerPortSet )
2441
2538
containerToCheck .SetContainerPortRangeMap (containerPortRangeMap )
2442
2539
return dockerPortMap , nil
0 commit comments