Skip to content

Commit 4ced791

Browse files
authored
Allow specifying Private IP by annotation for VLAN / VPC support (#141)
1 parent fc4ad36 commit 4ced791

3 files changed

Lines changed: 54 additions & 6 deletions

File tree

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ Kubernetes Services of type `LoadBalancer` will be served through a [Linode Node
3838

3939
The Linode CCM accepts several annotations which affect the properties of the underlying NodeBalancer deployment.
4040

41-
All of the service annotation names listed below have been shortened for readability. Each annotation **MUST** be prefixed with `service.beta.kubernetes.io/linode-loadbalancer-`. The values, such as `http`, are case-sensitive.
41+
All of the Service annotation names listed below have been shortened for readability. The values, such as `http`, are case-sensitive.
42+
43+
Each *Service* annotation **MUST** be prefixed with:<br />
44+
**`service.beta.kubernetes.io/linode-loadbalancer-`**
4245

4346
Annotation (Suffix) | Values | Default | Description
4447
---|---|---|---
@@ -80,7 +83,23 @@ Key | Values | Default | Description
8083
`proxy-protocol` | `none`, `v1`, `v2` | `none` | Specifies whether to use a version of Proxy Protocol on the underlying NodeBalancer. Overwrites `default-proxy-protocol`.
8184
`tls-secret-name` | string | | Specifies a secret to use for TLS. The secret type should be `kubernetes.io/tls`.
8285

83-
#### Example usage
86+
### Nodes
87+
88+
Kubernetes Nodes can be configured with the following annotations.
89+
90+
Each *Node* annotation **MUST** be prefixed with:<br />
91+
**`node.k8s.linode.com/`**
92+
93+
Key | Values | Default | Description
94+
---|---|---|---
95+
`private-ip` | `IPv4` | `none` | Specifies the Linode Private IP overriding default detection of the Node InternalIP.<br />When using a [VLAN] or [VPC], the Node InternalIP may not be a Linode Private IP as [required for NodeBalancers] and should be specified.
96+
97+
98+
[required for NodeBalancers]: https://www.linode.com/docs/api/nodebalancers/#nodebalancer-create__request-body-schema
99+
[VLAN]: https://www.linode.com/products/vlan/
100+
[VPC]: https://www.linode.com/blog/linode/new-betas-coming-to-green-light/
101+
102+
### Example usage
84103

85104
```yaml
86105
kind: Service

cloud/linode/loadbalancers.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ const (
5050

5151
annLinodeHostnameOnlyIngress = "service.beta.kubernetes.io/linode-loadbalancer-hostname-only-ingress"
5252
annLinodeLoadBalancerTags = "service.beta.kubernetes.io/linode-loadbalancer-tags"
53+
54+
annLinodeNodePrivateIP = "node.k8s.linode.com/private-ip"
5355
)
5456

5557
var (
@@ -644,7 +646,7 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam
644646

645647
func (l *loadbalancers) buildNodeBalancerNodeCreateOptions(node *v1.Node, nodePort int32) linodego.NodeBalancerNodeCreateOptions {
646648
return linodego.NodeBalancerNodeCreateOptions{
647-
Address: fmt.Sprintf("%v:%v", getNodeInternalIP(node), nodePort),
649+
Address: fmt.Sprintf("%v:%v", getNodePrivateIP(node), nodePort),
648650
Label: node.Name,
649651
Mode: "accept",
650652
Weight: 100,
@@ -758,7 +760,15 @@ func getPortConfigAnnotation(service *v1.Service, port int) (portConfigAnnotatio
758760
return annotation, nil
759761
}
760762

761-
func getNodeInternalIP(node *v1.Node) string {
763+
// getNodePrivateIP should provide the Linode Private IP the NodeBalance
764+
// will communicate with. When using a VLAN or VPC for the Kubernetes cluster
765+
// network, this will not be the NodeInternalIP, so this prefers an annotation
766+
// cluster operators may specify in such a situation.
767+
func getNodePrivateIP(node *v1.Node) string {
768+
if address, exists := node.Annotations[annLinodeNodePrivateIP]; exists {
769+
return address
770+
}
771+
762772
for _, addr := range node.Status.Addresses {
763773
if addr.Type == v1.NodeInternalIP {
764774
return addr.Address

cloud/linode/loadbalancers_test.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,7 +1112,7 @@ func Test_getHealthCheckType(t *testing.T) {
11121112
}
11131113
}
11141114

1115-
func Test_getNodeInternalIP(t *testing.T) {
1115+
func Test_getNodePrivateIP(t *testing.T) {
11161116
testcases := []struct {
11171117
name string
11181118
node *v1.Node
@@ -1146,11 +1146,30 @@ func Test_getNodeInternalIP(t *testing.T) {
11461146
},
11471147
"",
11481148
},
1149+
{
1150+
"node internal ip annotation present",
1151+
&v1.Node{
1152+
ObjectMeta: metav1.ObjectMeta{
1153+
Annotations: map[string]string{
1154+
annLinodeNodePrivateIP: "192.168.42.42",
1155+
},
1156+
},
1157+
Status: v1.NodeStatus{
1158+
Addresses: []v1.NodeAddress{
1159+
{
1160+
Type: v1.NodeInternalIP,
1161+
Address: "10.0.1.1",
1162+
},
1163+
},
1164+
},
1165+
},
1166+
"192.168.42.42",
1167+
},
11491168
}
11501169

11511170
for _, test := range testcases {
11521171
t.Run(test.name, func(t *testing.T) {
1153-
ip := getNodeInternalIP(test.node)
1172+
ip := getNodePrivateIP(test.node)
11541173
if ip != test.address {
11551174
t.Error("unexpected certificate")
11561175
t.Logf("expected: %q", test.address)

0 commit comments

Comments
 (0)