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
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ linters:
- opinionated
- performance
- style
disabled-checks:
- commentedOutCode
settings:
captLocal:
paramsOnly: true
Expand Down
13 changes: 8 additions & 5 deletions cloud/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ const (
NodeBalancerBackendSubnetName = "service.beta.kubernetes.io/linode-loadbalancer-backend-subnet-name"
NodeBalancerBackendSubnetID = "service.beta.kubernetes.io/linode-loadbalancer-backend-subnet-id"

NodeBalancerFrontendIPv4Range = "service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv4-range"
NodeBalancerFrontendIPv6Range = "service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv6-range"
NodeBalancerFrontendVPCName = "service.beta.kubernetes.io/linode-loadbalancer-frontend-vpc-name"
NodeBalancerFrontendSubnetName = "service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-name"
NodeBalancerFrontendSubnetID = "service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-id"
// Uncomment once https://github.com/linode/linodego/pull/978 is merged and a release is cut
/*
NodeBalancerFrontendIPv4Range = "service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv4-range"
NodeBalancerFrontendIPv6Range = "service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv6-range"
NodeBalancerFrontendVPCName = "service.beta.kubernetes.io/linode-loadbalancer-frontend-vpc-name"
NodeBalancerFrontendSubnetName = "service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-name"
NodeBalancerFrontendSubnetID = "service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-id"
*/
)
6 changes: 3 additions & 3 deletions cloud/linode/fake_linode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,8 @@ func (f *fakeAPI) setupRoutes() {
}
f.nb[strconv.Itoa(nb.ID)] = &nb

for _, backendVPC := range nbco.BackendVPCs {
if backendVPC.IPv4Range == nil || *backendVPC.IPv4Range == "" {
for _, backendVPC := range nbco.VPCs {
if backendVPC.IPv4Range == "" {
continue
}
subnet, ok := f.subnet[backendVPC.SubnetID]
Expand All @@ -401,7 +401,7 @@ func (f *fakeAPI) setupRoutes() {
}
subnet.Nodebalancers = append(subnet.Nodebalancers, linodego.VPCSubnetNodebalancers{
ID: nb.ID,
Ipv4Range: *backendVPC.IPv4Range,
Ipv4Range: backendVPC.IPv4Range,
})
}

Expand Down
69 changes: 38 additions & 31 deletions cloud/linode/loadbalancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,17 +691,19 @@ func (l *loadbalancers) getNodeBalancerByIP(ctx context.Context, service *v1.Ser
return nil, lbNotFoundError{serviceNn: getServiceNn(service)}
}

// filter by subnet ID if specified for frontend vpc ip
frontendSubnetID := service.GetAnnotations()[annotations.NodeBalancerFrontendSubnetID]
if frontendSubnetID != "" {
for i := range lbs {
lb := &lbs[i]
if lb.FrontendAddressType == "vpc" && lb.FrontendVPCSubnetID != nil && strconv.Itoa(*lb.FrontendVPCSubnetID) == frontendSubnetID {
return lb, nil
// Uncomment once https://github.com/linode/linodego/pull/978 is merged and a release is cut
/*
// filter by subnet ID if specified for frontend vpc ip
frontendSubnetID := service.GetAnnotations()[annotations.NodeBalancerFrontendSubnetID]
if frontendSubnetID != "" {
for i := range lbs {
lb := &lbs[i]
if lb.FrontendAddressType == "vpc" && lb.FrontendVPCSubnetID != nil && strconv.Itoa(*lb.FrontendVPCSubnetID) == frontendSubnetID {
return lb, nil
}
}
}
return nil, lbNotFoundError{serviceNn: getServiceNn(service)}
}
return nil, lbNotFoundError{serviceNn: getServiceNn(service)}
} */

klog.V(2).Infof("found NodeBalancer (%d) for service (%s) via IP (%s)", lbs[0].ID, getServiceNn(service), ip.String())
return &lbs[0], nil
Expand Down Expand Up @@ -764,7 +766,7 @@ func (l *loadbalancers) GetLinodeNBType(service *v1.Service) linodego.NodeBalanc
// 3. NodeBalancerBackendIPv4SubnetID/NodeBalancerBackendIPv4SubnetName flag
// 4. NodeBalancerBackendIPv4Subnet flag
// 5. Default to using the subnet ID of the service's VPC
func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Service) ([]linodego.NodeBalancerBackendVPCOptions, error) {
func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Service) ([]linodego.NodeBalancerVPCOptions, error) {
// Evaluate subnetID based on annotations or flags
subnetID, err := l.getSubnetIDForSVC(ctx, service)
if err != nil {
Expand All @@ -780,10 +782,10 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser
// If the user has specified a NodeBalancerBackendIPv4Range, use that
// for the NodeBalancer backend ipv4 range
if backendIPv4Range != "" {
vpcCreateOpts := []linodego.NodeBalancerBackendVPCOptions{
vpcCreateOpts := []linodego.NodeBalancerVPCOptions{
{
SubnetID: subnetID,
IPv4Range: &backendIPv4Range,
IPv4Range: backendIPv4Range,
},
}
return vpcCreateOpts, nil
Expand All @@ -807,10 +809,10 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser
if err != nil {
return nil, err
}
return []linodego.NodeBalancerBackendVPCOptions{
return []linodego.NodeBalancerVPCOptions{
{
SubnetID: subnetID,
IPv4Range: &backendIPv4Range,
IPv4Range: backendIPv4Range,
},
}, nil
}
Expand All @@ -821,7 +823,7 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser
_, vpcInAnnotation := service.GetAnnotations()[annotations.NodeBalancerBackendVPCName]
_, subnetInAnnotation := service.GetAnnotations()[annotations.NodeBalancerBackendSubnetName]
if vpcInAnnotation || subnetInAnnotation {
vpcCreateOpts := []linodego.NodeBalancerBackendVPCOptions{
vpcCreateOpts := []linodego.NodeBalancerVPCOptions{
{
SubnetID: subnetID,
},
Expand All @@ -832,7 +834,7 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser
// Precedence 3: If the user has specified a NodeBalancerBackendIPv4SubnetID, use that
// and auto-allocate subnets from it for the NodeBalancer
if options.Options.NodeBalancerBackendIPv4SubnetID != 0 {
vpcCreateOpts := []linodego.NodeBalancerBackendVPCOptions{
vpcCreateOpts := []linodego.NodeBalancerVPCOptions{
{
SubnetID: options.Options.NodeBalancerBackendIPv4SubnetID,
},
Expand All @@ -843,25 +845,27 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser
// Precedence 4: If the user has specified a NodeBalancerBackendIPv4Subnet, use that
// and auto-allocate subnets from it for the NodeBalancer.
if options.Options.NodeBalancerBackendIPv4Subnet != "" {
vpcCreateOpts := []linodego.NodeBalancerBackendVPCOptions{
vpcCreateOpts := []linodego.NodeBalancerVPCOptions{
{
SubnetID: subnetID,
IPv4Range: &options.Options.NodeBalancerBackendIPv4Subnet,
IPv4RangeAutoAssign: new(true),
IPv4Range: options.Options.NodeBalancerBackendIPv4Subnet,
IPv4RangeAutoAssign: true,
},
}
return vpcCreateOpts, nil
}

// Default to using the subnet ID of the service's VPC
vpcCreateOpts := []linodego.NodeBalancerBackendVPCOptions{
vpcCreateOpts := []linodego.NodeBalancerVPCOptions{
{
SubnetID: subnetID,
},
}
return vpcCreateOpts, nil
}

// Uncomment once https://github.com/linode/linodego/pull/978 is merged and a release is cut
/*
// getFrontendVPCCreateOptions returns the VPC options for the NodeBalancer frontend VPC creation.
// Order of precedence:
// 1. Frontend Subnet ID Annotation - Direct subnet ID
Expand Down Expand Up @@ -934,7 +938,7 @@ func (l *loadbalancers) getSubnetIDByVPCAndSubnetNames(ctx context.Context, vpcN

// Use the VPC ID and Subnet Name to get the subnet ID
return services.GetSubnetID(ctx, l.client, vpcID, subnetName)
}
} */

func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName string, service *v1.Service, configs []linodego.NodeBalancerConfigCreateOptions) (lb *linodego.NodeBalancer, err error) {
connThrottle := getConnectionThrottle(service)
Expand All @@ -953,18 +957,20 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri
}

if !useIPv6Backends && len(options.Options.VPCNames) > 0 && !options.Options.DisableNodeBalancerVPCBackends {
createOpts.BackendVPCs, err = l.getVPCCreateOptions(ctx, service)
createOpts.VPCs, err = l.getVPCCreateOptions(ctx, service)
if err != nil {
return nil, err
}
}

// Add frontend VPC configuration
if frontendVPCs, err := l.getFrontendVPCCreateOptions(ctx, service); err != nil {
return nil, err
} else if len(frontendVPCs) > 0 {
createOpts.FrontendVPCs = frontendVPCs
}
// Uncomment once https://github.com/linode/linodego/pull/978 is merged and a release is cut
/*
// Add frontend VPC configuration
if frontendVPCs, err := l.getFrontendVPCCreateOptions(ctx, service); err != nil {
return nil, err
} else if len(frontendVPCs) > 0 {
createOpts.FrontendVPCs = frontendVPCs
} */

// Check for static IPv4 address annotation
if ipv4, ok := service.GetAnnotations()[annotations.AnnLinodeLoadBalancerReservedIPv4]; ok {
Expand Down Expand Up @@ -1552,9 +1558,10 @@ func makeLoadBalancerStatus(service *v1.Service, nb *linodego.NodeBalancer) *v1.
}
}

if nb.FrontendAddressType == "vpc" {
// Uncomment once https://github.com/linode/linodego/pull/978 is merged and a release is cut
/* if nb.FrontendAddressType == "vpc" {
klog.V(4).Infof("NodeBalancer (%d) is using frontend VPC address type", nb.ID)
}
} */

// Check for per-service IPv6 annotation first, then fall back to global setting if not set
useIPv6 := getServiceBoolAnnotation(service, annotations.AnnLinodeEnableIPv6Ingress)
Expand Down
12 changes: 6 additions & 6 deletions cloud/linode/loadbalancers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"strings"
"testing"

"github.com/golang/mock/gomock"
"github.com/linode/linodego/v2"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -30,7 +29,6 @@ import (

"github.com/linode/linode-cloud-controller-manager/cloud/annotations"
"github.com/linode/linode-cloud-controller-manager/cloud/linode/client"
"github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks"
"github.com/linode/linode-cloud-controller-manager/cloud/linode/options"
"github.com/linode/linode-cloud-controller-manager/cloud/linode/services"
)
Expand Down Expand Up @@ -4434,8 +4432,8 @@ func Test_buildLoadBalancerRequestOmitsVPCConfigForIPv6Backends(t *testing.T) {
if err := json.Unmarshal([]byte(req.Body), &createOpts); err != nil {
t.Fatalf("unable to unmarshal create request body %#v, error: %#v", req.Body, err)
}
if len(createOpts.BackendVPCs) != 0 {
t.Fatalf("expected nodebalancer create request to omit VPC config for IPv6 backends, got %#v", createOpts.BackendVPCs)
if len(createOpts.VPCs) != 0 {
t.Fatalf("expected nodebalancer create request to omit VPC config for IPv6 backends, got %#v", createOpts.VPCs)
}
if len(createOpts.Configs) != 1 || len(createOpts.Configs[0].Nodes) != 1 {
t.Fatalf("expected a single nodebalancer config with one backend node, got %#v", createOpts.Configs)
Expand Down Expand Up @@ -5972,7 +5970,9 @@ func Test_validateNodeBalancerFrontendIPRange(t *testing.T) {
}
}

func Test_makeLoadBalancerStatus_FrontendVPC(t *testing.T) {
// Uncomment once https://github.com/linode/linodego/pull/978 is merged and a release is cut
//nolint:dupword // The commented-out test repeats args in its table data.
/* func Test_makeLoadBalancerStatus_FrontendVPC(t *testing.T) {
type args struct {
service *v1.Service
nb *linodego.NodeBalancer
Expand Down Expand Up @@ -6254,4 +6254,4 @@ func Test_getFrontendVPCCreateOptions(t *testing.T) {
}
})
}
}
} */
4 changes: 2 additions & 2 deletions cloud/linode/nodebalancer_backend_range_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ func TestGetVPCCreateOptionsWithReservedBackendRange(t *testing.T) {
require.NoError(t, err)
require.Len(t, got, 1)
assert.Equal(t, 456, got[0].SubnetID)
assert.Equal(t, new("10.63.88.4/30"), got[0].IPv4Range)
assert.Nil(t, got[0].IPv4RangeAutoAssign)
assert.Equal(t, "10.63.88.4/30", got[0].IPv4Range)
assert.False(t, got[0].IPv4RangeAutoAssign)
})
}
}
16 changes: 0 additions & 16 deletions docs/configuration/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,6 @@ The keys and the values in [annotations must be strings](https://kubernetes.io/d
| `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) |
| `backend-subnet-id` | string | | Subnet ID within VPC which is connected to the NodeBalancer backend. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `frontend-subnet-id` | string | | Subnet ID for the NodeBalancer frontend VPC configuration. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `frontend-vpc-name` | string | | Frontend VPC name for the NodeBalancer frontend VPC configuration. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `frontend-subnet-name` | string | | Frontend subnet name for the NodeBalancer frontend VPC configuration. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `frontend-ipv4-range` | string | | Optional IPv4 CIDR range from the frontend subnet. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `frontend-ipv6-range` | string | | Optional IPv6 CIDR range from the frontend subnet. See [Nodebalancer VPC Configuration](#nodebalancer-vpc-configuration) |
| `reserved-ipv4` | string | | An existing Reserved IPv4 address that will be used to initialize the NodeBalancer instance. See [LoadBalancer Configuration](loadbalancer.md#reserved-ipv4-addresses) |
| `retain-reserved-ipv4` | bool | `true` | When `false`, deleting a `LoadBalancer` service also releases the reserved IPv4 address attached to the NodeBalancer |

Expand Down Expand Up @@ -165,17 +160,6 @@ metadata:
service.beta.kubernetes.io/linode-loadbalancer-backend-subnet-name: "subnet1"
```

Frontend VPC configuration:

```yaml
metadata:
annotations:
service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-id: "169341"
# Optional:
# service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv4-range: "10.0.0.0/24"
# service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv6-range: "2001:db8::/64"
```

### Service with IPv6 Address

```yaml
Expand Down
39 changes: 1 addition & 38 deletions docs/configuration/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ When IPv6 is enabled (either globally or per-service), both IPv4 and IPv6 addres

### IPv6 Backend Support

IPv6 frontends and IPv6 backends are configured independently. Frontend IPv6 controls what the Service publishes in `status.loadBalancer.ingress`, while backend IPv6 controls which node addresses a NodeBalancer targets.
Backend IPv6 controls which node addresses a NodeBalancer targets.

IPv6 backends require a dual-stack workload cluster. In practice, the cluster networking stack must support IPv6 NodePort traffic, and the Service itself should be created as dual-stack. A single-stack IPv4 `LoadBalancer` Service can still be annotated for IPv6 backends, but the NodeBalancer health checks and traffic path may fail because the backend NodePort is not exposed over IPv6.

Expand Down Expand Up @@ -248,43 +248,6 @@ metadata:
service.beta.kubernetes.io/linode-loadbalancer-backend-subnet-name: "subnet1"
```

### Configuring NodeBalancer frontend with VPC

NodeBalancers can optionally be configured with a VPC-based frontend address.

Frontend VPC configuration supports the following annotations:

1. Choose the frontend subnet:

- `service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-id` (preferred)
- OR `service.beta.kubernetes.io/linode-loadbalancer-frontend-vpc-name` and `service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-name`

2. Optionally constrain the frontend address assignment within the subnet:

- `service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv4-range` (CIDR)
- `service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv6-range` (CIDR)

Order of precedence:

- If `frontend-subnet-id` is set, it is used.
- Otherwise, `frontend-vpc-name` + `frontend-subnet-name` are used.
- IPv4/IPv6 range annotations are optional add-ons and require one of the subnet selectors above.

Example:

```yaml
metadata:
annotations:
service.beta.kubernetes.io/linode-loadbalancer-frontend-subnet-id: "169341"
# Optional:
# service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv4-range: "10.0.0.0/24"
# service.beta.kubernetes.io/linode-loadbalancer-frontend-ipv6-range: "2001:db8::/64"
```

For a complete working example, see `examples/vpc-frontend-example.yaml`.

If CCM is started with `--nodebalancer-backend-ipv4-subnet` flag, then it will not allow provisioning of nodebalancer unless subnet specified in service annotation lie within the subnet specified using the flag. This is to prevent accidental overlap between nodebalancer backend ips and pod CIDRs.

### Reserving a NodeBalancer backend IPv4 range

`--nodebalancer-backend-ipv4-reserved-range` marks one /30 inside `--nodebalancer-backend-ipv4-subnet` as off-limits, so CCM never assigns it to a NodeBalancer. Use it to keep a backend range available for an address block managed outside the cluster.
Expand Down
3 changes: 0 additions & 3 deletions docs/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ This section provides working examples of common CCM configurations. Each exampl
- Session Affinity
- Custom Node Selection

3. **Frontend VPC NodeBalancer**
- `examples/vpc-frontend-example.yaml`

Note: To test UDP based NBs, one can use [test-server](https://github.com/rahulait/test-server) repo to run server using UDP protocol and then use the client commands in repo's readme to connect to the server.

For testing these examples, see the [test script](https://github.com/linode/linode-cloud-controller-manager/blob/master/examples/test.sh).
Expand Down
Loading
Loading