Skip to content

Commit 1e53829

Browse files
authored
Merge pull request #138 from schinmai-akamai/master
Support adding tags to nodebalancers
2 parents aec305f + f512c95 commit 1e53829

4 files changed

Lines changed: 110 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Annotation (Suffix) | Values | Default | Description
5656
`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.
5757
`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
5858
`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.
59+
`tags` | string | | A comma seperated list of tags to be applied to the createad NodeBalancer instance
5960

6061
#### Deprecated Annotations
6162

cloud/linode/fake_linode_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ func (f *fakeAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
251251
Region: nbco.Region,
252252
IPv4: &ip,
253253
Hostname: &hostname,
254+
Tags: nbco.Tags,
254255
}
255256

256257
if nbco.ClientConnThrottle != nil {
@@ -561,6 +562,9 @@ func (f *fakeAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
561562
if nbuo.Label != nil {
562563
nb.Label = nbuo.Label
563564
}
565+
if nbuo.Tags != nil {
566+
nb.Tags = *nbuo.Tags
567+
}
564568

565569
f.nb[strconv.Itoa(nb.ID)] = nb
566570
resp, err := json.Marshal(nb)

cloud/linode/loadbalancers.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"net/http"
88
"os"
9+
"reflect"
910
"strconv"
1011
"strings"
1112
"time"
@@ -47,6 +48,7 @@ const (
4748
annLinodeNodeBalancerID = "service.beta.kubernetes.io/linode-loadbalancer-nodebalancer-id"
4849

4950
annLinodeHostnameOnlyIngress = "service.beta.kubernetes.io/linode-loadbalancer-hostname-only-ingress"
51+
annLinodeLoadBalancerTags = "service.beta.kubernetes.io/linode-loadbalancer-tags"
5052
)
5153

5254
type lbNotFoundError struct {
@@ -266,6 +268,17 @@ func (l *loadbalancers) updateNodeBalancer(ctx context.Context, service *v1.Serv
266268
}
267269
}
268270

271+
tags := l.getLoadbalancerTags(ctx, service)
272+
if !reflect.DeepEqual(nb.Tags, tags) {
273+
update := nb.GetUpdateOptions()
274+
update.Tags = &tags
275+
nb, err = l.client.UpdateNodeBalancer(ctx, nb.ID, update)
276+
if err != nil {
277+
sentry.CaptureError(ctx, err)
278+
return err
279+
}
280+
}
281+
269282
// Get all of the NodeBalancer's configs
270283
nbCfgs, err := l.client.ListNodeBalancerConfigs(ctx, nb.ID, nil)
271284
if err != nil {
@@ -480,15 +493,25 @@ func (l *loadbalancers) getNodeBalancerByID(ctx context.Context, service *v1.Ser
480493
return nb, nil
481494
}
482495

496+
func (l *loadbalancers) getLoadbalancerTags(ctx context.Context, service *v1.Service) []string {
497+
tagStr, ok := getServiceAnnotation(service, annLinodeLoadBalancerTags)
498+
if ok {
499+
return strings.Split(tagStr, ",")
500+
}
501+
return []string{}
502+
}
503+
483504
func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName string, service *v1.Service, configs []*linodego.NodeBalancerConfigCreateOptions) (lb *linodego.NodeBalancer, err error) {
484505
connThrottle := getConnectionThrottle(service)
485506

486507
label := l.GetLoadBalancerName(ctx, clusterName, service)
508+
tags := l.getLoadbalancerTags(ctx, service)
487509
createOpts := linodego.NodeBalancerCreateOptions{
488510
Label: &label,
489511
Region: l.zone,
490512
ClientConnThrottle: &connThrottle,
491513
Configs: configs,
514+
Tags: tags,
492515
}
493516
return l.client.CreateNodeBalancer(ctx, createOpts)
494517
}

cloud/linode/loadbalancers_test.go

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"reflect"
1010
"strconv"
11+
"strings"
1112
"testing"
1213

1314
"github.com/linode/linodego"
@@ -127,6 +128,10 @@ func TestCCMLoadBalancers(t *testing.T) {
127128
name: "Update Load Balancer - Add TLS Port",
128129
f: testUpdateLoadBalancerAddTLSPort,
129130
},
131+
{
132+
name: "Update Load Balancer - Add Tags",
133+
f: testUpdateLoadBalancerAddTags,
134+
},
130135
{
131136
name: "Update Load Balancer - Specify NodeBalancerID",
132137
f: testUpdateLoadBalancerAddNodeBalancerID,
@@ -201,7 +206,8 @@ func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI) {
201206
Name: randString(10),
202207
UID: "foobar123",
203208
Annotations: map[string]string{
204-
annLinodeThrottle: "15",
209+
annLinodeThrottle: "15",
210+
annLinodeLoadBalancerTags: "fake,test,yolo",
205211
},
206212
},
207213
Spec: v1.ServiceSpec{
@@ -264,6 +270,13 @@ func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI) {
264270
t.Logf("actual: %v", nb.ClientConnThrottle)
265271
}
266272

273+
expectedTags := []string{"fake", "test", "yolo"}
274+
if !reflect.DeepEqual(nb.Tags, expectedTags) {
275+
t.Error("unexpected Tags")
276+
t.Logf("expected: %v", expectedTags)
277+
t.Logf("actual: %v", nb.Tags)
278+
}
279+
267280
defer func() { _ = lb.EnsureLoadBalancerDeleted(context.TODO(), "linodelb", svc) }()
268281
}
269282

@@ -415,6 +428,74 @@ func testUpdateLoadBalancerAddPortAnnotation(t *testing.T, client *linodego.Clie
415428
}
416429
}
417430

431+
func testUpdateLoadBalancerAddTags(t *testing.T, client *linodego.Client, _ *fakeAPI) {
432+
svc := &v1.Service{
433+
ObjectMeta: metav1.ObjectMeta{
434+
Name: randString(10),
435+
UID: "foobar123",
436+
Annotations: map[string]string{},
437+
},
438+
Spec: v1.ServiceSpec{
439+
Ports: []v1.ServicePort{
440+
{
441+
Name: randString(10),
442+
Protocol: "TCP",
443+
Port: int32(80),
444+
NodePort: int32(30000),
445+
},
446+
},
447+
},
448+
}
449+
450+
nodes := []*v1.Node{
451+
&v1.Node{
452+
Status: v1.NodeStatus{
453+
Addresses: []v1.NodeAddress{
454+
{
455+
Type: v1.NodeInternalIP,
456+
Address: "127.0.0.1",
457+
},
458+
},
459+
},
460+
},
461+
}
462+
463+
lb := &loadbalancers{client, "us-west", nil}
464+
fakeClientset := fake.NewSimpleClientset()
465+
lb.kubeClient = fakeClientset
466+
467+
defer lb.EnsureLoadBalancerDeleted(context.TODO(), "linodelb", svc)
468+
469+
lbStatus, err := lb.EnsureLoadBalancer(context.TODO(), "linodelb", svc, nodes)
470+
if err != nil {
471+
t.Errorf("EnsureLoadBalancer returned an error: %s", err)
472+
}
473+
svc.Status.LoadBalancer = *lbStatus
474+
stubService(fakeClientset, svc)
475+
476+
testTags := "test,new,tags"
477+
svc.ObjectMeta.SetAnnotations(map[string]string{
478+
annLinodeLoadBalancerTags: testTags,
479+
})
480+
481+
err = lb.UpdateLoadBalancer(context.TODO(), "linodelb", svc, nodes)
482+
if err != nil {
483+
t.Fatalf("UpdateLoadBalancer returned an error while updated annotations: %s", err)
484+
}
485+
486+
nb, err := lb.getNodeBalancerByStatus(context.TODO(), svc)
487+
if err != nil {
488+
t.Fatalf("failed to get NodeBalancer by status: %v", err)
489+
}
490+
491+
expectedTags := strings.Split(testTags, ",")
492+
observedTags := nb.Tags
493+
494+
if !reflect.DeepEqual(expectedTags, observedTags) {
495+
t.Errorf("NodeBalancer tags mismatch: expected %v, got %v", expectedTags, observedTags)
496+
}
497+
}
498+
418499
func testUpdateLoadBalancerAddTLSPort(t *testing.T, client *linodego.Client, _ *fakeAPI) {
419500
svc := &v1.Service{
420501
ObjectMeta: metav1.ObjectMeta{

0 commit comments

Comments
 (0)