Skip to content

Commit 55d3f8f

Browse files
committed
Wire-in IP compatibility detection and override (#4752)
1 parent 295f176 commit 55d3f8f

File tree

7 files changed

+102
-30
lines changed

7 files changed

+102
-30
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ additional details on each available environment variable.
211211
| `ECS_IMAGE_PULL_INACTIVITY_TIMEOUT` | 1m | The time to wait after docker pulls complete waiting for extraction of a container. Useful for tuning large Windows containers. | 1m | 3m |
212212
| `ECS_IMAGE_PULL_TIMEOUT` | 1h | The time to wait for pulling docker image. | 2h | 2h |
213213
| `ECS_INSTANCE_ATTRIBUTES` | `{"stack": "prod"}` | These attributes take effect only during initial registration. After the agent has joined an ECS cluster, use the PutAttributes API action to add additional attributes. For more information, see [Amazon ECS Container Agent Configuration](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-agent-config.html) in the Amazon ECS Developer Guide.| `{}` | `{}` |
214+
| `ECS_INSTANCE_IP_COMPATIBILITY` | `ipv4` | `ipv6` | Option to override Agent's auto detection of instance's IP compatibility. When unset, Agent consults the instance's default IPv4 and IPv6 routes to detect the IP compatibility of the instance. Use this option to override auto detection with the value you provide. For IPv6-only instances, Agent uses dualstack endpoints when making any AWS service calls and sets up features to be IPv6-compatible. For dualstack instances, just use `ipv4`. Neither this option nor the auto detection of IP compatibility are supported on Windows and Agent always works in IPv4-compatible mode. | Automatic detection based on default routes | Not Supported on Windows |
214215
| `ECS_ENABLE_TASK_ENI` | `false` | Whether to enable task networking for task to be launched with its own network interface | `false` | Not applicable |
215216
| `ECS_ENABLE_HIGH_DENSITY_ENI` | `false` | Whether to enable high density eni feature when using task networking | `true` | Not applicable |
216217
| `ECS_CNI_PLUGINS_PATH` | `/ecs/cni` | The path where the cni binary file is located | `/amazon-ecs-cni-plugins` | Not applicable |

agent/config/config.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,7 @@ func environmentConfig() (Config, error) {
605605
DynamicHostPortRange: parseDynamicHostPortRange("ECS_DYNAMIC_HOST_PORT_RANGE"),
606606
TaskPidsLimit: parseTaskPidsLimit(),
607607
FirelensAsyncEnabled: parseBooleanDefaultTrueConfig("ECS_ENABLE_FIRELENS_ASYNC"),
608-
609-
// TODO:feat:ipv6-only Enable InstanceIPCompatibility parameter when the feature is ready
610-
// InstanceIPCompatibility: parseInstanceIPCompatibility(),
608+
InstanceIPCompatibility: parseInstanceIPCompatibility(),
611609
}, err
612610
}
613611

agent/config/config_unix.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/aws/amazon-ecs-agent/agent/dockerclient"
2525
"github.com/aws/amazon-ecs-agent/agent/utils"
2626
"github.com/aws/amazon-ecs-agent/ecs-agent/ipcompatibility"
27+
commonutils "github.com/aws/amazon-ecs-agent/ecs-agent/utils"
2728
netutils "github.com/aws/amazon-ecs-agent/ecs-agent/utils/net"
2829
"github.com/aws/amazon-ecs-agent/ecs-agent/utils/netlinkwrapper"
2930

@@ -138,15 +139,13 @@ func DefaultConfig(ipCompatOverride ipcompatibility.IPCompatibility) Config {
138139
FirelensAsyncEnabled: BooleanDefaultTrue{Value: ExplicitlyEnabled},
139140
}
140141

141-
// TODO:feat:ipv6-only Enable IP compatibility detection when the feature is ready
142-
// if commonutils.ZeroOrNil(ipCompatOverride) {
143-
// logger.Info("No IP compatibility override provided, detecting instance IP compatibility for default config")
144-
// cfg.determineIPCompatibility()
145-
// cfg.setIPv6PortBindingDefault(cfg.InstanceIPCompatibility)
146-
// } else {
147-
// cfg.setIPv6PortBindingDefault(ipCompatOverride)
148-
// }
149-
cfg.setIPv6PortBindingDefault(ipCompatOverride)
142+
if commonutils.ZeroOrNil(ipCompatOverride) {
143+
logger.Info("No IP compatibility override provided, detecting instance IP compatibility for default config")
144+
cfg.determineIPCompatibility()
145+
} else {
146+
cfg.InstanceIPCompatibility = ipCompatOverride
147+
}
148+
cfg.setIPv6PortBindingDefault(cfg.InstanceIPCompatibility)
150149

151150
return cfg
152151
}

agent/config/config_unix_test.go

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -386,35 +386,121 @@ func TestShouldExcludeIPv6PortBindingDefault(t *testing.T) {
386386
testCases := []struct {
387387
name string
388388
instanceIPCompatibility ipcompatibility.IPCompatibility
389-
expectedExcludeIPv6 bool
389+
expectedExcludeIPv6 BooleanDefaultTrue
390390
}{
391391
{
392392
name: "ipv6-only instance",
393393
instanceIPCompatibility: ipcompatibility.NewIPv6OnlyCompatibility(),
394-
expectedExcludeIPv6: false, // IPv6 port bindings should be included for IPv6-only instances
394+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyDisabled},
395395
},
396396
{
397397
name: "dual-stack instance",
398398
instanceIPCompatibility: ipcompatibility.NewDualStackCompatibility(),
399-
expectedExcludeIPv6: true, // IPv6 port bindings should be excluded by default
399+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
400400
},
401401
{
402402
name: "ipv4-only instance",
403403
instanceIPCompatibility: ipcompatibility.NewIPv4OnlyCompatibility(),
404-
expectedExcludeIPv6: true, // IPv6 port bindings should be excluded by default
404+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
405405
},
406406
{
407407
name: "no ip compatibility",
408408
instanceIPCompatibility: ipcompatibility.NewIPCompatibility(false, false),
409-
expectedExcludeIPv6: true, // IPv6 port bindings should be excluded by default
409+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
410410
},
411411
}
412412

413413
for _, tc := range testCases {
414414
t.Run(tc.name, func(t *testing.T) {
415415
cfg := &Config{}
416416
cfg.setIPv6PortBindingDefault(tc.instanceIPCompatibility)
417-
assert.Equal(t, tc.expectedExcludeIPv6, cfg.ShouldExcludeIPv6PortBinding.Enabled())
417+
assert.Equal(t, tc.expectedExcludeIPv6, cfg.ShouldExcludeIPv6PortBinding)
418+
})
419+
}
420+
}
421+
422+
func TestDefaultConfigIPCompatibilityAndExcludeIPv6PortBinding(t *testing.T) {
423+
ipv4Gw := net.ParseIP("10.0.0.1")
424+
ipv6Gw := net.ParseIP("1:2:3:4::")
425+
require.NotNil(t, ipv4Gw)
426+
require.NotNil(t, ipv6Gw)
427+
ipv4Route := netlink.Route{Gw: ipv4Gw, Dst: nil}
428+
ipv6Route := netlink.Route{Gw: ipv6Gw, Dst: nil}
429+
430+
testCases := []struct {
431+
name string
432+
ipCompatOverride ipcompatibility.IPCompatibility
433+
setNetlinkExpectations func(*mock_netlinkwrapper.MockNetLink)
434+
expectedInstanceIPCompat ipcompatibility.IPCompatibility
435+
expectedExcludeIPv6 BooleanDefaultTrue
436+
}{
437+
{
438+
name: "Override with IPv4-only",
439+
ipCompatOverride: ipcompatibility.NewIPv4OnlyCompatibility(),
440+
expectedInstanceIPCompat: ipcompatibility.NewIPv4OnlyCompatibility(),
441+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
442+
},
443+
{
444+
name: "Override with IPv6-only",
445+
ipCompatOverride: ipcompatibility.NewIPv6OnlyCompatibility(),
446+
expectedInstanceIPCompat: ipcompatibility.NewIPv6OnlyCompatibility(),
447+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyDisabled},
448+
},
449+
{
450+
name: "Override with dual-stack",
451+
ipCompatOverride: ipcompatibility.NewDualStackCompatibility(),
452+
expectedInstanceIPCompat: ipcompatibility.NewDualStackCompatibility(),
453+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
454+
},
455+
456+
{
457+
name: "No override - auto-detect IPv4-only",
458+
ipCompatOverride: ipcompatibility.IPCompatibility{}, // Zero value triggers auto-detection
459+
setNetlinkExpectations: func(nl *mock_netlinkwrapper.MockNetLink) {
460+
nl.EXPECT().RouteList(nil, netlink.FAMILY_V4).Return([]netlink.Route{ipv4Route}, nil)
461+
nl.EXPECT().RouteList(nil, netlink.FAMILY_V6).Return([]netlink.Route{}, nil)
462+
},
463+
expectedInstanceIPCompat: ipcompatibility.NewIPv4OnlyCompatibility(),
464+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
465+
},
466+
{
467+
name: "No override - auto-detect IPv6-only",
468+
ipCompatOverride: ipcompatibility.IPCompatibility{}, // Zero value triggers auto-detection
469+
setNetlinkExpectations: func(nl *mock_netlinkwrapper.MockNetLink) {
470+
nl.EXPECT().RouteList(nil, netlink.FAMILY_V4).Return([]netlink.Route{}, nil)
471+
nl.EXPECT().RouteList(nil, netlink.FAMILY_V6).Return([]netlink.Route{ipv6Route}, nil)
472+
},
473+
expectedInstanceIPCompat: ipcompatibility.NewIPv6OnlyCompatibility(),
474+
// IPv6 port bindings included for IPv6-only
475+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyDisabled},
476+
},
477+
{
478+
name: "No override - auto-detect dual-stack",
479+
ipCompatOverride: ipcompatibility.IPCompatibility{}, // Zero value triggers auto-detection
480+
setNetlinkExpectations: func(nl *mock_netlinkwrapper.MockNetLink) {
481+
nl.EXPECT().RouteList(nil, netlink.FAMILY_V4).Return([]netlink.Route{ipv4Route}, nil)
482+
nl.EXPECT().RouteList(nil, netlink.FAMILY_V6).Return([]netlink.Route{ipv6Route}, nil)
483+
},
484+
expectedInstanceIPCompat: ipcompatibility.NewDualStackCompatibility(),
485+
expectedExcludeIPv6: BooleanDefaultTrue{Value: ExplicitlyEnabled},
486+
},
487+
}
488+
489+
for _, tc := range testCases {
490+
t.Run(tc.name, func(t *testing.T) {
491+
ctrl := gomock.NewController(t)
492+
defer ctrl.Finish()
493+
494+
mockNLWrapper := mock_netlinkwrapper.NewMockNetLink(ctrl)
495+
defer setMockNLWrapper(mockNLWrapper)()
496+
497+
if tc.setNetlinkExpectations != nil {
498+
tc.setNetlinkExpectations(mockNLWrapper)
499+
}
500+
501+
cfg := DefaultConfig(tc.ipCompatOverride)
502+
assert.Equal(t, tc.expectedInstanceIPCompat, cfg.InstanceIPCompatibility)
503+
assert.Equal(t, tc.expectedExcludeIPv6, cfg.ShouldExcludeIPv6PortBinding)
418504
})
419505
}
420506
}

agent/config/parse.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ const (
4545
// location of the credentials-fetcher location on the machine
4646
envCredentialsFetcherHostDir = "CREDENTIALS_FETCHER_HOST_DIR"
4747
// envInstanceIPCompatibility is an environment setting to override IP compatibility detection
48-
//
49-
// TODO:feat:IPv6-only - Remove lint rule below
50-
//
51-
//lint:ignore U1000 Constant will be used in the future
5248
envInstanceIPCompatibility = "ECS_INSTANCE_IP_COMPATIBILITY"
5349
)
5450

agent/config/parse_linux.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,6 @@ func parseTaskPidsLimit() int {
158158
// - "ipv4": Returns IPv4-only compatibility
159159
// - "ipv6": Returns IPv6-only compatibility
160160
// - empty/unset or invalid: Returns zero value
161-
//
162-
// TODO:feat:IPv6-only - Remove lint rule below
163-
//
164-
//lint:ignore U1000 Function will be used in the future
165161
func parseInstanceIPCompatibility() ipcompatibility.IPCompatibility {
166162
val := strings.TrimSpace(os.Getenv(envInstanceIPCompatibility))
167163
if val == "" {

agent/config/parse_windows.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,6 @@ func parseTaskPidsLimit() int {
202202

203203
// parseInstanceIPCompatibility always returns zero-value IP compatibility as the config
204204
// parameter to override instance IP compatibility is not supported on Windows.
205-
//
206-
// TODO:feat:IPv6-only - Remove lint rule below
207-
//
208-
//lint:ignore U1000 Constant will be used in the future
209205
func parseInstanceIPCompatibility() ipcompatibility.IPCompatibility {
210206
if os.Getenv(envInstanceIPCompatibility) != "" {
211207
logger.Warn(envInstanceIPCompatibility + " is not supported on Windows")

0 commit comments

Comments
 (0)