Skip to content

Commit 77d65e1

Browse files
[feat]: Add custom prefix for load balancer (#414)
* [feat]: Add custom prefix for load balancer * Update function for naming loadbalacer * Add limit error for prefix * Add dash after prefix * Change name of flag to nodebalacer-prefix * Add flag to helm chart * Remove hardocoded string * Fix tests * Update NodeBalancerPrefixCharLimit * Replace assert to require * Cover empty string case * Prefix validation * Update tests --------- Co-authored-by: Rahul Sharma <[email protected]>
1 parent a5bcaf2 commit 77d65e1

File tree

7 files changed

+80
-3
lines changed

7 files changed

+80
-3
lines changed

cloud/linode/cloud.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"os"
99
"strconv"
1010
"time"
11+
"regexp"
1112

1213
"github.com/spf13/pflag"
1314
"golang.org/x/exp/slices"
@@ -58,6 +59,7 @@ var Options struct {
5859
ClusterCIDRIPv4 string
5960
NodeCIDRMaskSizeIPv4 int
6061
NodeCIDRMaskSizeIPv6 int
62+
NodeBalancerPrefix string
6163
}
6264

6365
type linodeCloud struct {
@@ -69,8 +71,9 @@ type linodeCloud struct {
6971
}
7072

7173
var (
72-
instanceCache *instances
73-
ipHolderCharLimit int = 23
74+
instanceCache *instances
75+
ipHolderCharLimit int = 23
76+
NodeBalancerPrefixCharLimit int = 19
7477
)
7578

7679
func init() {
@@ -193,6 +196,19 @@ func newCloud() (cloudprovider.Interface, error) {
193196
return nil, fmt.Errorf("%s", msg)
194197
}
195198

199+
if len(Options.NodeBalancerPrefix) > NodeBalancerPrefixCharLimit {
200+
msg := fmt.Sprintf("nodebalancer-prefix must be %d characters or less: %s is %d characters\n", NodeBalancerPrefixCharLimit, Options.NodeBalancerPrefix, len(Options.NodeBalancerPrefix))
201+
klog.Error(msg)
202+
return nil, fmt.Errorf("%s", msg)
203+
}
204+
205+
validPrefix := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
206+
if !validPrefix.MatchString(Options.NodeBalancerPrefix) {
207+
msg := fmt.Sprintf("nodebalancer-prefix must be no empty and use only letters, numbers, underscores, and dashes: %s\n", Options.NodeBalancerPrefix)
208+
klog.Error(msg)
209+
return nil, fmt.Errorf("%s", msg)
210+
}
211+
196212
// create struct that satisfies cloudprovider.Interface
197213
lcloud := &linodeCloud{
198214
client: linodeClient,

cloud/linode/cloud_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func TestNewCloudRouteControllerDisabled(t *testing.T) {
2020
t.Setenv("LINODE_API_TOKEN", "dummyapitoken")
2121
t.Setenv("LINODE_REGION", "us-east")
2222
t.Setenv("LINODE_REQUEST_TIMEOUT_SECONDS", "10")
23+
Options.NodeBalancerPrefix = "ccm"
2324

2425
t.Run("should not fail if vpc is empty and routecontroller is disabled", func(t *testing.T) {
2526
Options.VPCName = ""
@@ -45,6 +46,7 @@ func TestNewCloud(t *testing.T) {
4546
t.Setenv("LINODE_REQUEST_TIMEOUT_SECONDS", "10")
4647
t.Setenv("LINODE_ROUTES_CACHE_TTL_SECONDS", "60")
4748
Options.LinodeGoDebug = true
49+
Options.NodeBalancerPrefix = "ccm"
4850

4951
t.Run("should fail if api token is empty", func(t *testing.T) {
5052
t.Setenv("LINODE_API_TOKEN", "")
@@ -126,6 +128,57 @@ func TestNewCloud(t *testing.T) {
126128
_, err := newCloud()
127129
assert.Error(t, err, "expected error if ipholdersuffix is longer than 23 chars")
128130
})
131+
132+
t.Run("should fail if nodebalancer-prefix is longer than 19 chars", func(t *testing.T) {
133+
prefix := Options.NodeBalancerPrefix
134+
rtEnabled := Options.EnableRouteController
135+
Options.EnableRouteController = false
136+
Options.LoadBalancerType = "nodebalancer"
137+
Options.NodeBalancerPrefix = strings.Repeat("a", 21)
138+
defer func() {
139+
Options.NodeBalancerPrefix = prefix
140+
Options.LoadBalancerType = ""
141+
Options.EnableRouteController = rtEnabled
142+
}()
143+
_, err := newCloud()
144+
t.Log(err)
145+
require.Error(t, err, "expected error if nodebalancer-prefix is longer than 19 chars")
146+
require.ErrorContains(t, err, "nodebalancer-prefix")
147+
})
148+
149+
t.Run("should fail if nodebalancer-prefix is empty", func(t *testing.T) {
150+
prefix := Options.NodeBalancerPrefix
151+
rtEnabled := Options.EnableRouteController
152+
Options.EnableRouteController = false
153+
Options.LoadBalancerType = "nodebalancer"
154+
Options.NodeBalancerPrefix = ""
155+
defer func() {
156+
Options.NodeBalancerPrefix = prefix
157+
Options.LoadBalancerType = ""
158+
Options.EnableRouteController = rtEnabled
159+
}()
160+
_, err := newCloud()
161+
t.Log(err)
162+
require.Error(t, err, "expected error if nodebalancer-prefix is empty")
163+
require.ErrorContains(t, err, "nodebalancer-prefix must be no empty")
164+
})
165+
166+
t.Run("should fail if not validated nodebalancer-prefix", func(t *testing.T) {
167+
prefix := Options.NodeBalancerPrefix
168+
rtEnabled := Options.EnableRouteController
169+
Options.EnableRouteController = false
170+
Options.LoadBalancerType = "nodebalancer"
171+
Options.NodeBalancerPrefix = "\\+x"
172+
defer func() {
173+
Options.NodeBalancerPrefix = prefix
174+
Options.LoadBalancerType = ""
175+
Options.EnableRouteController = rtEnabled
176+
}()
177+
_, err := newCloud()
178+
t.Log(err)
179+
require.Error(t, err, "expected error if not validated nodebalancer-prefix")
180+
require.ErrorContains(t, err, "nodebalancer-prefix must be no empty and use only letters, numbers, underscores, and dashes")
181+
})
129182
}
130183

131184
func Test_linodeCloud_LoadBalancer(t *testing.T) {

cloud/linode/loadbalancers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func (l *loadbalancers) cleanupOldNodeBalancer(ctx context.Context, service *v1.
227227
// GetLoadBalancer will not modify service.
228228
func (l *loadbalancers) GetLoadBalancerName(_ context.Context, _ string, _ *v1.Service) string {
229229
unixNano := strconv.FormatInt(time.Now().UnixNano(), 16)
230-
return fmt.Sprintf("ccm-%s", unixNano[len(unixNano)-12:])
230+
return fmt.Sprintf("%s-%s", Options.NodeBalancerPrefix, unixNano[len(unixNano)-12:])
231231
}
232232

233233
// GetLoadBalancer returns the *v1.LoadBalancerStatus of service.

deploy/chart/templates/daemonset.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ spec:
156156
{{- if .Values.nodeBalancerBackendIPv4Subnet }}
157157
- --nodebalancer-backend-ipv4-subnet={{ .Values.nodeBalancerBackendIPv4Subnet }}
158158
{{- end }}
159+
{{- if .Values.nodeBalancerPrefix }}
160+
- --nodebalancer-prefix={{ .Values.nodeBalancerPrefix }}
161+
{{- end }}
159162
{{- if .Values.extraArgs }}
160163
{{- toYaml .Values.extraArgs | nindent 12 }}
161164
{{- end }}

deploy/chart/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ tolerations:
116116
# nodeBalancerBackendIPv4SubnetName is the subnet name to use for the backend ips of the NodeBalancer
117117
# nodeBalancerBackendIPv4SubnetName: ""
118118

119+
# nodeBalancerPrefix is used to add prefix for nodeBalancer name. Default is "ccm"
120+
# nodeBalancerPrefix: ""
121+
119122
# This section adds the ability to pass environment variables to adjust CCM defaults
120123
# https://github.com/linode/linode-cloud-controller-manager/blob/master/cloud/linode/loadbalancers.go
121124
# LINODE_HOSTNAME_ONLY_INGRESS type bool is supported

docs/configuration/environment.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ The CCM supports the following flags:
5353
| `--enable-ipv6-for-loadbalancers` | `false` | Set both IPv4 and IPv6 addresses for all LoadBalancer services (when disabled, only IPv4 is used). This can also be configured per-service using the `service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress` annotation. |
5454
| `--node-cidr-mask-size-ipv4` | `24` | ipv4 cidr mask size for pod cidrs allocated to nodes |
5555
| `--node-cidr-mask-size-ipv6` | `64` | ipv6 cidr mask size for pod cidrs allocated to nodes |
56+
| `--nodebalancer-prefix` | `ccm` | Name prefix for NoadBalancers. |
5657

5758
## Configuration Methods
5859

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func main() {
9898
command.Flags().IntVar(&linode.Options.NodeBalancerBackendIPv4SubnetID, "nodebalancer-backend-ipv4-subnet-id", 0, "ipv4 subnet id to use for NodeBalancer backends")
9999
command.Flags().StringVar(&linode.Options.NodeBalancerBackendIPv4SubnetName, "nodebalancer-backend-ipv4-subnet-name", "", "ipv4 subnet name to use for NodeBalancer backends")
100100
command.Flags().BoolVar(&linode.Options.DisableNodeBalancerVPCBackends, "disable-nodebalancer-vpc-backends", false, "disables nodebalancer backends in VPCs (when enabled, nodebalancers will only have private IPs as backends for backward compatibility)")
101+
command.Flags().StringVar(&linode.Options.NodeBalancerPrefix, "nodebalancer-prefix", "ccm", fmt.Sprintf("Name prefix for NoadBalancers. (max. %v char.)", linode.NodeBalancerPrefixCharLimit))
101102

102103
// Set static flags
103104
command.Flags().VisitAll(func(fl *pflag.Flag) {

0 commit comments

Comments
 (0)