Skip to content

Commit

Permalink
Allow specifying Private IP by annotation for VLAN / VPC support
Browse files Browse the repository at this point in the history
  • Loading branch information
glennpratt committed Nov 28, 2023
1 parent fc4ad36 commit b4dc7c8
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 6 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ Kubernetes Services of type `LoadBalancer` will be served through a [Linode Node

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

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.
All of the Service annotation names listed below have been shortened for readability. The values, such as `http`, are case-sensitive.

Each *Service* annotation **MUST** be prefixed with:<br />
**`service.beta.kubernetes.io/linode-loadbalancer-`**

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

#### Example usage
### Nodes

Kubernetes Nodes can be configured with the following annotations.

Each *Node* annotation **MUST** be prefixed with:<br />
**`node.k8s.linode.com/`**

Key | Values | Default | Description
---|---|---|---
`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.


[required for NodeBalancers]: https://www.linode.com/docs/api/nodebalancers/#nodebalancer-create__request-body-schema
[VLAN]: https://www.linode.com/products/vlan/
[VPC]: https://www.linode.com/blog/linode/new-betas-coming-to-green-light/

### Example usage

```yaml
kind: Service
Expand Down
14 changes: 12 additions & 2 deletions cloud/linode/loadbalancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ const (

annLinodeHostnameOnlyIngress = "service.beta.kubernetes.io/linode-loadbalancer-hostname-only-ingress"
annLinodeLoadBalancerTags = "service.beta.kubernetes.io/linode-loadbalancer-tags"

annLinodeNodePrivateIP = "node.k8s.linode.com/private-ip"
)

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

func (l *loadbalancers) buildNodeBalancerNodeCreateOptions(node *v1.Node, nodePort int32) linodego.NodeBalancerNodeCreateOptions {
return linodego.NodeBalancerNodeCreateOptions{
Address: fmt.Sprintf("%v:%v", getNodeInternalIP(node), nodePort),
Address: fmt.Sprintf("%v:%v", getNodePrivateIP(node), nodePort),
Label: node.Name,
Mode: "accept",
Weight: 100,
Expand Down Expand Up @@ -758,7 +760,15 @@ func getPortConfigAnnotation(service *v1.Service, port int) (portConfigAnnotatio
return annotation, nil
}

func getNodeInternalIP(node *v1.Node) string {
// getNodePrivateIP should provide the Linode Private IP the NodeBalance
// will communicate with. When using a VLAN or VPC for the Kubernetes cluster
// network, this will not be the NodeInternalIP, so this prefers an annotation
// cluster operators may specify in such a situation.
func getNodePrivateIP(node *v1.Node) string {
if address, exists := node.Annotations[annLinodeNodePrivateIP]; exists {
return address
}

for _, addr := range node.Status.Addresses {
if addr.Type == v1.NodeInternalIP {
return addr.Address
Expand Down
23 changes: 21 additions & 2 deletions cloud/linode/loadbalancers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,7 @@ func Test_getHealthCheckType(t *testing.T) {
}
}

func Test_getNodeInternalIP(t *testing.T) {
func Test_getNodePrivateIP(t *testing.T) {
testcases := []struct {
name string
node *v1.Node
Expand Down Expand Up @@ -1146,11 +1146,30 @@ func Test_getNodeInternalIP(t *testing.T) {
},
"",
},
{
"node internal ip annotation present",
&v1.Node{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
annLinodeNodePrivateIP: "192.168.42.42",
},
},
Status: v1.NodeStatus{
Addresses: []v1.NodeAddress{
{
Type: v1.NodeInternalIP,
Address: "10.0.1.1",
},
},
},
},
"192.168.42.42",
},
}

for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
ip := getNodeInternalIP(test.node)
ip := getNodePrivateIP(test.node)
if ip != test.address {
t.Error("unexpected certificate")
t.Logf("expected: %q", test.address)
Expand Down

0 comments on commit b4dc7c8

Please sign in to comment.