Skip to content

Commit

Permalink
Merge pull request #138 from schinmai-akamai/master
Browse files Browse the repository at this point in the history
Support adding tags to nodebalancers
  • Loading branch information
tchinmai7 authored Nov 8, 2023
2 parents aec305f + f512c95 commit 1e53829
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Annotation (Suffix) | Values | Default | Description
`preserve` | [bool](#annotation-bool-values) | `false` | When `true`, deleting a `LoadBalancer` service does not delete the underlying NodeBalancer. This will also prevent deletion of the former LoadBalancer when another one is specified with the `nodebalancer-id` annotation.
`nodebalancer-id` | string | | The ID of the NodeBalancer to front the service. When not specified, a new NodeBalancer will be created. This can be configured on service creation or patching
`hostname-only-ingress` | [bool](#annotation-bool-values) | `false` | When `true`, the LoadBalancerStatus for the service will only contain the Hostname. This is useful for bypassing kube-proxy's rerouting of in-cluster requests originally intended for the external LoadBalancer to the service's constituent pod IPs.
`tags` | string | | A comma seperated list of tags to be applied to the createad NodeBalancer instance

#### Deprecated Annotations

Expand Down
4 changes: 4 additions & 0 deletions cloud/linode/fake_linode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ func (f *fakeAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Region: nbco.Region,
IPv4: &ip,
Hostname: &hostname,
Tags: nbco.Tags,
}

if nbco.ClientConnThrottle != nil {
Expand Down Expand Up @@ -561,6 +562,9 @@ func (f *fakeAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if nbuo.Label != nil {
nb.Label = nbuo.Label
}
if nbuo.Tags != nil {
nb.Tags = *nbuo.Tags
}

f.nb[strconv.Itoa(nb.ID)] = nb
resp, err := json.Marshal(nb)
Expand Down
23 changes: 23 additions & 0 deletions cloud/linode/loadbalancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"os"
"reflect"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -47,6 +48,7 @@ const (
annLinodeNodeBalancerID = "service.beta.kubernetes.io/linode-loadbalancer-nodebalancer-id"

annLinodeHostnameOnlyIngress = "service.beta.kubernetes.io/linode-loadbalancer-hostname-only-ingress"
annLinodeLoadBalancerTags = "service.beta.kubernetes.io/linode-loadbalancer-tags"
)

type lbNotFoundError struct {
Expand Down Expand Up @@ -266,6 +268,17 @@ func (l *loadbalancers) updateNodeBalancer(ctx context.Context, service *v1.Serv
}
}

tags := l.getLoadbalancerTags(ctx, service)
if !reflect.DeepEqual(nb.Tags, tags) {
update := nb.GetUpdateOptions()
update.Tags = &tags
nb, err = l.client.UpdateNodeBalancer(ctx, nb.ID, update)
if err != nil {
sentry.CaptureError(ctx, err)
return err
}
}

// Get all of the NodeBalancer's configs
nbCfgs, err := l.client.ListNodeBalancerConfigs(ctx, nb.ID, nil)
if err != nil {
Expand Down Expand Up @@ -480,15 +493,25 @@ func (l *loadbalancers) getNodeBalancerByID(ctx context.Context, service *v1.Ser
return nb, nil
}

func (l *loadbalancers) getLoadbalancerTags(ctx context.Context, service *v1.Service) []string {
tagStr, ok := getServiceAnnotation(service, annLinodeLoadBalancerTags)
if ok {
return strings.Split(tagStr, ",")
}
return []string{}
}

func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName string, service *v1.Service, configs []*linodego.NodeBalancerConfigCreateOptions) (lb *linodego.NodeBalancer, err error) {
connThrottle := getConnectionThrottle(service)

label := l.GetLoadBalancerName(ctx, clusterName, service)
tags := l.getLoadbalancerTags(ctx, service)
createOpts := linodego.NodeBalancerCreateOptions{
Label: &label,
Region: l.zone,
ClientConnThrottle: &connThrottle,
Configs: configs,
Tags: tags,
}
return l.client.CreateNodeBalancer(ctx, createOpts)
}
Expand Down
83 changes: 82 additions & 1 deletion cloud/linode/loadbalancers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"reflect"
"strconv"
"strings"
"testing"

"github.com/linode/linodego"
Expand Down Expand Up @@ -127,6 +128,10 @@ func TestCCMLoadBalancers(t *testing.T) {
name: "Update Load Balancer - Add TLS Port",
f: testUpdateLoadBalancerAddTLSPort,
},
{
name: "Update Load Balancer - Add Tags",
f: testUpdateLoadBalancerAddTags,
},
{
name: "Update Load Balancer - Specify NodeBalancerID",
f: testUpdateLoadBalancerAddNodeBalancerID,
Expand Down Expand Up @@ -201,7 +206,8 @@ func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI) {
Name: randString(10),
UID: "foobar123",
Annotations: map[string]string{
annLinodeThrottle: "15",
annLinodeThrottle: "15",
annLinodeLoadBalancerTags: "fake,test,yolo",
},
},
Spec: v1.ServiceSpec{
Expand Down Expand Up @@ -264,6 +270,13 @@ func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI) {
t.Logf("actual: %v", nb.ClientConnThrottle)
}

expectedTags := []string{"fake", "test", "yolo"}
if !reflect.DeepEqual(nb.Tags, expectedTags) {
t.Error("unexpected Tags")
t.Logf("expected: %v", expectedTags)
t.Logf("actual: %v", nb.Tags)
}

defer func() { _ = lb.EnsureLoadBalancerDeleted(context.TODO(), "linodelb", svc) }()
}

Expand Down Expand Up @@ -415,6 +428,74 @@ func testUpdateLoadBalancerAddPortAnnotation(t *testing.T, client *linodego.Clie
}
}

func testUpdateLoadBalancerAddTags(t *testing.T, client *linodego.Client, _ *fakeAPI) {
svc := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: randString(10),
UID: "foobar123",
Annotations: map[string]string{},
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
Name: randString(10),
Protocol: "TCP",
Port: int32(80),
NodePort: int32(30000),
},
},
},
}

nodes := []*v1.Node{
&v1.Node{
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{
Type: v1.NodeInternalIP,
Address: "127.0.0.1",
},
},
},
},
}

lb := &loadbalancers{client, "us-west", nil}
fakeClientset := fake.NewSimpleClientset()
lb.kubeClient = fakeClientset

defer lb.EnsureLoadBalancerDeleted(context.TODO(), "linodelb", svc)

lbStatus, err := lb.EnsureLoadBalancer(context.TODO(), "linodelb", svc, nodes)
if err != nil {
t.Errorf("EnsureLoadBalancer returned an error: %s", err)
}
svc.Status.LoadBalancer = *lbStatus
stubService(fakeClientset, svc)

testTags := "test,new,tags"
svc.ObjectMeta.SetAnnotations(map[string]string{
annLinodeLoadBalancerTags: testTags,
})

err = lb.UpdateLoadBalancer(context.TODO(), "linodelb", svc, nodes)
if err != nil {
t.Fatalf("UpdateLoadBalancer returned an error while updated annotations: %s", err)
}

nb, err := lb.getNodeBalancerByStatus(context.TODO(), svc)
if err != nil {
t.Fatalf("failed to get NodeBalancer by status: %v", err)
}

expectedTags := strings.Split(testTags, ",")
observedTags := nb.Tags

if !reflect.DeepEqual(expectedTags, observedTags) {
t.Errorf("NodeBalancer tags mismatch: expected %v, got %v", expectedTags, observedTags)
}
}

func testUpdateLoadBalancerAddTLSPort(t *testing.T, client *linodego.Client, _ *fakeAPI) {
svc := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Expand Down

0 comments on commit 1e53829

Please sign in to comment.