Skip to content

Commit

Permalink
Merge pull request #3688 from wweiwei-li/dualstackWithoutIPV4
Browse files Browse the repository at this point in the history
Add dualstack without public IPv4 IP Address type
  • Loading branch information
k8s-ci-robot committed May 10, 2024
2 parents 334ffcf + c8d6aa3 commit 2a66d99
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 6 deletions.
1 change: 1 addition & 0 deletions config/crd/bases/elbv2.k8s.aws_ingressclassparams.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ spec:
enum:
- ipv4
- dualstack
- dualstack-without-public-ipv4
type: string
loadBalancerAttributes:
description: LoadBalancerAttributes define the custom attributes to
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/ingress/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ You can add annotations to kubernetes Ingress and Service objects to customize t
| [alb.ingress.kubernetes.io/group.name](#group.name) | string |N/A|Ingress|N/A|
| [alb.ingress.kubernetes.io/group.order](#group.order) | integer |0|Ingress|N/A|
| [alb.ingress.kubernetes.io/tags](#tags) | stringMap |N/A|Ingress,Service|Merge|
| [alb.ingress.kubernetes.io/ip-address-type](#ip-address-type) | ipv4 \| dualstack |ipv4|Ingress|Exclusive|
| [alb.ingress.kubernetes.io/ip-address-type](#ip-address-type) | ipv4 \| dualstack \| dualstack-without-public-ipv4 |ipv4|Ingress|Exclusive|
| [alb.ingress.kubernetes.io/scheme](#scheme) | internal \| internet-facing |internal|Ingress|Exclusive|
| [alb.ingress.kubernetes.io/subnets](#subnets) | stringList |N/A|Ingress|Exclusive|
| [alb.ingress.kubernetes.io/security-groups](#security-groups) | stringList |N/A|Ingress|Exclusive|
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/ingress/ingress_class.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Within any given availability zone, subnets with a cluster tag will be chosen ov

#### spec.ipAddressType

`ipAddressType` is an optional setting. The available options are `ipv4` or `dualstack`.
`ipAddressType` is an optional setting. The available options are `ipv4`, `dualstack`, or `dualstack-without-public-ipv4`.

Cluster administrators can use `ipAddressType` field to restrict the ipAddressType for all Ingresses that belong to this IngressClass.

Expand Down
1 change: 1 addition & 0 deletions helm/aws-load-balancer-controller/crds/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ spec:
enum:
- ipv4
- dualstack
- dualstack-without-public-ipv4
type: string
loadBalancerAttributes:
description: LoadBalancerAttributes define the custom attributes to
Expand Down
11 changes: 11 additions & 0 deletions pkg/ingress/model_build_load_balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ func (t *defaultModelBuildTask) buildLoadBalancerIPAddressType(_ context.Context
return elbv2model.IPAddressTypeIPV4, nil
case string(elbv2model.IPAddressTypeDualStack):
return elbv2model.IPAddressTypeDualStack, nil
case string(elbv2model.IPAddressTypeDualStackWithoutPublicIPV4):
return elbv2model.IPAddressTypeDualStackWithoutPublicIPV4, nil
default:
return "", errors.Errorf("unknown IPAddressType: %v", rawIPAddressType)
}
Expand Down Expand Up @@ -412,3 +414,12 @@ func buildLoadBalancerSubnetMappingsWithSubnetIDs(subnetIDs []string) []elbv2mod
}
return subnetMappings
}

func isIPv6Supported(ipAddressType elbv2model.IPAddressType) bool {
switch ipAddressType {
case elbv2model.IPAddressTypeDualStack, elbv2model.IPAddressTypeDualStackWithoutPublicIPV4:
return true
default:
return false
}
}
114 changes: 114 additions & 0 deletions pkg/ingress/model_build_load_balancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1295,3 +1295,117 @@ func Test_defaultModelBuildTask_buildLoadBalancerSubnets(t *testing.T) {
})
}
}

func Test_defaultModelBuildTask_buildLoadBalancerIPAddressType(t *testing.T) {
type fields struct {
ingGroup Group
}

tests := []struct {
name string
fields fields
want elbv2.IPAddressType
wantErr error
}{
{
name: "No ip-address-type annotation set",
fields: fields{
ingGroup: Group{
ID: GroupID{Name: "explicit-group"},
Members: []ClassifiedIngress{
{
Ing: &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "awesome-ns",
Name: "ing-1",
},
},
},
},
},
},
want: "",
},
{
name: "The ip-address-type annotation is set to ipv4",
fields: fields{
ingGroup: Group{
ID: GroupID{Name: "explicit-group"},
Members: []ClassifiedIngress{
{
Ing: &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "awesome-ns",
Name: "ing-1",
Annotations: map[string]string{
"alb.ingress.kubernetes.io/ip-address-type": "ipv4",
},
},
},
},
},
},
},
want: elbv2.IPAddressTypeIPV4,
},
{
name: "The ip-address-type annotation is set to dualstack",
fields: fields{
ingGroup: Group{
ID: GroupID{Name: "explicit-group"},
Members: []ClassifiedIngress{
{
Ing: &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "awesome-ns",
Name: "ing-1",
Annotations: map[string]string{
"alb.ingress.kubernetes.io/ip-address-type": "dualstack",
},
},
},
},
},
},
},
want: elbv2.IPAddressTypeDualStack,
},
{
name: "The ip-address-type annotation is set to dualstack-without-public-ipv4",
fields: fields{
ingGroup: Group{
ID: GroupID{Name: "explicit-group"},
Members: []ClassifiedIngress{
{
Ing: &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "awesome-ns",
Name: "ing-1",
Annotations: map[string]string{
"alb.ingress.kubernetes.io/ip-address-type": "dualstack-without-public-ipv4",
"alb.ingress.kubernetes.io/scheme": "internet-facing",
},
},
},
},
},
},
},
want: elbv2.IPAddressTypeDualStackWithoutPublicIPV4,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
task := &defaultModelBuildTask{
ingGroup: tt.fields.ingGroup,
annotationParser: annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io"),
}
got, err := task.buildLoadBalancerIPAddressType(context.Background())
if err != nil {
assert.EqualError(t, err, tt.wantErr.Error())
} else {
assert.Equal(t, tt.want, got)
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/ingress/model_build_managed_sg.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (t *defaultModelBuildTask) buildManagedSecurityGroupIngressPermissions(_ co
},
})
}
if ipAddressType == elbv2model.IPAddressTypeDualStack {
if isIPv6Supported(ipAddressType) {
for _, cidr := range cfg.inboundCIDRv6s {
permissions = append(permissions, ec2model.IPPermission{
IPProtocol: "tcp",
Expand Down
2 changes: 1 addition & 1 deletion pkg/ingress/model_build_target_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (t *defaultModelBuildTask) buildTargetGroupIPAddressType(_ context.Context,
}
}
if ipv6Configured {
if *t.loadBalancer.Spec.IPAddressType != elbv2model.IPAddressTypeDualStack {
if !isIPv6Supported(*t.loadBalancer.Spec.IPAddressType) {
return "", errors.New("unsupported IPv6 configuration, lb not dual-stack")
}
return elbv2model.TargetGroupIPAddressTypeIPv6, nil
Expand Down
5 changes: 3 additions & 2 deletions pkg/model/elbv2/load_balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ const (
type IPAddressType string

const (
IPAddressTypeIPV4 IPAddressType = "ipv4"
IPAddressTypeDualStack IPAddressType = "dualstack"
IPAddressTypeIPV4 IPAddressType = "ipv4"
IPAddressTypeDualStack IPAddressType = "dualstack"
IPAddressTypeDualStackWithoutPublicIPV4 IPAddressType = "dualstack-without-public-ipv4"
)

type SecurityGroupsInboundRulesOnPrivateLinkStatus string
Expand Down
44 changes: 44 additions & 0 deletions test/e2e/ingress/vanilla_ingress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,50 @@ var _ = Describe("vanilla ingress tests", func() {
Status(http.StatusNotFound)
})
})

Context("with `alb.ingress.kubernetes.io/ip-address-type` variant settings", func() {
It("with 'alb.ingress.kubernetes.io/ip-address-type' annotation explicitly specified, one ALB shall be created and functional", func() {
appBuilder := manifest.NewFixedResponseServiceBuilder()
ingBuilder := manifest.NewIngressBuilder()
dp, svc := appBuilder.Build(sandboxNS.Name, "app", tf.Options.TestImageRegistry)
ingBackend := networking.IngressBackend{
Service: &networking.IngressServiceBackend{
Name: svc.Name,
Port: networking.ServiceBackendPort{
Number: 80,
},
},
}
annotation := map[string]string{
"kubernetes.io/ingress.class": "alb",
"alb.ingress.kubernetes.io/scheme": "internet-facing",
"alb.ingress.kubernetes.io/target-type": "ip",
"alb.ingress.kubernetes.io/ip-address-type": "ipv4",
}

if tf.Options.IPFamily == "IPv6" {
annotation["alb.ingress.kubernetes.io/ip-address-type"] = "dualstack-without-public-ipv4"
}

ing := ingBuilder.
AddHTTPRoute("", networking.HTTPIngressPath{Path: "/path", PathType: &exact, Backend: ingBackend}).
WithAnnotations(annotation).Build(sandboxNS.Name, "ing")
resStack := fixture.NewK8SResourceStack(tf, dp, svc, ing)
err := resStack.Setup(ctx)
Expect(err).NotTo(HaveOccurred())

defer resStack.TearDown(ctx)

lbARN, lbDNS := ExpectOneLBProvisionedForIngress(ctx, tf, ing)

// test traffic
ExpectLBDNSBeAvailable(ctx, tf, lbARN, lbDNS)
httpExp := httpexpect.New(tf.LoggerReporter, fmt.Sprintf("http://%v", lbDNS))
httpExp.GET("/path").Expect().
Status(http.StatusOK).
Body().Equal("Hello World!")
})
})
})

// ExpectOneLBProvisionedForIngress expects one LoadBalancer provisioned for Ingress.
Expand Down

0 comments on commit 2a66d99

Please sign in to comment.