Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cloud/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const (
AnnLinodeCloudFirewallID = "service.beta.kubernetes.io/linode-loadbalancer-firewall-id"
AnnLinodeCloudFirewallACL = "service.beta.kubernetes.io/linode-loadbalancer-firewall-acl"

// AnnLinodeEnableIPv6Ingress is the annotation used to specify that a service should include both IPv4 and IPv6
// addresses for its LoadBalancer ingress. When set to "true", both addresses will be included in the status.
AnnLinodeEnableIPv6Ingress = "service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress"

AnnLinodeNodePrivateIP = "node.k8s.linode.com/private-ip"
AnnLinodeHostUUID = "node.k8s.linode.com/host-uuid"

Expand Down
21 changes: 11 additions & 10 deletions cloud/linode/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,17 @@ var Options struct {
EnableRouteController bool
EnableTokenHealthChecker bool
// Deprecated: use VPCNames instead
VPCName string
VPCNames string
SubnetNames string
LoadBalancerType string
BGPNodeSelector string
IpHolderSuffix string
LinodeExternalNetwork *net.IPNet
NodeBalancerTags []string
DefaultNBType string
GlobalStopChannel chan<- struct{}
VPCName string
VPCNames string
SubnetNames string
LoadBalancerType string
BGPNodeSelector string
IpHolderSuffix string
LinodeExternalNetwork *net.IPNet
NodeBalancerTags []string
DefaultNBType string
GlobalStopChannel chan<- struct{}
EnableIPv6ForLoadBalancers bool
}

type linodeCloud struct {
Expand Down
41 changes: 36 additions & 5 deletions cloud/linode/loadbalancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1052,13 +1052,44 @@ func makeLoadBalancerStatus(service *v1.Service, nb *linodego.NodeBalancer) *v1.
ingress := v1.LoadBalancerIngress{
Hostname: *nb.Hostname,
}
if !getServiceBoolAnnotation(service, annotations.AnnLinodeHostnameOnlyIngress) {
if val := envBoolOptions("LINODE_HOSTNAME_ONLY_INGRESS"); val {
klog.Infof("LINODE_HOSTNAME_ONLY_INGRESS: (%v)", val)
} else {
ingress.IP = *nb.IPv4

// Return hostname-only if annotation is set or environment variable is set
if getServiceBoolAnnotation(service, annotations.AnnLinodeHostnameOnlyIngress) {
return &v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{ingress},
}
}

if val := envBoolOptions("LINODE_HOSTNAME_ONLY_INGRESS"); val {
klog.Infof("LINODE_HOSTNAME_ONLY_INGRESS: (%v)", val)
return &v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{ingress},
}
}

// Check for per-service IPv6 annotation first, then fall back to global setting
useIPv6 := getServiceBoolAnnotation(service, annotations.AnnLinodeEnableIPv6Ingress) || Options.EnableIPv6ForLoadBalancers

// When IPv6 is enabled (either per-service or globally), include both IPv4 and IPv6
if useIPv6 && nb.IPv6 != nil && *nb.IPv6 != "" {
ingresses := []v1.LoadBalancerIngress{
{
Hostname: *nb.Hostname,
IP: *nb.IPv4,
},
{
Hostname: *nb.Hostname,
IP: *nb.IPv6,
},
}
klog.V(4).Infof("Using both IPv4 and IPv6 addresses for NodeBalancer (%d): %s, %s", nb.ID, *nb.IPv4, *nb.IPv6)
return &v1.LoadBalancerStatus{
Ingress: ingresses,
}
}

// Default case - just use IPv4
ingress.IP = *nb.IPv4
return &v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{ingress},
}
Expand Down
69 changes: 69 additions & 0 deletions cloud/linode/loadbalancers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ func TestCCMLoadBalancers(t *testing.T) {
name: "makeLoadBalancerStatus",
f: testMakeLoadBalancerStatus,
},
{
name: "makeLoadBalancerStatusWithIPv6",
f: testMakeLoadBalancerStatusWithIPv6,
},
{
name: "makeLoadBalancerStatusEnvVar",
f: testMakeLoadBalancerStatusEnvVar,
Expand Down Expand Up @@ -3191,6 +3195,71 @@ func testMakeLoadBalancerStatus(t *testing.T, client *linodego.Client, _ *fakeAP
}
}

func testMakeLoadBalancerStatusWithIPv6(t *testing.T, client *linodego.Client, _ *fakeAPI) {
ipv4 := "192.168.0.1"
ipv6 := "2600:3c00::f03c:91ff:fe24:3a2f"
hostname := "nb-192-168-0-1.newark.nodebalancer.linode.com"
nb := &linodego.NodeBalancer{
IPv4: &ipv4,
IPv6: &ipv6,
Hostname: &hostname,
}

svc := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Annotations: make(map[string]string, 1),
},
}

// Test with EnableIPv6ForLoadBalancers = false (default)
Options.EnableIPv6ForLoadBalancers = false
expectedStatus := &v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{{
Hostname: hostname,
IP: ipv4,
}},
}
status := makeLoadBalancerStatus(svc, nb)
if !reflect.DeepEqual(status, expectedStatus) {
t.Errorf("expected status with EnableIPv6ForLoadBalancers=false to be %#v; got %#v", expectedStatus, status)
}

// Test with EnableIPv6ForLoadBalancers = true
Options.EnableIPv6ForLoadBalancers = true
expectedStatus = &v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{
Hostname: hostname,
IP: ipv4,
},
{
Hostname: hostname,
IP: ipv6,
},
},
}
status = makeLoadBalancerStatus(svc, nb)
if !reflect.DeepEqual(status, expectedStatus) {
t.Errorf("expected status with EnableIPv6ForLoadBalancers=true to be %#v; got %#v", expectedStatus, status)
}

// Test with per-service annotation
// Reset the global flag to false and set the annotation
Options.EnableIPv6ForLoadBalancers = false
svc.Annotations[annotations.AnnLinodeEnableIPv6Ingress] = "true"

// Expect the same result as when the global flag is enabled
status = makeLoadBalancerStatus(svc, nb)
if !reflect.DeepEqual(status, expectedStatus) {
t.Errorf("expected status with %s=true annotation to be %#v; got %#v",
annotations.AnnLinodeEnableIPv6Ingress, expectedStatus, status)
}

// Reset the flag to its default value
Options.EnableIPv6ForLoadBalancers = false
}

func testMakeLoadBalancerStatusEnvVar(t *testing.T, client *linodego.Client, _ *fakeAPI) {
ipv4 := "192.168.0.1"
hostname := "nb-192-168-0-1.newark.nodebalancer.linode.com"
Expand Down
3 changes: 3 additions & 0 deletions deploy/chart/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ spec:
{{- if .Values.defaultNBType }}
- --default-nodebalancer-type={{ .Values.defaultNBType }}
{{- end }}
{{- if .Values.enableIPv6ForLoadBalancers }}
- --enable-ipv6-for-loadbalancers={{ .Values.enableIPv6ForLoadBalancers }}
{{- end }}
{{- with .Values.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
Expand Down
5 changes: 5 additions & 0 deletions deploy/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ tolerations:
# Default NodeBalancer type to create("common" or "premium"). Default is "common"
# defaultNBType: "common"

# Enable IPv6 ingress addresses for LoadBalancer services
# When enabled, both IPv4 and IPv6 addresses will be included in the LoadBalancer status for all services
# This can also be controlled per-service using the "service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress" annotation
# enableIPv6ForLoadBalancers: true

# This section adds the ability to pass environment variables to adjust CCM defaults
# https://github.com/linode/linode-cloud-controller-manager/blob/master/cloud/linode/loadbalancers.go
# LINODE_HOSTNAME_ONLY_INGRESS type bool is supported
Expand Down
5 changes: 3 additions & 2 deletions docs/configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ The Linode Cloud Controller Manager (CCM) offers extensive configuration options
- Node controller behavior
- [See node management](nodes.md#node-controller-behavior)

4. **[Environment Variables](environment.md)**
4. **[Environment Variables and Flags](environment.md)**
- Cache settings
- API configuration
- Network settings
- BGP configuration
- [See environment reference](environment.md#available-variables)
- IPv6 configuration
- [See configuration reference](environment.md#flags)

5. **[Firewall Setup](firewall.md)**
- CCM-managed firewalls
Expand Down
8 changes: 8 additions & 0 deletions docs/configuration/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ For implementation details, see:
| `firewall-id` | string | | An existing Cloud Firewall ID to be attached to the NodeBalancer instance. See [Firewall Setup](firewall.md) |
| `firewall-acl` | string | | The Firewall rules to be applied to the NodeBalancer. See [Firewall Configuration](#firewall-configuration) |
| `nodebalancer-type` | string | | The type of NodeBalancer to create (options: common, premium). See [NodeBalancer Types](#nodebalancer-type) |
| `enable-ipv6-ingress` | bool | `false` | When `true`, both IPv4 and IPv6 addresses will be included in the LoadBalancerStatus ingress |
| `backend-ipv4-range` | string | | The IPv4 range from VPC subnet to be applied to the NodeBalancer backend. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `backend-vpc-name` | string | | VPC which is connected to the NodeBalancer backend. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `backend-subnet-name` | string | | Subnet within VPC which is connected to the NodeBalancer backend. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
Expand Down Expand Up @@ -124,6 +125,13 @@ metadata:
service.beta.kubernetes.io/linode-loadbalancer-subnet-name: "subnet1"
```

### Service with IPv6 Address
```yaml
metadata:
annotations:
service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress: "true"
```

For more examples and detailed configuration options, see:
- [LoadBalancer Configuration](loadbalancer.md)
- [Firewall Configuration](firewall.md)
Expand Down
30 changes: 27 additions & 3 deletions docs/configuration/environment.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Environment Variables
# Environment Variables and Flags

## Overview

Environment variables provide global configuration options for the CCM. These settings affect caching, API behavior, and networking configurations.
The CCM can be configured using environment variables and flags. Environment variables provide global configuration options, while flags control specific features.

## Available Variables
## Environment Variables

### Cache Configuration

Expand All @@ -28,6 +28,24 @@ Environment variables provide global configuration options for the CCM. These se
| `BGP_CUSTOM_ID_MAP` | "" | Use your own map instead of default region map for BGP |
| `BGP_PEER_PREFIX` | `2600:3c0f` | Use your own BGP peer prefix instead of default one |

## Flags

The CCM supports the following flags:

| Flag | Default | Description |
|------|---------|-------------|
| `--linodego-debug` | `false` | Enables debug output for the LinodeAPI wrapper |
| `--enable-route-controller` | `false` | Enables route_controller for CCM |
| `--enable-token-health-checker` | `false` | Enables Linode API token health checker |
| `--vpc-names` | `""` | Comma separated VPC names whose routes will be managed by route-controller |
| `--subnet-names` | `""` | Comma separated subnet names whose routes will be managed by route-controller (requires vpc-names flag) |
| `--load-balancer-type` | `nodebalancer` | Configures which type of load-balancing to use (options: nodebalancer, cilium-bgp) |
| `--bgp-node-selector` | `""` | Node selector to use to perform shared IP fail-over with BGP |
| `--ip-holder-suffix` | `""` | Suffix to append to the IP holder name when using shared IP fail-over with BGP |
| `--default-nodebalancer-type` | `common` | Default type of NodeBalancer to create (options: common, premium) |
| `--nodebalancer-tags` | `[]` | Linode tags to apply to all NodeBalancers |
| `--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. |

## Configuration Methods

### Helm Chart
Expand All @@ -36,6 +54,9 @@ Configure via `values.yaml`:
env:
- name: LINODE_INSTANCE_CACHE_TTL
value: "30"
args:
- --enable-ipv6-for-loadbalancers
- --enable-route-controller
```

### Manual Deployment
Expand All @@ -49,6 +70,9 @@ spec:
env:
- name: LINODE_INSTANCE_CACHE_TTL
value: "30"
args:
- --enable-ipv6-for-loadbalancers
- --enable-route-controller
```

## Usage Guidelines
Expand Down
28 changes: 27 additions & 1 deletion docs/configuration/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,32 @@ When using NodeBalancers, the CCM automatically:

For more details, see [Linode NodeBalancer Documentation](https://www.linode.com/docs/products/networking/nodebalancers/).

### IPv6 Support

NodeBalancers support both IPv4 and IPv6 ingress addresses. By default, the CCM uses only IPv4 address for LoadBalancer services.

You can enable IPv6 addresses globally for all services by setting the `enable-ipv6-for-loadbalancers` flag:

```yaml
spec:
template:
spec:
containers:
- name: ccm-linode
args:
- --enable-ipv6-for-loadbalancers=true
```

Alternatively, you can enable IPv6 addresses for individual services using the annotation:

```yaml
metadata:
annotations:
service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress: "true"
```

When IPv6 is enabled (either globally or per-service), both IPv4 and IPv6 addresses will be included in the service's LoadBalancer status.

### Basic Configuration

Create a LoadBalancer service:
Expand Down Expand Up @@ -219,7 +245,7 @@ metadata:
- [Service Annotations](annotations.md)
- [Firewall Configuration](firewall.md)
- [Session Affinity](session-affinity.md)
- [Environment Variables](environment.md)
- [Environment Variables and Flags](environment.md)
- [Route Configuration](routes.md)
- [Linode NodeBalancer Documentation](https://www.linode.com/docs/products/networking/nodebalancers/)
- [Cilium BGP Documentation](https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane/)
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func main() {
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().StringVar(&linode.Options.DefaultNBType, "default-nodebalancer-type", string(linodego.NBTypeCommon), "default type of NodeBalancer to create (options: common, premium)")
command.Flags().StringSliceVar(&linode.Options.NodeBalancerTags, "nodebalancer-tags", []string{}, "Linode tags to apply to all NodeBalancers")
command.Flags().BoolVar(&linode.Options.EnableIPv6ForLoadBalancers, "enable-ipv6-for-loadbalancers", false, "set both IPv4 and IPv6 addresses for all LoadBalancer services (when disabled, only IPv4 is used)")

// Set static flags
command.Flags().VisitAll(func(fl *pflag.Flag) {
Expand Down
Loading