From 8fb3e11f6db66c826f70186ca905befa1a7a7ed2 Mon Sep 17 00:00:00 2001 From: Khaja Omer Date: Fri, 8 Aug 2025 14:59:47 -0500 Subject: [PATCH] feat: VPC NodeBalancer backends behind feature flag API: add spec.network.enableVPCBackends (default false, immutable). When true and VPCRef/VPCID is set, create the NodeBalancer in the target VPC and prefer VPC backend IPs. NodeBalancerBackendIPv4Range remains optional and is honored when provided. Services: introduce ShouldUseVPC(scope) and DetermineAPIServerLBPort(scope); EnsureNodeBalancer uses VPC SubnetID and optional IPv4Range; node registration prefers VPC internal IPs when enabled, otherwise falls back to Linode private IPs. Controller: refactor getIPPortCombo to select VPC IPs first, factor helpers findFirstVPCInternalIP/findFirstPrivateInternalIP and buildPortCombosForIP; reuse DetermineAPIServerLBPort for DNS endpoint. CRDs+Docs: extend LinodeCluster CRDs with enableVPCBackends (default false, immutable); update docs reference for the new field. Tests: update unit tests to set EnableVPCBackends=true in VPC scenarios and to use new helpers; keep behavior unchanged when flag is false. --- api/v1alpha2/linodecluster_types.go | 10 +++ cloud/services/loadbalancers.go | 46 ++++++----- cloud/services/loadbalancers_test.go | 11 +++ ...cture.cluster.x-k8s.io_linodeclusters.yaml | 12 +++ ...uster.x-k8s.io_linodeclustertemplates.yaml | 12 +++ docs/src/reference/out.md | 1 + hack/generate-flavors.sh | 2 +- .../linodecluster_controller_helpers.go | 82 ++++++++++--------- .../linodecluster_controller_helpers_test.go | 4 + .../linodecluster_controller_test.go | 5 +- 10 files changed, 124 insertions(+), 61 deletions(-) diff --git a/api/v1alpha2/linodecluster_types.go b/api/v1alpha2/linodecluster_types.go index 39d058f69..e8d9745bd 100644 --- a/api/v1alpha2/linodecluster_types.go +++ b/api/v1alpha2/linodecluster_types.go @@ -190,6 +190,16 @@ type NetworkSpec struct { // example: 10.10.10.0/30 // +optional NodeBalancerBackendIPv4Range string `json:"nodeBalancerBackendIPv4Range,omitempty"` + + // EnableVPCBackends toggles VPC-scoped NodeBalancer and VPC backend IP usage. + // If set to false (default), the NodeBalancer will not be created in a VPC and + // backends will use Linode private IPs. If true, the NodeBalancer will be + // created in the configured VPC (when VPCRef or VPCID is set) and backends + // will use VPC IPs. + // +kubebuilder:default=false + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + // +optional + EnableVPCBackends bool `json:"enableVPCBackends,omitempty"` } type LinodeNBPortConfig struct { diff --git a/cloud/services/loadbalancers.go b/cloud/services/loadbalancers.go index eb3cc10d2..45973ecd4 100644 --- a/cloud/services/loadbalancers.go +++ b/cloud/services/loadbalancers.go @@ -22,6 +22,21 @@ const ( DefaultKonnectivityLBPort = 8132 ) +// DetermineAPIServerLBPort returns the configured API server load balancer port, +// or the provider default when not explicitly set. +func DetermineAPIServerLBPort(clusterScope *scope.ClusterScope) int { + if clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort != 0 { + return clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort + } + return DefaultApiserverLBPort +} + +// ShouldUseVPC decides whether VPC IPs/backends should be preferred and a VPC-scoped +// NodeBalancer should be created. It requires both the feature flag and a VPC reference/ID. +func ShouldUseVPC(clusterScope *scope.ClusterScope) bool { + return clusterScope.LinodeCluster.Spec.Network.EnableVPCBackends && (clusterScope.LinodeCluster.Spec.VPCRef != nil || clusterScope.LinodeCluster.Spec.VPCID != nil) +} + // FindSubnet selects a subnet from the provided subnets based on the subnet name // It handles both direct VPC subnets and VPCRef subnets // If subnet name is provided, it looks for a matching subnet; otherwise, it uses the first subnet @@ -125,19 +140,18 @@ func EnsureNodeBalancer(ctx context.Context, clusterScope *scope.ClusterScope, l Tags: []string{string(clusterScope.LinodeCluster.UID)}, } - // if NodeBalancerBackendIPv4Range is set, create the NodeBalancer in the specified VPC - if clusterScope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range != "" && (clusterScope.LinodeCluster.Spec.VPCRef != nil || clusterScope.LinodeCluster.Spec.VPCID != nil) { - logger.Info("Creating NodeBalancer in VPC", "NodeBalancerBackendIPv4Range", clusterScope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range) + // if enableVPCBackends is true and vpcRef or vpcID is set, create the NodeBalancer in the specified VPC + if ShouldUseVPC(clusterScope) { + logger.Info("Creating NodeBalancer in VPC") subnetID, err := getSubnetID(ctx, clusterScope, logger) if err != nil { logger.Error(err, "Failed to fetch Linode Subnet ID") return nil, err } - createConfig.VPCs = []linodego.NodeBalancerVPCOptions{ - { - IPv4Range: clusterScope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range, - SubnetID: subnetID, - }, + + createConfig.VPCs = []linodego.NodeBalancerVPCOptions{{SubnetID: subnetID}} + if clusterScope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range != "" { + createConfig.VPCs[0].IPv4Range = clusterScope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range } } @@ -264,10 +278,7 @@ func EnsureNodeBalancerConfigs( nbConfigs := []*linodego.NodeBalancerConfig{} var apiserverLinodeNBConfig *linodego.NodeBalancerConfig var err error - apiLBPort := DefaultApiserverLBPort - if clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort != 0 { - apiLBPort = clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort - } + apiLBPort := DetermineAPIServerLBPort(clusterScope) if clusterScope.LinodeCluster.Spec.Network.ApiserverNodeBalancerConfigID != nil { apiserverLinodeNBConfig, err = clusterScope.LinodeClient.GetNodeBalancerConfig( @@ -325,10 +336,7 @@ func EnsureNodeBalancerConfigs( } func processAndCreateNodeBalancerNodes(ctx context.Context, ipAddress string, clusterScope *scope.ClusterScope, nodeBalancerNodes []linodego.NodeBalancerNode, subnetID int) error { - apiserverLBPort := DefaultApiserverLBPort - if clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort != 0 { - apiserverLBPort = clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort - } + apiserverLBPort := DetermineAPIServerLBPort(clusterScope) // Set the port number and NB config ID for standard ports portsToBeAdded := make([]map[string]int, 0) @@ -382,12 +390,8 @@ func AddNodesToNB(ctx context.Context, logger logr.Logger, clusterScope *scope.C return errors.New("nil NodeBalancer Config ID") } - // if NodeBalancerBackendIPv4Range is set, we want to prioritize finding the VPC IP address - // otherwise, we will use the private IP address subnetID := 0 - useVPCIps := clusterScope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range != "" && clusterScope.LinodeCluster.Spec.VPCRef != nil - if useVPCIps { - // Get subnetID + if ShouldUseVPC(clusterScope) { subnetID, err := getSubnetID(ctx, clusterScope, logger) if err != nil { logger.Error(err, "Failed to fetch Linode Subnet ID") diff --git a/cloud/services/loadbalancers_test.go b/cloud/services/loadbalancers_test.go index e47a1cb86..22825c329 100644 --- a/cloud/services/loadbalancers_test.go +++ b/cloud/services/loadbalancers_test.go @@ -276,6 +276,7 @@ func TestEnsureNodeBalancer(t *testing.T) { Namespace: "default", }, Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, }, @@ -339,6 +340,7 @@ func TestEnsureNodeBalancer(t *testing.T) { Region: "us-east", VPCID: ptr.To(456), Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, }, @@ -393,6 +395,7 @@ func TestEnsureNodeBalancer(t *testing.T) { Region: "us-east", VPCID: ptr.To(789), Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, }, @@ -420,6 +423,7 @@ func TestEnsureNodeBalancer(t *testing.T) { Namespace: "default", }, Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, }, @@ -1713,6 +1717,7 @@ func TestAddNodeToNBFullWorkflow(t *testing.T) { Namespace: "default", }, Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerID: ptr.To(1234), ApiserverNodeBalancerConfigID: ptr.To(5678), NodeBalancerBackendIPv4Range: "10.0.0.0/24", @@ -1786,6 +1791,7 @@ func TestAddNodeToNBFullWorkflow(t *testing.T) { Namespace: "default", }, Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerID: ptr.To(1234), ApiserverNodeBalancerConfigID: ptr.To(5678), NodeBalancerBackendIPv4Range: "10.0.0.0/24", @@ -1844,6 +1850,7 @@ func TestAddNodeToNBFullWorkflow(t *testing.T) { Namespace: "default", }, Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerID: ptr.To(1234), ApiserverNodeBalancerConfigID: ptr.To(5678), NodeBalancerBackendIPv4Range: "10.0.0.0/24", @@ -2564,6 +2571,7 @@ func TestAddNodeToNBWithVPC(t *testing.T) { }, Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, ApiserverNodeBalancerConfigID: ptr.To(222), NodeBalancerID: ptr.To(111), NodeBalancerBackendIPv4Range: "10.0.0.0/24", @@ -2635,6 +2643,7 @@ func TestAddNodeToNBWithVPC(t *testing.T) { }, Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, ApiserverNodeBalancerConfigID: ptr.To(222), NodeBalancerID: ptr.To(111), NodeBalancerBackendIPv4Range: "10.0.0.0/24", @@ -2710,6 +2719,7 @@ func TestAddNodeToNBWithVPC(t *testing.T) { }, Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, ApiserverNodeBalancerConfigID: ptr.To(222), NodeBalancerID: ptr.To(111), NodeBalancerBackendIPv4Range: "10.0.0.0/24", @@ -2819,6 +2829,7 @@ func TestAddNodeToNBWithVPC(t *testing.T) { }, Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, ApiserverNodeBalancerConfigID: ptr.To(222), NodeBalancerID: ptr.To(111), NodeBalancerBackendIPv4Range: "10.0.0.0/24", diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclusters.yaml index 94e018b83..32c7f5fc0 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclusters.yaml @@ -155,6 +155,18 @@ spec: Ignored if the LoadBalancerType is set to anything other than dns If not set, CAPL will create a unique identifier for you type: string + enableVPCBackends: + default: false + description: |- + EnableVPCBackends toggles VPC-scoped NodeBalancer and VPC backend IP usage. + If set to false (default), the NodeBalancer will not be created in a VPC and + backends will use Linode private IPs. If true, the NodeBalancer will be + created in the configured VPC (when VPCRef or VPCID is set) and backends + will use VPC IPs. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf loadBalancerType: default: NodeBalancer description: LoadBalancerType is the type of load balancer to diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclustertemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclustertemplates.yaml index bd4d028ae..31145fcd9 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclustertemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_linodeclustertemplates.yaml @@ -151,6 +151,18 @@ spec: Ignored if the LoadBalancerType is set to anything other than dns If not set, CAPL will create a unique identifier for you type: string + enableVPCBackends: + default: false + description: |- + EnableVPCBackends toggles VPC-scoped NodeBalancer and VPC backend IP usage. + If set to false (default), the NodeBalancer will not be created in a VPC and + backends will use Linode private IPs. If true, the NodeBalancer will be + created in the configured VPC (when VPCRef or VPCID is set) and backends + will use VPC IPs. + type: boolean + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf loadBalancerType: default: NodeBalancer description: LoadBalancerType is the type of load balancer diff --git a/docs/src/reference/out.md b/docs/src/reference/out.md index 2895d1ada..55265740f 100644 --- a/docs/src/reference/out.md +++ b/docs/src/reference/out.md @@ -1170,6 +1170,7 @@ _Appears in:_ | `subnetName` _string_ | subnetName is the name/label of the VPC subnet to be used by the cluster | | | | `useVlan` _boolean_ | UseVlan provisions a cluster that uses VLANs instead of VPCs. IPAM is managed internally. | | | | `nodeBalancerBackendIPv4Range` _string_ | NodeBalancerBackendIPv4Range is the subnet range we want to provide for creating nodebalancer in VPC.
example: 10.10.10.0/30 | | | +| `enableVPCBackends` _boolean_ | EnableVPCBackends toggles VPC-scoped NodeBalancer and VPC backend IP usage.
If set to false (default), the NodeBalancer will not be created in a VPC and
backends will use Linode private IPs. If true, the NodeBalancer will be
created in the configured VPC (when VPCRef or VPCID is set) and backends
will use VPC IPs. | false | | #### ObjectStorageACL diff --git a/hack/generate-flavors.sh b/hack/generate-flavors.sh index 7fada7187..ca7e75100 100755 --- a/hack/generate-flavors.sh +++ b/hack/generate-flavors.sh @@ -16,7 +16,7 @@ SUPPORTED_CLUSTERCLASSES=( for clusterclass in ${SUPPORTED_CLUSTERCLASSES[@]}; do # clusterctl expects clusterclass not have the "cluster-template" prefix # except for the actual cluster template using the clusterclass - echo "****** Generating clusterclass-${clusterclass} flavor ******" + echo "****** Generating ${clusterclass} flavor ******" kustomize build "${FLAVORS_DIR}/${clusterclass}" > "${REPO_ROOT}/templates/${clusterclass}.yaml" cp "${FLAVORS_DIR}/${clusterclass}/cluster-template.yaml" "${REPO_ROOT}/templates/cluster-template-${clusterclass}.yaml" done diff --git a/internal/controller/linodecluster_controller_helpers.go b/internal/controller/linodecluster_controller_helpers.go index af5b2939b..adbdd31df 100644 --- a/internal/controller/linodecluster_controller_helpers.go +++ b/internal/controller/linodecluster_controller_helpers.go @@ -87,51 +87,62 @@ func removeMachineFromNB(ctx context.Context, logger logr.Logger, clusterScope * } func getIPPortCombo(cscope *scope.ClusterScope) (ipPortComboList []string) { - apiserverLBPort := services.DefaultApiserverLBPort - if cscope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort != 0 { - apiserverLBPort = cscope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort - } - - // Check if we're using VPC - useVPCIps := cscope.LinodeCluster.Spec.Network.NodeBalancerBackendIPv4Range != "" && cscope.LinodeCluster.Spec.VPCRef != nil + apiServerLBPort := services.DetermineAPIServerLBPort(cscope) + useVPCIPs := services.ShouldUseVPC(cscope) for _, eachMachine := range cscope.LinodeMachines.Items { - // First try to find VPC IPs if we're using VPC - if useVPCIps { - vpcIPFound := false - for _, IPs := range eachMachine.Status.Addresses { - // Look for internal IPs that are NOT 192.168.* (likely VPC IPs) - if IPs.Type == clusterv1.MachineInternalIP && !util.IsLinodePrivateIP(IPs.Address) { - vpcIPFound = true - ipPortComboList = append(ipPortComboList, fmt.Sprintf("%s:%d", IPs.Address, apiserverLBPort)) - for _, portConfig := range cscope.LinodeCluster.Spec.Network.AdditionalPorts { - ipPortComboList = append(ipPortComboList, fmt.Sprintf("%s:%d", IPs.Address, portConfig.Port)) - } - break // Use first VPC IP found for this machine - } - } + var selectedIP string - // If we found a VPC IP for this machine, continue to the next machine - if vpcIPFound { - continue + if useVPCIPs { + if ip, ok := findFirstVPCInternalIP(eachMachine.Status.Addresses); ok { + selectedIP = ip } } - // Fall back to original behavior for this machine if no VPC IP found or not using VPC - for _, IPs := range eachMachine.Status.Addresses { - if IPs.Type != clusterv1.MachineInternalIP || !util.IsLinodePrivateIP(IPs.Address) { - continue + if selectedIP == "" { + if ip, ok := findFirstPrivateInternalIP(eachMachine.Status.Addresses); ok { + selectedIP = ip } - ipPortComboList = append(ipPortComboList, fmt.Sprintf("%s:%d", IPs.Address, apiserverLBPort)) - for _, portConfig := range cscope.LinodeCluster.Spec.Network.AdditionalPorts { - ipPortComboList = append(ipPortComboList, fmt.Sprintf("%s:%d", IPs.Address, portConfig.Port)) - } - break // Use first 192.168.* IP found for this machine + } + + if selectedIP != "" { + ipPortComboList = append(ipPortComboList, buildPortCombosForIP(selectedIP, apiServerLBPort, cscope.LinodeCluster.Spec.Network.AdditionalPorts)...) } } + return ipPortComboList } +// findFirstVPCInternalIP returns the first internal IP that is not in Linode's private 192.168.* range. +func findFirstVPCInternalIP(addresses []clusterv1.MachineAddress) (string, bool) { + for _, addr := range addresses { + if addr.Type == clusterv1.MachineInternalIP && !util.IsLinodePrivateIP(addr.Address) { + return addr.Address, true + } + } + return "", false +} + +// findFirstPrivateInternalIP returns the first internal IP in Linode's private 192.168.* range. +func findFirstPrivateInternalIP(addresses []clusterv1.MachineAddress) (string, bool) { + for _, addr := range addresses { + if addr.Type == clusterv1.MachineInternalIP && util.IsLinodePrivateIP(addr.Address) { + return addr.Address, true + } + } + return "", false +} + +// buildPortCombosForIP composes ip:port pairs for the API server port and any additional ports. +func buildPortCombosForIP(ip string, apiServerLBPort int, additionalPorts []infrav1alpha2.LinodeNBPortConfig) []string { + results := make([]string, 0, 1+len(additionalPorts)) + results = append(results, fmt.Sprintf("%s:%d", ip, apiServerLBPort)) + for _, portConfig := range additionalPorts { + results = append(results, fmt.Sprintf("%s:%d", ip, portConfig.Port)) + } + return results +} + func linodeMachineToLinodeCluster(tracedClient client.Client, logger logr.Logger) handler.MapFunc { logger = logger.WithName("LinodeClusterReconciler").WithName("linodeMachineToLinodeCluster") @@ -188,10 +199,7 @@ func handleDNS(clusterScope *scope.ClusterScope) { subDomain = clusterScope.LinodeCluster.Spec.Network.DNSSubDomainOverride } dnsHost := subDomain + "." + clusterSpec.Network.DNSRootDomain - apiLBPort := services.DefaultApiserverLBPort - if clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort != 0 { - apiLBPort = clusterScope.LinodeCluster.Spec.Network.ApiserverLoadBalancerPort - } + apiLBPort := services.DetermineAPIServerLBPort(clusterScope) clusterScope.LinodeCluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{ Host: dnsHost, Port: int32(apiLBPort), // #nosec G115: Integer overflow conversion is safe for port numbers diff --git a/internal/controller/linodecluster_controller_helpers_test.go b/internal/controller/linodecluster_controller_helpers_test.go index 9edfe3ffe..88d36aa08 100644 --- a/internal/controller/linodecluster_controller_helpers_test.go +++ b/internal/controller/linodecluster_controller_helpers_test.go @@ -93,6 +93,7 @@ func TestGetIPPortCombo(t *testing.T) { LinodeCluster: &infrav1alpha2.LinodeCluster{ Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, VPCRef: &corev1.ObjectReference{ @@ -127,6 +128,7 @@ func TestGetIPPortCombo(t *testing.T) { LinodeCluster: &infrav1alpha2.LinodeCluster{ Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, VPCRef: &corev1.ObjectReference{ @@ -192,6 +194,7 @@ func TestGetIPPortCombo(t *testing.T) { LinodeCluster: &infrav1alpha2.LinodeCluster{ Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", AdditionalPorts: []infrav1alpha2.LinodeNBPortConfig{ { @@ -227,6 +230,7 @@ func TestGetIPPortCombo(t *testing.T) { LinodeCluster: &infrav1alpha2.LinodeCluster{ Spec: infrav1alpha2.LinodeClusterSpec{ Network: infrav1alpha2.NetworkSpec{ + EnableVPCBackends: true, NodeBalancerBackendIPv4Range: "10.0.0.0/24", }, VPCRef: &corev1.ObjectReference{ diff --git a/internal/controller/linodecluster_controller_test.go b/internal/controller/linodecluster_controller_test.go index 8a8b6475f..4d8221d71 100644 --- a/internal/controller/linodecluster_controller_test.go +++ b/internal/controller/linodecluster_controller_test.go @@ -79,7 +79,7 @@ var _ = Describe("cluster-lifecycle", Ordered, Label("cluster", "cluster-lifecyc Spec: infrav1alpha2.LinodeVPCSpec{ Region: "us-ord", Subnets: []infrav1alpha2.VPCSubnetCreateOptions{ - {Label: "subnet1", IPv4: "10.0.0.0/8"}, + {Label: "subnet1", IPv4: "10.0.0.0/8", SubnetID: 123}, }, }, } @@ -787,7 +787,8 @@ var _ = Describe("cluster-with-direct-vpcid", Ordered, Label("cluster", "direct- Label: "subnet-1", }, }, - }, nil) + }, nil). + AnyTimes() // Mock the CreateNodeBalancer call to avoid unexpected call error mck.LinodeClient.EXPECT().CreateNodeBalancer(gomock.Any(), gomock.Any()).