diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index 4d398579..ed9b21ed 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -42,6 +42,7 @@ var Options struct { BGPNodeSelector string IpHolderSuffix string LinodeExternalNetwork *net.IPNet + NodeBalancerTags []string } type linodeCloud struct { diff --git a/cloud/linode/loadbalancers.go b/cloud/linode/loadbalancers.go index e9c80f3d..eb80aad8 100644 --- a/cloud/linode/loadbalancers.go +++ b/cloud/linode/loadbalancers.go @@ -612,6 +612,8 @@ func (l *loadbalancers) GetLoadBalancerTags(_ context.Context, clusterName strin tags = append(tags, clusterName) } + tags = append(tags, Options.NodeBalancerTags...) + tagStr, ok := service.GetAnnotations()[annotations.AnnLinodeLoadBalancerTags] if ok { return append(tags, strings.Split(tagStr, ",")...) diff --git a/cloud/linode/loadbalancers_test.go b/cloud/linode/loadbalancers_test.go index 3140c5b0..2cce0575 100644 --- a/cloud/linode/loadbalancers_test.go +++ b/cloud/linode/loadbalancers_test.go @@ -150,6 +150,10 @@ func TestCCMLoadBalancers(t *testing.T) { name: "Create Load Balancer With Invalid Firewall ACL - NO Allow Or Deny", f: testCreateNodeBalanceWithNoAllowOrDenyList, }, + { + name: "Create Load Balancer With Global Tags set", + f: testCreateNodeBalancerWithGlobalTags, + }, { name: "Update Load Balancer - Add Node", f: testUpdateLoadBalancerAddNode, @@ -274,7 +278,7 @@ func stubService(fake *fake.Clientset, service *v1.Service) { _, _ = fake.CoreV1().Services("").Create(context.TODO(), service, metav1.CreateOptions{}) } -func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI, annMap map[string]string) error { +func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI, annMap map[string]string, expectedTags []string) error { svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: randString(), @@ -341,7 +345,9 @@ func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI, a t.Logf("actual: %v", nb.ClientConnThrottle) } - expectedTags := []string{"linodelb", "fake", "test", "yolo"} + if len(expectedTags) == 0 { + expectedTags = []string{"linodelb", "fake", "test", "yolo"} + } if !reflect.DeepEqual(nb.Tags, expectedTags) { t.Error("unexpected Tags") t.Logf("expected: %v", expectedTags) @@ -366,7 +372,7 @@ func testCreateNodeBalancer(t *testing.T, client *linodego.Client, _ *fakeAPI, a } func testCreateNodeBalancerWithOutFirewall(t *testing.T, client *linodego.Client, f *fakeAPI) { - err := testCreateNodeBalancer(t, client, f, nil) + err := testCreateNodeBalancer(t, client, f, nil, nil) if err != nil { t.Fatalf("expected a nil error, got %v", err) } @@ -377,7 +383,7 @@ func testCreateNodeBalanceWithNoAllowOrDenyList(t *testing.T, client *linodego.C annotations.AnnLinodeCloudFirewallACL: `{}`, } - err := testCreateNodeBalancer(t, client, f, annotations) + err := testCreateNodeBalancer(t, client, f, annotations, nil) if err == nil || !stderrors.Is(err, firewall.ErrInvalidFWConfig) { t.Fatalf("expected a %v error, got %v", firewall.ErrInvalidFWConfig, err) } @@ -395,7 +401,7 @@ func testCreateNodeBalanceWithBothAllowOrDenyList(t *testing.T, client *linodego }`, } - err := testCreateNodeBalancer(t, client, f, annotations) + err := testCreateNodeBalancer(t, client, f, annotations, nil) if err == nil || !stderrors.Is(err, firewall.ErrInvalidFWConfig) { t.Fatalf("expected a %v error, got %v", firewall.ErrInvalidFWConfig, err) } @@ -410,7 +416,7 @@ func testCreateNodeBalancerWithAllowList(t *testing.T, client *linodego.Client, }`, } - err := testCreateNodeBalancer(t, client, f, annotations) + err := testCreateNodeBalancer(t, client, f, annotations, nil) if err != nil { t.Fatalf("expected a non-nil error, got %v", err) } @@ -425,7 +431,7 @@ func testCreateNodeBalancerWithDenyList(t *testing.T, client *linodego.Client, f }`, } - err := testCreateNodeBalancer(t, client, f, annotations) + err := testCreateNodeBalancer(t, client, f, annotations, nil) if err != nil { t.Fatalf("expected a non-nil error, got %v", err) } @@ -435,7 +441,7 @@ func testCreateNodeBalancerWithFirewall(t *testing.T, client *linodego.Client, f annotations := map[string]string{ annotations.AnnLinodeCloudFirewallID: "123", } - err := testCreateNodeBalancer(t, client, f, annotations) + err := testCreateNodeBalancer(t, client, f, annotations, nil) if err != nil { t.Fatalf("expected a nil error, got %v", err) } @@ -446,12 +452,25 @@ func testCreateNodeBalancerWithInvalidFirewall(t *testing.T, client *linodego.Cl annotations.AnnLinodeCloudFirewallID: "qwerty", } expectedError := "strconv.Atoi: parsing \"qwerty\": invalid syntax" - err := testCreateNodeBalancer(t, client, f, annotations) + err := testCreateNodeBalancer(t, client, f, annotations, nil) if err.Error() != expectedError { t.Fatalf("expected a %s error, got %v", expectedError, err) } } +func testCreateNodeBalancerWithGlobalTags(t *testing.T, client *linodego.Client, f *fakeAPI) { + original := Options.NodeBalancerTags + defer func() { + Options.NodeBalancerTags = original + }() + Options.NodeBalancerTags = []string{"foobar"} + expectedTags := []string{"linodelb", "foobar", "fake", "test", "yolo"} + err := testCreateNodeBalancer(t, client, f, nil, expectedTags) + if err != nil { + t.Fatalf("expected a nil error, got %v", err) + } +} + func testUpdateLoadBalancerAddNode(t *testing.T, client *linodego.Client, f *fakeAPI) { svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ diff --git a/deploy/chart/templates/daemonset.yaml b/deploy/chart/templates/daemonset.yaml index fb839bfa..68176b74 100644 --- a/deploy/chart/templates/daemonset.yaml +++ b/deploy/chart/templates/daemonset.yaml @@ -65,6 +65,9 @@ spec: {{- end}} - --load-balancer-type={{ required "A valid .Values.sharedIPLoadBalancing.loadBalancerType is required for shared IP load-balancing" .Values.sharedIPLoadBalancing.loadBalancerType }} {{- end }} + {{- if .Values.nodeBalancerTags }} + - --nodebalancer-tags={{ join " " .Values.nodeBalancerTags }} + {{- end }} volumeMounts: - mountPath: /etc/kubernetes name: k8s diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index 3d5feb0b..c8e296bd 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -64,6 +64,9 @@ env: # - name: EXAMPLE_ENV_VAR # value: "true" +# Linode tags to apply to all NodeBalancers +nodeBalancerTags: [] + # This section adds the ability to pass volumes to the CCM DaemonSet volumes: # - name: test-volume diff --git a/main.go b/main.go index 53b5b2ab..593755c8 100644 --- a/main.go +++ b/main.go @@ -86,6 +86,7 @@ func main() { command.Flags().StringVar(&linode.Options.LoadBalancerType, "load-balancer-type", "nodebalancer", "configures which type of load-balancing to use for LoadBalancer Services (options: nodebalancer, cilium-bgp)") command.Flags().StringVar(&linode.Options.BGPNodeSelector, "bgp-node-selector", "", "node selector to use to perform shared IP fail-over with BGP (e.g. cilium-bgp-peering=true") command.Flags().StringVar(&linode.Options.IpHolderSuffix, "ip-holder-suffix", "", "suffix to append to the ip holder name when using shared IP fail-over with BGP (e.g. ip-holder-suffix=my-cluster-name") + command.Flags().StringSliceVar(&linode.Options.NodeBalancerTags, "nodebalancer-tags", []string{}, "Linode tags to apply to all NodeBalancers") // Set static flags command.Flags().VisitAll(func(fl *pflag.Flag) {