Skip to content

Commit a6aae91

Browse files
author
Rahul Sharma
committed
enable adding ipv6 slaac as annotation
1 parent b0bf52f commit a6aae91

File tree

8 files changed

+103
-6
lines changed

8 files changed

+103
-6
lines changed

cloud/annotations/annotations.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ const (
3838
// addresses for its LoadBalancer ingress. When set to "true", both addresses will be included in the status.
3939
AnnLinodeEnableIPv6Ingress = "service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress"
4040

41-
AnnLinodeNodePrivateIP = "node.k8s.linode.com/private-ip"
42-
AnnLinodeHostUUID = "node.k8s.linode.com/host-uuid"
41+
AnnLinodeNodePrivateIP = "node.k8s.linode.com/private-ip"
42+
AnnLinodeHostUUID = "node.k8s.linode.com/host-uuid"
43+
AnnLinodeNodePublicIPv6SLAAC = "node.k8s.linode.com/public-ipv6-slaac"
4344

4445
AnnLinodeNodeIPSharingUpdated = "node.k8s.linode.com/ip-sharing-updated"
4546
AnnExcludeNodeFromNb = "node.k8s.linode.com/exclude-from-nb"

cloud/linode/cloud.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ var Options struct {
6161
NodeCIDRMaskSizeIPv4 int
6262
NodeCIDRMaskSizeIPv6 int
6363
NodeBalancerPrefix string
64+
AddPublicIPv6SLAACAnnotation bool
6465
}
6566

6667
type linodeCloud struct {

cloud/linode/node_controller.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,14 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error {
270270
lastUpdate := s.LastMetadataUpdate(node.Name)
271271

272272
uuid, foundLabel := node.Labels[annotations.AnnLinodeHostUUID]
273-
configuredPrivateIP, foundAnnotation := node.Annotations[annotations.AnnLinodeNodePrivateIP]
273+
configuredPrivateIP, foundPrivateIPAnnotation := node.Annotations[annotations.AnnLinodeNodePrivateIP]
274+
configuredPublicIPv6SLAAC, foundIPv6Annotation := "", true
275+
if Options.AddPublicIPv6SLAACAnnotation {
276+
configuredPublicIPv6SLAAC, foundIPv6Annotation = node.Annotations[annotations.AnnLinodeNodePublicIPv6SLAAC]
277+
}
274278

275279
metaAge := time.Since(lastUpdate)
276-
if foundLabel && foundAnnotation && metaAge < s.ttl {
280+
if foundLabel && foundPrivateIPAnnotation && foundIPv6Annotation && metaAge < s.ttl {
277281
klog.V(3).InfoS("Skipping refresh, ttl not reached",
278282
"node", klog.KObj(node),
279283
"ttl", s.ttl,
@@ -298,7 +302,12 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error {
298302
}
299303
}
300304

301-
if uuid == linode.HostUUID && node.Spec.ProviderID != "" && configuredPrivateIP == expectedPrivateIP {
305+
expectedPublicIPv6SLAAC := ""
306+
if Options.AddPublicIPv6SLAACAnnotation {
307+
expectedPublicIPv6SLAAC = linode.IPv6
308+
}
309+
310+
if uuid == linode.HostUUID && node.Spec.ProviderID != "" && configuredPrivateIP == expectedPrivateIP && configuredPublicIPv6SLAAC == expectedPublicIPv6SLAAC {
302311
s.SetLastMetadataUpdate(node.Name)
303312
return nil
304313
}
@@ -325,6 +334,9 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error {
325334
if nodeResult.Annotations[annotations.AnnLinodeNodePrivateIP] != expectedPrivateIP && expectedPrivateIP != "" {
326335
nodeResult.Annotations[annotations.AnnLinodeNodePrivateIP] = expectedPrivateIP
327336
}
337+
if nodeResult.Annotations[annotations.AnnLinodeNodePublicIPv6SLAAC] != expectedPublicIPv6SLAAC && expectedPublicIPv6SLAAC != "" {
338+
nodeResult.Annotations[annotations.AnnLinodeNodePublicIPv6SLAAC] = expectedPublicIPv6SLAAC
339+
}
328340
updatedNode, err = s.kubeclient.CoreV1().Nodes().Update(ctx, nodeResult, metav1.UpdateOptions{})
329341
return err
330342
}); err != nil {

cloud/linode/node_controller_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,80 @@ func TestNodeController_processNext(t *testing.T) {
197197
}
198198

199199
func TestNodeController_handleNode(t *testing.T) {
200+
// Mock dependencies
201+
ctrl := gomock.NewController(t)
202+
defer ctrl.Finish()
203+
client := mocks.NewMockClient(ctrl)
204+
kubeClient := fake.NewSimpleClientset()
205+
optionsIPv6SLAAC := Options.AddPublicIPv6SLAACAnnotation
206+
defer func() {
207+
Options.AddPublicIPv6SLAACAnnotation = optionsIPv6SLAAC
208+
}()
209+
Options.AddPublicIPv6SLAACAnnotation = true
210+
node := &v1.Node{
211+
ObjectMeta: metav1.ObjectMeta{
212+
Name: "test-node",
213+
Labels: map[string]string{},
214+
Annotations: map[string]string{},
215+
},
216+
Spec: v1.NodeSpec{ProviderID: "linode://123"},
217+
}
218+
_, err := kubeClient.CoreV1().Nodes().Create(t.Context(), node, metav1.CreateOptions{})
219+
require.NoError(t, err, "expected no error during node creation")
220+
221+
instCache := newInstances(client)
222+
223+
t.Setenv("LINODE_METADATA_TTL", "30")
224+
nodeCtrl := newNodeController(kubeClient, client, nil, instCache)
225+
assert.Equal(t, 30*time.Second, nodeCtrl.ttl, "expected ttl to be 30 seconds")
226+
227+
t.Setenv("K8S_NODECACHE_TTL", "60")
228+
currK8sNodeCache := newK8sNodeCache()
229+
assert.Equal(t, 60*time.Second, currK8sNodeCache.ttl, "expected ttl to be 60 seconds")
230+
231+
// Test: Successful metadata update
232+
publicIP := net.ParseIP("172.234.31.123")
233+
privateIP := net.ParseIP("192.168.159.135")
234+
publicIPv6SLAAC := "2001:db::f03c:91ff:fe2b:1a2b"
235+
client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{
236+
{ID: 123, Label: "test-node", IPv4: []*net.IP{&publicIP, &privateIP}, IPv6: publicIPv6SLAAC, HostUUID: "123"},
237+
}, nil)
238+
err = nodeCtrl.handleNode(t.Context(), node)
239+
require.NoError(t, err, "expected no error during handleNode")
240+
241+
// Check metadataLastUpdate
242+
lastUpdate := nodeCtrl.LastMetadataUpdate("test-node")
243+
if time.Since(lastUpdate) > 5*time.Second {
244+
t.Errorf("metadataLastUpdate was not updated correctly")
245+
}
246+
247+
// Annotations set, no update needed as ttl not reached
248+
node.Labels[annotations.AnnLinodeHostUUID] = "123"
249+
node.Annotations[annotations.AnnLinodeNodePrivateIP] = privateIP.String()
250+
node.Annotations[annotations.AnnLinodeNodePublicIPv6SLAAC] = publicIPv6SLAAC
251+
err = nodeCtrl.handleNode(t.Context(), node)
252+
require.NoError(t, err, "expected no error during handleNode")
253+
254+
// Lookup failure for linode instance
255+
client = mocks.NewMockClient(ctrl)
256+
nodeCtrl.instances = newInstances(client)
257+
nodeCtrl.metadataLastUpdate["test-node"] = time.Now().Add(-2 * nodeCtrl.ttl)
258+
client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, errors.New("lookup failed"))
259+
err = nodeCtrl.handleNode(t.Context(), node)
260+
require.Error(t, err, "expected error during handleNode, got nil")
261+
262+
// All fields already set
263+
client = mocks.NewMockClient(ctrl)
264+
nodeCtrl.instances = newInstances(client)
265+
nodeCtrl.metadataLastUpdate["test-node"] = time.Now().Add(-2 * nodeCtrl.ttl)
266+
client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{
267+
{ID: 123, Label: "test-node", IPv4: []*net.IP{&publicIP, &privateIP}, IPv6: publicIPv6SLAAC, HostUUID: "123"},
268+
}, nil)
269+
err = nodeCtrl.handleNode(t.Context(), node)
270+
assert.NoError(t, err, "expected no error during handleNode")
271+
}
272+
273+
func TestNodeController_handleNodeWithoutSLAAC(t *testing.T) {
200274
// Mock dependencies
201275
ctrl := gomock.NewController(t)
202276
defer ctrl.Finish()

deploy/chart/templates/daemonset.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ spec:
178178
{{- if .Values.nodeBalancerPrefix }}
179179
- --nodebalancer-prefix={{ .Values.nodeBalancerPrefix }}
180180
{{- end }}
181+
{{- with .Values.addPublicIPv6SLAACAnnotation }}
182+
- --add-public-ipv6-slaac-annotation={{ . }}
183+
{{- end }}
181184
{{- if .Values.extraArgs }}
182185
{{- toYaml .Values.extraArgs | nindent 12 }}
183186
{{- end }}

deploy/chart/values.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ tolerations:
124124
# nodeBalancerPrefix is used to add prefix for nodeBalancer name. Default is "ccm"
125125
# nodeBalancerPrefix: ""
126126

127+
# addPublicIPv6SLAACAnnotation is used to add public IPv6 SLAAC annotation to nodes.
128+
# When set to true, the node's public IPv6 SLAAC address will be added as an annotation to the node.
129+
# addPublicIPv6SLAACAnnotation: false
130+
127131
# This section adds the ability to pass environment variables to adjust CCM defaults
128132
# https://github.com/linode/linode-cloud-controller-manager/blob/master/cloud/linode/loadbalancers.go
129133
# LINODE_HOSTNAME_ONLY_INGRESS type bool is supported

docs/configuration/environment.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ The CCM supports the following flags:
5656
| `--node-cidr-mask-size-ipv4` | Int | `24` | ipv4 cidr mask size for pod cidrs allocated to nodes |
5757
| `--node-cidr-mask-size-ipv6` | Int | `64` | ipv6 cidr mask size for pod cidrs allocated to nodes |
5858
| `--nodebalancer-prefix` | String | `ccm` | Name prefix for NoadBalancers. |
59-
| `--disable-ipv6-node-cidr-allocation` | `false` | disables allocating IPv6 CIDR ranges to nodes when using CCM for node IPAM (set to `true` if IPv6 ranges are not configured on Linode interfaces) |
59+
| `--disable-ipv6-node-cidr-allocation` | Boolean | `false` | disables allocating IPv6 CIDR ranges to nodes when using CCM for node IPAM (set to `true` if IPv6 ranges are not configured on Linode interfaces) |
60+
| `--add-public-ipv6-slaac-annotation` | Boolean | `false` | adds public IPv6 SLAAC annotation to nodes (when enabled, the node's public IPv6 SLAAC address will be added as an annotation to the node) |
6061

6162
## Configuration Methods
6263

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func main() {
101101
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)")
102102
command.Flags().StringVar(&linode.Options.NodeBalancerPrefix, "nodebalancer-prefix", "ccm", fmt.Sprintf("Name prefix for NoadBalancers. (max. %v char.)", linode.NodeBalancerPrefixCharLimit))
103103
command.Flags().BoolVar(&linode.Options.DisableIPv6NodeCIDRAllocation, "disable-ipv6-node-cidr-allocation", false, "disables IPv6 node cidr allocation by ipam controller (when enabled, IPv6 cidr ranges will be allocated to nodes)")
104+
command.Flags().BoolVar(&linode.Options.AddPublicIPv6SLAACAnnotation, "add-public-ipv6-slaac-annotation", false, "adds public IPv6 SLAAC annotation to nodes (when enabled, the node's public IPv6 SLAAC address will be added as an annotation to the node)")
104105

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

0 commit comments

Comments
 (0)