diff --git a/pkg/cloud/aws/aws_config_transformer.go b/pkg/cloud/aws/aws_config_transformer.go index 92f96805b..385064e5a 100644 --- a/pkg/cloud/aws/aws_config_transformer.go +++ b/pkg/cloud/aws/aws_config_transformer.go @@ -54,7 +54,8 @@ func readAWSConfig(source string) (*awsconfig.CloudConfig, error) { } func marshalAWSConfig(cfg *awsconfig.CloudConfig) (string, error) { - file := ini.Empty() + // Configure iniv1 to allow shadow fields to enable multiple entries of NodeIPFamilies. + file := ini.Empty(ini.LoadOptions{AllowShadows: true}) if err := file.Section("Global").ReflectFrom(&cfg.Global); err != nil { return "", fmt.Errorf("failed to reflect global config: %w", err) } @@ -65,6 +66,25 @@ func marshalAWSConfig(cfg *awsconfig.CloudConfig) (string, error) { } } + // In dual-stack environment, the CCM expects NodeIPFamilies to be in the format: + // + // NodeIPFamilies=ipv4 + // NodeIPFamilies=ipv6 + // + // However, iniv1 is serializing go slices as comma-separated list, for example: + // + // NodeIPFamilies=ipv4,ipv6 + // + // Below logic ensures the original NodeIPFamilies field is kept as-is after transforming. + nodeIPKey := file.Section("Global").Key("NodeIPFamilies") + for i, ipFamily := range cfg.Global.NodeIPFamilies { + if i == 0 { + nodeIPKey.SetValue(ipFamily) + } else if err := nodeIPKey.AddShadow(ipFamily); err != nil { + return "", fmt.Errorf("failed to set NodeIPFamilies: %w", err) + } + } + for _, section := range file.Sections() { for key, value := range section.KeysHash() { // Ignore anything that is the zero value for its type. diff --git a/pkg/cloud/aws/aws_config_transformer_test.go b/pkg/cloud/aws/aws_config_transformer_test.go index 34c5116d8..f85fc4d1d 100644 --- a/pkg/cloud/aws/aws_config_transformer_test.go +++ b/pkg/cloud/aws/aws_config_transformer_test.go @@ -83,6 +83,20 @@ URL = https://s3.foo.bar SigningRegion = signing_region `, // Ordered based on the order of fields in the AWS CloudConfig struct. }, + { + name: "with NodeIPFamilies", + source: `[Global] +NodeIPFamilies = ipv4 +NodeIPFamilies = ipv6 + `, + expected: `[Global] +DisableSecurityGroupIngress = false +NodeIPFamilies = ipv4 +NodeIPFamilies = ipv6 +ClusterServiceLoadBalancerHealthProbeMode = Shared +ClusterServiceSharedLoadBalancerHealthProbePort = 0 +`, + }, } for _, tc := range testCases {