-
Notifications
You must be signed in to change notification settings - Fork 611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DHPA] Upudate dockerPortMap() in task.go with dynamic host port range support - part 2+ #3589
[DHPA] Upudate dockerPortMap() in task.go with dynamic host port range support - part 2+ #3589
Conversation
// | ||
// Instead, ECS Agent finds host ports within the given dynamic host port range. An error will be returned for case (2) if | ||
// ECS Agent cannot find an available host port within range. | ||
func (task *Task) buildPortMapWithSCIngressConfig(dynamicHostPortRange string) (nat.PortMap, map[int]struct{}, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function buildPortMapWithSCIngressConfig()
has been reviewed in #3585.
// and do not have port collisions. | ||
hostPortStr = strconv.Itoa(int(*ic.HostPort)) | ||
} else { | ||
// For default bridge mode service connect experience, customers do not specify a host port |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments from line 2366-2369 are newly added in this PR.
// | ||
// For non-service connect bridge network mode task, ECS Agent will assign a host port or a host port range | ||
// within the default/user-specified dynamic host port range. If no available host port or host port range can be | ||
// found by ECS Agent, an error will be returned. | ||
// | ||
// Note that, | ||
// Note that | ||
// (a) ECS Agent will not assign a new host port within the dynamic host port range for awsvpc network mode task | ||
// (b) ECS Agent will not assign a new host port within the dynamic host port range if the user-specified host port exists | ||
func (task *Task) dockerPortMap(container *apicontainer.Container, dynamicHostPortRange string) (nat.PortMap, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function dockerPortMap()
has been reviewed in #3585.
// If container is neither SC container nor pause container, it's a regular task container. Its port bindings(s) | ||
// are published by the associated pause container, and we leave the map empty here (docker would actually complain | ||
// otherwise). | ||
// If the container is not a pause container, then it is a regular customers' application container |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments from line 2444-2446 are newly updated in this PR.
The original comment shown as follows is not valid; thus it is removed in this PR.
// Note - for service connect bridge mode, we do not allow customers to specify a host port for their application containers.
// Additionally, AppNet will NOT proxy traffic to that port when an ephemeral host port is assigned.
@@ -376,17 +376,6 @@ func TestDockerHostConfigPortBinding(t *testing.T) { | |||
if !reflect.DeepEqual(config.PortBindings, tc.expectedPortBinding) { | |||
t.Error("Expected port bindings to be resolved, was: ", config.PortBindings) | |||
} | |||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NEW!: Remove singular host port validation in TestDockerHostConfigPortBinding
as ports should be validated before returning back from utils.GetHostPort()
@@ -399,13 +388,6 @@ func TestDockerHostConfigPortBinding(t *testing.T) { | |||
if !reflect.DeepEqual(tc.testTask.Containers[0].ContainerPortRangeMap, tc.expectedContainerPortRangeMap) { | |||
t.Error("Expected container port range map to be resolved, was: ", tc.testTask.Containers[0].GetContainerPortRangeMap()) | |||
} | |||
} else { | |||
// Verify ECS Agent assigned host port range are within the dynamic host port range |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NEW!: Remove host port range validation in TestDockerHostConfigSCBridgeMode
as ports should be validated before returning back from utils.GetHostPortRange()
@@ -424,6 +406,14 @@ var ( | |||
defaultSCProtocol = "/tcp" | |||
) | |||
|
|||
func getDefaultDynamicHostPortRange() (start int, end int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function getDefaultDynamicHostPortRange()
has been reviewed in #3585.
@@ -479,61 +469,91 @@ func convertSCPort(port uint16) nat.Port { | |||
} | |||
|
|||
// TestDockerHostConfigSCBridgeMode verifies port bindings and network mode overrides for each | |||
// container in an SC-enabled bridge mode task. The test task is consisted of the SC container, a regular container, | |||
// container in an SC-enabled bridge mode task with default/user-specified dynamic host port range. | |||
// The test task is consisted of the SC container, a regular container, | |||
// and two pause containers associated with each. | |||
func TestDockerHostConfigSCBridgeMode(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test TestDockerHostConfigSCBridgeMode()
has been reviewed in #3585.
@@ -96,13 +98,25 @@ func (pt *safePortTracker) GetLastAssignedHostPort() int { | |||
|
|||
var tracker safePortTracker | |||
|
|||
// ResetTracker resets the last assigned host port to 0. | |||
func ResetTracker() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function ResetTracker()
has been reviewed in #3585.
assert.Equal(t, tc.expectedResult, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestResetTracker(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test TestResetTracker()
has been reviewed in #3585.
@@ -196,7 +196,8 @@ func TestGetHostPort(t *testing.T) { | |||
|
|||
for _, tc := range testCases { | |||
if tc.resetLastAssignedHostPort { | |||
tracker.SetLastAssignedHostPort(0) | |||
// need to reset the tracker to avoid getting data from previous test cases | |||
ResetTracker() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function ResetTracker()
has been reviewed in #3585.
@@ -144,7 +144,7 @@ func TestGetHostPortRange(t *testing.T) { | |||
assert.Equal(t, tc.expectedLastAssignedPort[i], actualLastAssignedHostPort) | |||
} else { | |||
// need to reset the tracker to avoid getting data from previous test cases | |||
tracker.SetLastAssignedHostPort(0) | |||
ResetTracker() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function ResetTracker()
has been reviewed in #3585.
// GetHostPortRange gets N contiguous host ports from the ephemeral host port range defined on the host. | ||
// dynamicHostPortRange can be set by customers using ECS Agent environment variable ECS_DYNAMIC_HOST_PORT_RANGE; | ||
// otherwise, ECS Agent will use the default value returned from GetDynamicHostPortRange() in the utils package. | ||
func GetHostPortRange(numberOfPorts int, protocol string, dynamicHostPortRange string) (string, error) { | ||
portLock.Lock() | ||
defer portLock.Unlock() | ||
result, err := getNumOfHostPorts(numberOfPorts, protocol, dynamicHostPortRange) | ||
if err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NEW!: Add port range validation as ports should be validated before returning back from utils.GetHostPortRange()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIt; generally, I would handle the case where err != nil
in an if
condition for the first thing that could return an error and then another if
condition if the next thing could also return an error.
if err == nil { | ||
result = strings.Split(result, "-")[0] | ||
// Verify the found host port is within the given dynamic host port range |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NEW!: Add port validation as ports should be validated before returning back from utils.GetHostPort()
// or return an error if no host port is available within the range. | ||
hostPortStr, err = getHostPort(protocolStr, dynamicHostPortRange) | ||
if err != nil { | ||
return nil, nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit(non-blocking): maybe we can add a logging statement here saying no host port was available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have to add a logging statement here as the error returned from this function will be surfaced on line 2426 : )
// and let them be published by the associated pause container. | ||
// Note - for SC bridge mode we do not allow customer to specify a host port for their containers. Additionally, | ||
// When an ephemeral host port is assigned, Appnet will NOT proxy traffic to that port | ||
// Find the task container associated with this particular pause container | ||
taskContainer, err := task.getBridgeModeTaskContainerForPauseContainer(container) | ||
if err != nil { | ||
return nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit(non-blocking): can also maybe add a logging statement here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! I saw that getBridgeModeTaskContainerForPauseContainer()
has been used in dockerExposedPorts()
and PopulateServiceConnectContainerMappingEnvVar()
as well. These functions are related to createContainer, thus the error returned from getBridgeModeTaskContainerForPauseContainer()
will eventually be logged as dockerapi.DockerContainerMetadata{Error: XXX}
in ecs-agent.log and also be reported back to ECS control plane.
return dockerPortMap, nil | ||
} | ||
// If the associated task container to this pause container is NOT the service connect AppNet container, | ||
// we will continue to update the dockerPortMap for the pause container using the port bindings | ||
// configured for the application container since port bindings will be published by the pasue container. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "pause"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching this. Will fix the typo when we are going in the PR to rebase against the dev branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comments that serve as mile markers in this code review are really helpful. Thank you for putting those in.
// GetHostPortRange gets N contiguous host ports from the ephemeral host port range defined on the host. | ||
// dynamicHostPortRange can be set by customers using ECS Agent environment variable ECS_DYNAMIC_HOST_PORT_RANGE; | ||
// otherwise, ECS Agent will use the default value returned from GetDynamicHostPortRange() in the utils package. | ||
func GetHostPortRange(numberOfPorts int, protocol string, dynamicHostPortRange string) (string, error) { | ||
portLock.Lock() | ||
defer portLock.Unlock() | ||
result, err := getNumOfHostPorts(numberOfPorts, protocol, dynamicHostPortRange) | ||
if err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIt; generally, I would handle the case where err != nil
in an if
condition for the first thing that could return an error and then another if
condition if the next thing could also return an error.
1. Add GetHostPort() and update unit tests #3570 2. Upudate dockerPortMap() in task.go with dynamic host port range support part 1 #3584 3. Upudate dockerPortMap() in task.go with dynamic host port range support part 2 #3589 4. Validate the host port/host port range found by ECS Agent before returning it #3589
1. Add GetHostPort() and update unit tests aws#3570 2. Upudate dockerPortMap() in task.go with dynamic host port range support part 1 aws#3584 3. Upudate dockerPortMap() in task.go with dynamic host port range support part 2 aws#3589 4. Validate the host port/host port range found by ECS Agent before returning it aws#3589 5. Refactor buildPortMapWithSCIngressConfig() in task.go aws#3600
1. Add GetHostPort() and update unit tests #3570 2. Upudate dockerPortMap() in task.go with dynamic host port range support part 1 #3584 3. Upudate dockerPortMap() in task.go with dynamic host port range support part 2 #3589 4. Validate the host port/host port range found by ECS Agent before returning it #3589 5. Refactor buildPortMapWithSCIngressConfig() in task.go #3600
Summary
DHPA - Dynamic Host Port Assignment
The target branch of this PR is feature/dynamicHostPortAssignment.
This is a follow-up PR for "Upudate dockerPortMap() in task.go with dynamic host port range support" part 1 and part 2 to
dockerPortMap()
. Details of service connect ingress listener port bindings changes, comparing to part 1 can be found in part 2.GetHostPort()
/GetHostPortRange()
to address comments on the part 1 PR here.Implementation details
agent/utils/ephemeral_ports.go
GetHostPort()
/GetHostPortRange()
portIsInRange()
andverifyPortsWithinRange()
to private functionagent/utils/ephemeral_ports_test.go
ResetTracker()
inTestGetHostPort
andTestGetHostPortRange
TestResetTracker
TestGetHostPort
as the port should be validated before returning back fromutils.GetHostPort()
agent/api/task/task.go
buildPortMapWithSCIngressConfig()
to build a dockerPortMap and a containerPortSet for ingress listener portsdockerPortMap()
logger.Debug
entry fromdockerPortMap()
agent/api/task/task_test.go
TestDockerHostConfigSCBridgeMode
to test changesTestDockerHostConfigPortBinding
andTestDockerHostConfigSCBridgeMode
as ports should be validated before returning back fromutils.GetHostPort()
/utils.GetHostPortRange()
Testing
New tests cover the changes: yes
Unit test
TestGetHostPortRange
TestGetHostPort
TestResetTracker
TestDockerHostConfigPortBinding
TestDockerHostConfigSCBridgeMode
Manual testing
Re-ran test cases listed in Upudate dockerPortMap() in task.go with dynamic host port range support - part 1, and test results are the same as expected.
New tests cover the changes: no
Description for the changelog
[Enhancement] Support user-specified dynamic host port range for a singular port and default bridge mode service connect ingress listener port.
Related PRs
Licensing
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.