Merge pull request #16299 from justinsb/split_out_loadbalancerlistener

Refactor: Split out NLB Listener into its own task
This commit is contained in:
Kubernetes Prow Robot 2024-02-04 14:02:36 -08:00 committed by GitHub
commit 61842d1e45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 398 additions and 226 deletions

View File

@ -21,39 +21,41 @@ import (
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/elbv2"
"k8s.io/klog/v2"
)
func (m *MockELBV2) DescribeListeners(request *elbv2.DescribeListenersInput) (*elbv2.DescribeListenersOutput, error) {
func (m *MockELBV2) DescribeListenersPagesWithContext(ctx aws.Context, request *elbv2.DescribeListenersInput, callback func(*elbv2.DescribeListenersOutput, bool) bool, options ...request.Option) error {
m.mutex.Lock()
defer m.mutex.Unlock()
klog.Infof("DescribeListeners v2 %v", request)
klog.Infof("DescribeListenersPagesWithContext v2 %v", request)
resp := &elbv2.DescribeListenersOutput{
page := &elbv2.DescribeListenersOutput{
Listeners: make([]*elbv2.Listener, 0),
}
for _, l := range m.Listeners {
listener := l.description
if aws.StringValue(request.LoadBalancerArn) == aws.StringValue(listener.LoadBalancerArn) {
resp.Listeners = append(resp.Listeners, &listener)
page.Listeners = append(page.Listeners, &listener)
} else {
for _, reqARN := range request.ListenerArns {
if aws.StringValue(reqARN) == aws.StringValue(listener.ListenerArn) {
resp.Listeners = append(resp.Listeners, &listener)
page.Listeners = append(page.Listeners, &listener)
}
}
}
}
return resp, nil
callback(page, true)
return nil
}
func (m *MockELBV2) CreateListener(request *elbv2.CreateListenerInput) (*elbv2.CreateListenerOutput, error) {
func (m *MockELBV2) CreateListenerWithContext(ctx aws.Context, request *elbv2.CreateListenerInput, opts ...request.Option) (*elbv2.CreateListenerOutput, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
klog.Infof("CreateListener v2 %v", request)
klog.Infof("CreateListenerWithContext v2 %v", request)
l := elbv2.Listener{
DefaultActions: request.DefaultActions,
@ -96,11 +98,11 @@ func (m *MockELBV2) CreateListener(request *elbv2.CreateListenerInput) (*elbv2.C
return &elbv2.CreateListenerOutput{Listeners: []*elbv2.Listener{&l}}, nil
}
func (m *MockELBV2) DeleteListener(request *elbv2.DeleteListenerInput) (*elbv2.DeleteListenerOutput, error) {
func (m *MockELBV2) DeleteListenerWithContext(ctx aws.Context, request *elbv2.DeleteListenerInput, opts ...request.Option) (*elbv2.DeleteListenerOutput, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
klog.Infof("DeleteListener v2 %v", request)
klog.Infof("DeleteListenerWithContext v2 %v", request)
lARN := aws.StringValue(request.ListenerArn)
if _, ok := m.Listeners[lARN]; !ok {

View File

@ -134,31 +134,49 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error {
listeners := map[string]*awstasks.ClassicLoadBalancerListener{
"443": {InstancePort: 443},
}
var nlbListeners []*awstasks.NetworkLoadBalancerListener
nlbListeners := []*awstasks.NetworkLoadBalancerListener{
{
Port: 443,
TargetGroupName: b.NLBTargetGroupName("tcp"),
},
}
if b.Cluster.UsesNoneDNS() {
nlbListeners = append(nlbListeners, &awstasks.NetworkLoadBalancerListener{
Port: wellknownports.KopsControllerPort,
TargetGroupName: b.NLBTargetGroupName("kops-controller"),
})
}
if lbSpec.SSLCertificate == "" {
listener443 := &awstasks.NetworkLoadBalancerListener{
Name: fi.PtrTo(b.NLBListenerName("api", 443)),
Lifecycle: b.Lifecycle,
NetworkLoadBalancer: b.LinkToNLB("api"),
Port: 443,
TargetGroup: b.LinkToTargetGroup("tcp"),
}
nlbListeners = append(nlbListeners, listener443)
} else {
listener8443 := &awstasks.NetworkLoadBalancerListener{
Name: fi.PtrTo(b.NLBListenerName("api", 8443)),
Lifecycle: b.Lifecycle,
NetworkLoadBalancer: b.LinkToNLB("api"),
Port: 8443,
TargetGroup: b.LinkToTargetGroup("tcp"),
}
nlbListeners = append(nlbListeners, listener8443)
if lbSpec.SSLCertificate != "" {
listeners["443"].SSLCertificateID = lbSpec.SSLCertificate
nlbListeners[0].Port = 8443
nlbListener := &awstasks.NetworkLoadBalancerListener{
Port: 443,
TargetGroupName: b.NLBTargetGroupName("tls"),
SSLCertificateID: lbSpec.SSLCertificate,
listener443 := &awstasks.NetworkLoadBalancerListener{
Name: fi.PtrTo(b.NLBListenerName("api", 443)),
Lifecycle: b.Lifecycle,
NetworkLoadBalancer: b.LinkToNLB("api"),
Port: 443,
TargetGroup: b.LinkToTargetGroup("tls"),
SSLCertificateID: lbSpec.SSLCertificate,
}
if lbSpec.SSLPolicy != nil {
nlbListener.SSLPolicy = *lbSpec.SSLPolicy
listener443.SSLPolicy = *lbSpec.SSLPolicy
}
nlbListeners = append(nlbListeners, listener443)
}
if b.Cluster.UsesNoneDNS() {
nlbListener := &awstasks.NetworkLoadBalancerListener{
Name: fi.PtrTo(b.NLBListenerName("api", wellknownports.KopsControllerPort)),
Lifecycle: b.Lifecycle,
NetworkLoadBalancer: b.LinkToNLB("api"),
Port: wellknownports.KopsControllerPort,
TargetGroup: b.LinkToTargetGroup("kops-controller"),
}
nlbListeners = append(nlbListeners, nlbListener)
}
@ -184,7 +202,6 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error {
b.LinkToELBSecurityGroup("api"),
},
SubnetMappings: nlbSubnetMappings,
Listeners: nlbListeners,
TargetGroups: make([]*awstasks.TargetGroup, 0),
Tags: tags,
@ -359,6 +376,9 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error {
nlb.TargetGroups = append(nlb.TargetGroups, secondaryTG)
}
sort.Stable(awstasks.OrderTargetGroupsByName(nlb.TargetGroups))
for _, nlbListener := range nlbListeners {
c.AddTask(nlbListener)
}
c.AddTask(nlb)
}

View File

@ -328,12 +328,15 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error {
// Override the returned name to be the expected ELB name
tags["Name"] = "bastion." + b.ClusterName()
nlbListeners := []*awstasks.NetworkLoadBalancerListener{
{
Port: 22,
TargetGroupName: b.NLBTargetGroupName("bastion"),
},
nlbListener := &awstasks.NetworkLoadBalancerListener{
Name: fi.PtrTo(b.NLBListenerName("bastion", 22)),
Lifecycle: b.Lifecycle,
NetworkLoadBalancer: b.LinkToNLB("bastion"),
Port: 22,
TargetGroup: b.LinkToTargetGroup("bastion"),
}
c.AddTask(nlbListener)
nlb = &awstasks.NetworkLoadBalancer{
Name: fi.PtrTo(b.NLBName("bastion")),
Lifecycle: b.Lifecycle,
@ -344,7 +347,6 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error {
SecurityGroups: []*awstasks.SecurityGroup{
b.LinkToELBSecurityGroup("bastion"),
},
Listeners: nlbListeners,
TargetGroups: make([]*awstasks.TargetGroup, 0),
Tags: tags,

View File

@ -19,6 +19,7 @@ package model
import (
"fmt"
"regexp"
"strconv"
"strings"
"k8s.io/klog/v2"
@ -115,6 +116,11 @@ func (b *KopsModelContext) LinkToNLB(prefix string) *awstasks.NetworkLoadBalance
return &awstasks.NetworkLoadBalancer{Name: &name}
}
func (b *KopsModelContext) NLBListenerName(loadBalancerPrefix string, port int) string {
name := b.NLBName(loadBalancerPrefix)
return name + "-" + strconv.Itoa(port)
}
func (b *KopsModelContext) LinkToTargetGroup(prefix string) *awstasks.TargetGroup {
name := b.NLBTargetGroupName(prefix)
return &awstasks.TargetGroup{Name: &name}

View File

@ -58,8 +58,6 @@ type NetworkLoadBalancer struct {
SubnetMappings []*SubnetMapping
SecurityGroups []*SecurityGroup
Listeners []*NetworkLoadBalancerListener
Scheme *string
CrossZoneLoadBalancing *bool
@ -80,6 +78,9 @@ type NetworkLoadBalancer struct {
// waitForLoadBalancerReady controls whether we wait for the load balancer to be ready before completing the "Render" operation.
waitForLoadBalancerReady bool
// After this is found/created, we store the ARN
loadBalancerArn string
}
func (e *NetworkLoadBalancer) SetWaitForLoadBalancerReady(v bool) {
@ -94,66 +95,6 @@ func (e *NetworkLoadBalancer) CompareWithID() *string {
return e.Name
}
type NetworkLoadBalancerListener struct {
Port int
TargetGroupName string
SSLCertificateID string
SSLPolicy string
}
func (e *NetworkLoadBalancerListener) mapToAWS(targetGroups []*TargetGroup, loadBalancerArn string) (*elbv2.CreateListenerInput, error) {
var tgARN string
for _, tg := range targetGroups {
if fi.ValueOf(tg.Name) == e.TargetGroupName {
tgARN = fi.ValueOf(tg.ARN)
}
}
if tgARN == "" {
return nil, fmt.Errorf("target group not found for NLB listener %+v", e)
}
l := &elbv2.CreateListenerInput{
DefaultActions: []*elbv2.Action{
{
TargetGroupArn: aws.String(tgARN),
Type: aws.String(elbv2.ActionTypeEnumForward),
},
},
LoadBalancerArn: aws.String(loadBalancerArn),
Port: aws.Int64(int64(e.Port)),
}
if e.SSLCertificateID != "" {
l.Certificates = []*elbv2.Certificate{}
l.Certificates = append(l.Certificates, &elbv2.Certificate{
CertificateArn: aws.String(e.SSLCertificateID),
})
l.Protocol = aws.String(elbv2.ProtocolEnumTls)
if e.SSLPolicy != "" {
l.SslPolicy = aws.String(e.SSLPolicy)
}
} else {
l.Protocol = aws.String(elbv2.ProtocolEnumTcp)
}
return l, nil
}
var _ fi.CloudupHasDependencies = &NetworkLoadBalancerListener{}
func (e *NetworkLoadBalancerListener) GetDependencies(tasks map[string]fi.CloudupTask) []fi.CloudupTask {
return nil
}
// OrderListenersByPort implements sort.Interface for []OrderListenersByPort, based on port number
type OrderListenersByPort []*NetworkLoadBalancerListener
func (a OrderListenersByPort) Len() int { return len(a) }
func (a OrderListenersByPort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a OrderListenersByPort) Less(i, j int) bool {
return a[i].Port < a[j].Port
}
// The load balancer name 'api.renamenlbcluster.k8s.local' can only contain characters that are alphanumeric characters and hyphens(-)\n\tstatus code: 400,
func findNetworkLoadBalancerByLoadBalancerName(cloud awsup.AWSCloud, loadBalancerName string) (*elbv2.LoadBalancer, error) {
request := &elbv2.DescribeLoadBalancersInput{
@ -255,6 +196,7 @@ func (e *NetworkLoadBalancer) getHostedZoneId() *string {
}
func (e *NetworkLoadBalancer) Find(c *fi.CloudupContext) (*NetworkLoadBalancer, error) {
ctx := c.Context()
cloud := c.T.Cloud.(awsup.AWSCloud)
lb, err := cloud.FindELBV2ByNameTag(e.Tags["Name"])
@ -319,23 +261,16 @@ func (e *NetworkLoadBalancer) Find(c *fi.CloudupContext) (*NetworkLoadBalancer,
request := &elbv2.DescribeListenersInput{
LoadBalancerArn: loadBalancerArn,
}
response, err := cloud.ELBV2().DescribeListeners(request)
if err != nil {
return nil, fmt.Errorf("error querying for NLB listeners :%v", err)
var listeners []*elbv2.Listener
if err := cloud.ELBV2().DescribeListenersPagesWithContext(ctx, request, func(page *elbv2.DescribeListenersOutput, lastPage bool) bool {
listeners = append(listeners, page.Listeners...)
return true
}); err != nil {
return nil, fmt.Errorf("listing NLB listeners: %w", err)
}
actual.Listeners = []*NetworkLoadBalancerListener{}
actual.TargetGroups = []*TargetGroup{}
for _, l := range response.Listeners {
actualListener := &NetworkLoadBalancerListener{}
actualListener.Port = int(aws.Int64Value(l.Port))
if len(l.Certificates) != 0 {
actualListener.SSLCertificateID = aws.StringValue(l.Certificates[0].CertificateArn) // What if there is more then one certificate, can we just grab the default certificate? we don't set it as default, we only set the one.
if l.SslPolicy != nil {
actualListener.SSLPolicy = aws.StringValue(l.SslPolicy)
}
}
for _, l := range listeners {
// This will need to be rearranged when we recognized multiple listeners and target groups per NLB
if len(l.DefaultActions) > 0 {
targetGroupARN := l.DefaultActions[0].TargetGroupArn
@ -356,11 +291,8 @@ func (e *NetworkLoadBalancer) Find(c *fi.CloudupContext) (*NetworkLoadBalancer,
if len(descResp.TargetGroups) != 1 {
return nil, fmt.Errorf("unexpected DescribeTargetGroups response: %v", descResp)
}
actualListener.TargetGroupName = aws.StringValue(descResp.TargetGroups[0].TargetGroupName)
}
}
actual.Listeners = append(actual.Listeners, actualListener)
}
sort.Stable(OrderTargetGroupsByName(actual.TargetGroups))
@ -443,6 +375,9 @@ func (e *NetworkLoadBalancer) Find(c *fi.CloudupContext) (*NetworkLoadBalancer,
actual.WellKnownServices = e.WellKnownServices
actual.Lifecycle = e.Lifecycle
// Store for other tasks
e.loadBalancerArn = aws.StringValue(lb.LoadBalancerArn)
klog.V(4).Infof("Found NLB %+v", actual)
return actual, nil
@ -496,7 +431,6 @@ func (e *NetworkLoadBalancer) Run(c *fi.CloudupContext) error {
func (e *NetworkLoadBalancer) Normalize(c *fi.CloudupContext) error {
// We need to sort our arrays consistently, so we don't get spurious changes
sort.Stable(OrderSubnetMappingsByName(e.SubnetMappings))
sort.Stable(OrderListenersByPort(e.Listeners))
sort.Stable(OrderTargetGroupsByName(e.TargetGroups))
e.IpAddressType = fi.PtrTo("dualstack")
@ -569,10 +503,6 @@ func (_ *NetworkLoadBalancer) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Ne
var loadBalancerName string
var loadBalancerArn string
if len(e.Listeners) != len(e.TargetGroups) {
return fmt.Errorf("nlb listeners and target groups do not match: %v listeners vs %v target groups", len(e.Listeners), len(e.TargetGroups))
}
if a == nil {
if e.LoadBalancerName == nil {
return fi.RequiredField("LoadBalancerName")
@ -619,7 +549,8 @@ func (_ *NetworkLoadBalancer) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Ne
e.DNSName = lb.DNSName
e.HostedZoneId = lb.CanonicalHostedZoneId
e.VPC = &VPC{ID: lb.VpcId}
loadBalancerArn = fi.ValueOf(lb.LoadBalancerArn)
loadBalancerArn = aws.StringValue(lb.LoadBalancerArn)
e.loadBalancerArn = loadBalancerArn
}
if e.waitForLoadBalancerReady {
@ -634,20 +565,6 @@ func (_ *NetworkLoadBalancer) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Ne
}
}
{
for _, listener := range e.Listeners {
createListenerInput, err := listener.mapToAWS(e.TargetGroups, loadBalancerArn)
if err != nil {
return err
}
klog.V(2).Infof("Creating Listener for NLB with port %v", listener.Port)
_, err = t.Cloud.ELBV2().CreateListener(createListenerInput)
if err != nil {
return fmt.Errorf("error creating listener for NLB: %v", err)
}
}
}
} else {
loadBalancerName = fi.ValueOf(a.LoadBalancerName)
@ -720,43 +637,6 @@ func (_ *NetworkLoadBalancer) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Ne
}
}
if changes.Listeners != nil {
if lb != nil {
request := &elbv2.DescribeListenersInput{
LoadBalancerArn: lb.LoadBalancerArn,
}
response, err := t.Cloud.ELBV2().DescribeListeners(request)
if err != nil {
return fmt.Errorf("error querying for NLB listeners :%v", err)
}
for _, l := range response.Listeners {
// delete the listener before recreating it
_, err := t.Cloud.ELBV2().DeleteListener(&elbv2.DeleteListenerInput{
ListenerArn: l.ListenerArn,
})
if err != nil {
return fmt.Errorf("error deleting load balancer listener with arn = : %v : %v", l.ListenerArn, err)
}
}
}
for _, listener := range changes.Listeners {
awsListener, err := listener.mapToAWS(e.TargetGroups, loadBalancerArn)
if err != nil {
return err
}
klog.V(2).Infof("Creating Listener for NLB with port %v", listener.Port)
_, err = t.Cloud.ELBV2().CreateListener(awsListener)
if err != nil {
return fmt.Errorf("error creating NLB listener: %v", err)
}
}
}
if err := t.AddELBV2Tags(loadBalancerArn, e.Tags); err != nil {
return err
}
@ -792,20 +672,6 @@ type terraformNetworkLoadBalancerSubnetMapping struct {
PrivateIPv4Address *string `cty:"private_ipv4_address"`
}
type terraformNetworkLoadBalancerListener struct {
LoadBalancer *terraformWriter.Literal `cty:"load_balancer_arn"`
Port int64 `cty:"port"`
Protocol string `cty:"protocol"`
CertificateARN *string `cty:"certificate_arn"`
SSLPolicy *string `cty:"ssl_policy"`
DefaultAction []terraformNetworkLoadBalancerListenerAction `cty:"default_action"`
}
type terraformNetworkLoadBalancerListenerAction struct {
Type string `cty:"type"`
TargetGroupARN *terraformWriter.Literal `cty:"target_group_arn"`
}
func (_ *NetworkLoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *NetworkLoadBalancer) error {
nlbTF := &terraformNetworkLoadBalancer{
Name: *e.LoadBalancerName,
@ -839,48 +705,11 @@ func (_ *NetworkLoadBalancer) RenderTerraform(t *terraform.TerraformTarget, a, e
}
}
err := t.RenderResource("aws_lb", *e.Name, nlbTF)
err := t.RenderResource("aws_lb", e.TerraformName(), nlbTF)
if err != nil {
return err
}
for _, listener := range e.Listeners {
var listenerTG *TargetGroup
for _, tg := range e.TargetGroups {
if aws.StringValue(tg.Name) == listener.TargetGroupName {
listenerTG = tg
break
}
}
if listenerTG == nil {
return fmt.Errorf("target group not found for NLB listener %+v", e)
}
listenerTF := &terraformNetworkLoadBalancerListener{
LoadBalancer: e.TerraformLink(),
Port: int64(listener.Port),
DefaultAction: []terraformNetworkLoadBalancerListenerAction{
{
Type: elbv2.ActionTypeEnumForward,
TargetGroupARN: listenerTG.TerraformLink(),
},
},
}
if listener.SSLCertificateID != "" {
listenerTF.CertificateARN = &listener.SSLCertificateID
listenerTF.Protocol = elbv2.ProtocolEnumTls
if listener.SSLPolicy != "" {
listenerTF.SSLPolicy = &listener.SSLPolicy
}
} else {
listenerTF.Protocol = elbv2.ProtocolEnumTcp
}
err = t.RenderResource("aws_lb_listener", fmt.Sprintf("%v-%v", e.TerraformName(), listener.Port), listenerTF)
if err != nil {
return err
}
}
return nil
}
@ -894,7 +723,7 @@ func (e *NetworkLoadBalancer) TerraformLink(params ...string) *terraformWriter.L
if len(params) > 0 {
prop = params[0]
}
return terraformWriter.LiteralProperty("aws_lb", *e.Name, prop)
return terraformWriter.LiteralProperty("aws_lb", e.TerraformName(), prop)
}
// FindDeletions schedules deletion of the corresponding legacy classic load balancer when it no longer has targets.

View File

@ -0,0 +1,261 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package awstasks
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elbv2"
"k8s.io/klog/v2"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
"k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter"
)
// +kops:fitask
type NetworkLoadBalancerListener struct {
Name *string
Lifecycle fi.Lifecycle
NetworkLoadBalancer *NetworkLoadBalancer
Port int
TargetGroup *TargetGroup
SSLCertificateID string
SSLPolicy string
listenerArn string
}
var _ fi.CompareWithID = &NetworkLoadBalancerListener{}
var _ fi.CloudupTaskNormalize = &NetworkLoadBalancerListener{}
func (e *NetworkLoadBalancerListener) CompareWithID() *string {
return e.Name
}
func (e *NetworkLoadBalancerListener) Find(c *fi.CloudupContext) (*NetworkLoadBalancerListener, error) {
ctx := c.Context()
cloud := c.T.Cloud.(awsup.AWSCloud)
if e.NetworkLoadBalancer == nil {
return nil, fi.RequiredField("NetworkLoadBalancer")
}
loadBalancerArn := e.NetworkLoadBalancer.loadBalancerArn
if loadBalancerArn == "" {
return nil, nil
}
var l *elbv2.Listener
{
request := &elbv2.DescribeListenersInput{
LoadBalancerArn: &loadBalancerArn,
}
// TODO: Move to lbInfo?
var allListeners []*elbv2.Listener
if err := cloud.ELBV2().DescribeListenersPagesWithContext(ctx, request, func(page *elbv2.DescribeListenersOutput, lastPage bool) bool {
allListeners = append(allListeners, page.Listeners...)
return true
}); err != nil {
return nil, fmt.Errorf("error querying for NLB listeners :%v", err)
}
var matches []*elbv2.Listener
for _, listener := range allListeners {
if aws.Int64Value(listener.Port) == int64(e.Port) {
matches = append(matches, listener)
}
}
if len(matches) == 0 {
return nil, nil
}
if len(matches) > 1 {
return nil, fmt.Errorf("found multiple listeners matching %+v", e)
}
l = matches[0]
}
actual := &NetworkLoadBalancerListener{}
actual.listenerArn = aws.StringValue(l.ListenerArn)
actual.Port = int(aws.Int64Value(l.Port))
if len(l.Certificates) != 0 {
actual.SSLCertificateID = aws.StringValue(l.Certificates[0].CertificateArn) // What if there is more then one certificate, can we just grab the default certificate? we don't set it as default, we only set the one.
if l.SslPolicy != nil {
actual.SSLPolicy = aws.StringValue(l.SslPolicy)
}
}
// This will need to be rearranged when we recognized multiple listeners and target groups per NLB
if len(l.DefaultActions) > 0 {
targetGroupARN := l.DefaultActions[0].TargetGroupArn
if targetGroupARN != nil {
actual.TargetGroup = &TargetGroup{
ARN: targetGroupARN,
}
}
}
_ = actual.Normalize(c)
actual.Lifecycle = e.Lifecycle
// Avoid spurious changes
actual.Name = e.Name
actual.NetworkLoadBalancer = e.NetworkLoadBalancer
klog.V(4).Infof("Found NLB listener %+v", actual)
return actual, nil
}
func (e *NetworkLoadBalancerListener) Run(c *fi.CloudupContext) error {
return fi.CloudupDefaultDeltaRunMethod(e, c)
}
func (e *NetworkLoadBalancerListener) Normalize(c *fi.CloudupContext) error {
return nil
}
func (*NetworkLoadBalancerListener) CheckChanges(a, e, changes *NetworkLoadBalancerListener) error {
return nil
}
func (*NetworkLoadBalancerListener) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *NetworkLoadBalancerListener) error {
ctx := context.TODO()
if e.NetworkLoadBalancer == nil {
return fi.RequiredField("NetworkLoadBalancer")
}
loadBalancerArn := e.NetworkLoadBalancer.loadBalancerArn
if loadBalancerArn == "" {
return fmt.Errorf("load balancer not yet created (arn not set)")
}
if a != nil {
// TODO: Can we do better here?
klog.Warningf("deleting ELB listener %q for required changes (%+v)", a.listenerArn, changes)
// delete the listener before recreating it
_, err := t.Cloud.ELBV2().DeleteListenerWithContext(ctx, &elbv2.DeleteListenerInput{
ListenerArn: &a.listenerArn,
})
if err != nil {
return fmt.Errorf("error deleting load balancer listener with arn=%q: %w", e.listenerArn, err)
}
a = nil
}
if a == nil {
if e.TargetGroup == nil {
return fi.RequiredField("TargetGroup")
}
targetGroupARN := fi.ValueOf(e.TargetGroup.ARN)
if targetGroupARN == "" {
return fmt.Errorf("target group not yet created (arn not set)")
}
request := &elbv2.CreateListenerInput{
DefaultActions: []*elbv2.Action{
{
TargetGroupArn: aws.String(targetGroupARN),
Type: aws.String(elbv2.ActionTypeEnumForward),
},
},
LoadBalancerArn: aws.String(loadBalancerArn),
Port: aws.Int64(int64(e.Port)),
}
if e.SSLCertificateID != "" {
request.Certificates = []*elbv2.Certificate{}
request.Certificates = append(request.Certificates, &elbv2.Certificate{
CertificateArn: aws.String(e.SSLCertificateID),
})
request.Protocol = aws.String(elbv2.ProtocolEnumTls)
if e.SSLPolicy != "" {
request.SslPolicy = aws.String(e.SSLPolicy)
}
} else {
request.Protocol = aws.String(elbv2.ProtocolEnumTcp)
}
klog.V(2).Infof("Creating Listener for NLB with port %v", e.Port)
_, err := t.Cloud.ELBV2().CreateListenerWithContext(ctx, request)
if err != nil {
return fmt.Errorf("creating listener for NLB on port %v: %w", e.Port, err)
}
}
// TODO: Tags on the listener?
return nil
}
type terraformNetworkLoadBalancerListener struct {
LoadBalancer *terraformWriter.Literal `cty:"load_balancer_arn"`
Port int64 `cty:"port"`
Protocol string `cty:"protocol"`
CertificateARN *string `cty:"certificate_arn"`
SSLPolicy *string `cty:"ssl_policy"`
DefaultAction []terraformNetworkLoadBalancerListenerAction `cty:"default_action"`
}
type terraformNetworkLoadBalancerListenerAction struct {
Type string `cty:"type"`
TargetGroupARN *terraformWriter.Literal `cty:"target_group_arn"`
}
func (_ *NetworkLoadBalancerListener) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *NetworkLoadBalancerListener) error {
if e.TargetGroup == nil {
return fi.RequiredField("TargetGroup")
}
listenerTF := &terraformNetworkLoadBalancerListener{
LoadBalancer: e.NetworkLoadBalancer.TerraformLink(),
Port: int64(e.Port),
DefaultAction: []terraformNetworkLoadBalancerListenerAction{
{
Type: elbv2.ActionTypeEnumForward,
TargetGroupARN: e.TargetGroup.TerraformLink(),
},
},
}
if e.SSLCertificateID != "" {
listenerTF.CertificateARN = &e.SSLCertificateID
listenerTF.Protocol = elbv2.ProtocolEnumTls
if e.SSLPolicy != "" {
listenerTF.SSLPolicy = &e.SSLPolicy
}
} else {
listenerTF.Protocol = elbv2.ProtocolEnumTcp
}
err := t.RenderResource("aws_lb_listener", e.TerraformName(), listenerTF)
if err != nil {
return err
}
return nil
}
func (e *NetworkLoadBalancerListener) TerraformName() string {
tfName := fmt.Sprintf("%v-%v", e.NetworkLoadBalancer.TerraformName(), e.Port)
return tfName
}

View File

@ -0,0 +1,52 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by fitask. DO NOT EDIT.
package awstasks
import (
"k8s.io/kops/upup/pkg/fi"
)
// NetworkLoadBalancerListener
var _ fi.HasLifecycle = &NetworkLoadBalancerListener{}
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
func (o *NetworkLoadBalancerListener) GetLifecycle() fi.Lifecycle {
return o.Lifecycle
}
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
func (o *NetworkLoadBalancerListener) SetLifecycle(lifecycle fi.Lifecycle) {
o.Lifecycle = lifecycle
}
var _ fi.HasName = &NetworkLoadBalancerListener{}
// GetName returns the Name of the object, implementing fi.HasName
func (o *NetworkLoadBalancerListener) GetName() *string {
return o.Name
}
// String is the stringer function for the task, producing readable output using fi.TaskAsString
func (o *NetworkLoadBalancerListener) String() string {
return fi.CloudupTaskAsString(o)
}