diff --git a/Makefile b/Makefile index c94ef946..52ea3d43 100644 --- a/Makefile +++ b/Makefile @@ -233,18 +233,6 @@ e2e-test-ipv6-backends: LINODE_URL=$(LINODE_URL) \ chainsaw test e2e/test --selector ipv6-backends $(E2E_FLAGS) -.PHONY: e2e-test-bgp -e2e-test-bgp: - KUBECONFIG=$(KUBECONFIG_PATH) CLUSTER_SUFFIX=$(CLUSTER_NAME) ./e2e/setup/cilium-setup.sh - KUBECONFIG=$(KUBECONFIG_PATH) kubectl -n kube-system rollout status daemonset/ccm-linode --timeout=300s - CLUSTER_NAME=$(CLUSTER_NAME) \ - MGMT_KUBECONFIG=$(MGMT_KUBECONFIG_PATH) \ - KUBECONFIG=$(KUBECONFIG_PATH) \ - REGION=$(LINODE_REGION) \ - LINODE_TOKEN=$(LINODE_TOKEN) \ - LINODE_URL=$(LINODE_URL) \ - chainsaw test e2e/bgp-test/lb-cilium-bgp $(E2E_FLAGS) - .PHONY: e2e-test-subnet e2e-test-subnet: # Generate cluster manifests for second cluster diff --git a/README.md b/README.md index c1adadf6..ae511dc7 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,7 @@ A Cloud Controller Manager (CCM) is a Kubernetes control plane component that em - Health checks - Session affinity - Protocol configuration -- Supports multiple load balancing approaches - - Traditional NodeBalancer deployment - - BGP-based IP sharing for cost optimization - - Custom firewall rules and security configurations +- Supports NodeBalancer deployment with custom firewall rules and security configurations #### Route Controller diff --git a/cloud/linode/cilium_loadbalancers.go b/cloud/linode/cilium_loadbalancers.go deleted file mode 100644 index 01ae40b6..00000000 --- a/cloud/linode/cilium_loadbalancers.go +++ /dev/null @@ -1,589 +0,0 @@ -package linode - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "os" - "slices" - "strings" - - "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" - ciliumclient "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/typed/cilium.io/v2alpha1" - slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" - "github.com/google/uuid" - "github.com/linode/linodego/v2" - v1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/retry" - "k8s.io/klog/v2" - "k8s.io/utils/ptr" - - "github.com/linode/linode-cloud-controller-manager/cloud/annotations" - "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" - ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" -) - -const ( - ciliumLBClass = "io.cilium/bgp-control-plane" - ipHolderLabelPrefix = "linode-ccm-ip-holder" - ciliumBGPPeeringPolicyName = "linode-ccm-bgp-peering" - defaultBGPPeerPrefix = "2600:3c0f" - commonControlPlaneLabel = "node-role.kubernetes.io/control-plane" -) - -// This mapping is unfortunately necessary since there is no way to get the -// numeric ID for a data center from the API. -// These values come from https://www.linode.com/docs/products/compute/compute-instances/guides/failover/#ip-sharing-availability -var ( - regionIDMap = map[string]int{ - "nl-ams": 22, // Amsterdam (Netherlands) - "us-southeast": 4, // Atlanta, GA (USA) - "in-maa": 25, // Chennai (India) - "us-ord": 18, // Chicago, IL (USA) - "us-central": 2, // Dallas, TX (USA) - "eu-central": 10, // Frankfurt (Germany) - // "us-west": 3, // Fremont, CA (USA) Undergoing network upgrades - "id-cgk": 29, // Jakarta (Indonesia) - "eu-west": 7, // London (United Kingdom) - "gb-lon": 44, // London 2 (United Kingdom) - "us-lax": 30, // Los Angeles, CA (USA) - "es-mad": 24, // Madrid (Spain) - "au-mel": 45, // Melbourne (Australia) - "us-mia": 28, // Miami, FL (USA) - "it-mil": 27, // Milan (Italy) - "ap-west": 14, // Mumbai (India) - "in-bom-2": 46, // Mumbai 2 (India) - "us-east": 6, // Newark, NJ (USA) - "jp-osa": 26, // Osaka (Japan) - "fr-par": 19, // Paris (France) - "br-gru": 21, // São Paulo (Brazil) - "us-sea": 20, // Seattle, WA (USA) - "ap-south": 9, // Singapore - "sg-sin-2": 48, // Singapore 2 - "se-sto": 23, // Stockholm (Sweden) - "ap-southeast": 16, // Sydney (Australia) - "ap-northeast": 11, // Tokyo (Japan) - "ca-central": 15, // Toronto (Canada) - "us-iad": 17, // Washington, DC (USA) - } - BGPNodeSelectorFlagInputLen int = 2 -) - -// getExistingSharedIPsInCluster determines the list of addresses to share on nodes by checking the -// CiliumLoadBalancerIPPools created by the CCM in createCiliumLBIPPool -// NOTE: Cilium CRDs must be installed for this to work -func (l *loadbalancers) getExistingSharedIPsInCluster(ctx context.Context) ([]string, error) { - addrs := []string{} - if err := l.retrieveCiliumClientset(); err != nil { - return addrs, err - } - pools, err := l.ciliumClient.CiliumLoadBalancerIPPools().List(ctx, metav1.ListOptions{ - LabelSelector: "app.kubernetes.io/managed-by=linode-ccm", - }) - if err != nil { - return addrs, err - } - for _, pool := range pools.Items { - for _, block := range pool.Spec.Blocks { - addrs = append(addrs, strings.TrimSuffix(string(block.Cidr), "/32")) - } - } - return addrs, nil -} - -func (l *loadbalancers) getExistingSharedIPs(ctx context.Context, ipHolder *linodego.Instance) ([]string, error) { - if ipHolder == nil { - return nil, nil - } - ipHolderAddrs, err := l.client.GetInstanceIPAddresses(ctx, ipHolder.ID) - if err != nil { - return nil, err - } - addrs := make([]string, 0, len(ipHolderAddrs.IPv4.Public)) - for _, addr := range ipHolderAddrs.IPv4.Public { - addrs = append(addrs, addr.Address) - } - return addrs, nil -} - -// shareIPs shares the given list of IP addresses on the given Node -func (l *loadbalancers) shareIPs(ctx context.Context, addrs []string, node *v1.Node) error { - nodeLinodeID, err := ccmUtils.ParseProviderID(node.Spec.ProviderID) - if err != nil { - return err - } - if err = l.retrieveKubeClient(); err != nil { - return err - } - if err = l.client.ShareIPAddresses(ctx, linodego.IPAddressesShareOptions{ - IPs: addrs, - LinodeID: nodeLinodeID, - }); err != nil { - return err - } - // need to make sure node is up-to-date - node, err = l.kubeClient.CoreV1().Nodes().Get(ctx, node.Name, metav1.GetOptions{}) - if err != nil { - return err - } - if node.Labels == nil { - node.Labels = make(map[string]string) - } - node.Labels[annotations.AnnLinodeNodeIPSharingUpdated] = "true" - retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { - _, err := l.kubeClient.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) - return err - }) - if retryErr != nil { - klog.Infof("could not update Node: %s", retryErr.Error()) - return retryErr - } - - klog.Infof("shared IPs %v on Linode %d", addrs, nodeLinodeID) - - return nil -} - -// handleIPSharing makes sure that the appropriate Nodes that are labeled to -// perform IP sharing (via a specified node selector) have the expected IPs shared -// in the event that a Node joins the cluster after the LoadBalancer Service already -// exists -func (l *loadbalancers) handleIPSharing(ctx context.Context, node *v1.Node, ipHolderSuffix string) error { - // ignore cases where the provider ID has been set - if node.Spec.ProviderID == "" { - klog.Info("skipping IP while providerID is unset") - return nil - } - // If performing Service load-balancing via IP sharing + BGP, check for a special annotation - // added by the CCM gets set when load-balancer IPs have been successfully shared on the node - if options.Options.BGPNodeSelector != "" { - kv := strings.Split(options.Options.BGPNodeSelector, "=") - // Check if node should be participating in IP sharing via the given selector - if val, ok := node.Labels[kv[0]]; !ok || len(kv) != 2 || val != kv[1] { - // not a selected Node - return nil - } - } else if _, ok := node.Labels[commonControlPlaneLabel]; ok { - // If there is no node selector specified, default to sharing across worker nodes only - return nil - } - // check if node has been updated with IPs to share - if _, foundIpSharingUpdatedLabel := node.Labels[annotations.AnnLinodeNodeIPSharingUpdated]; foundIpSharingUpdatedLabel { - // IPs are already shared on the Node - return nil - } - // Get the IPs to be shared on the Node and configure sharing. - // This also annotates the node that IPs have been shared. - inClusterAddrs, err := l.getExistingSharedIPsInCluster(ctx) - if err != nil { - klog.Infof("error getting shared IPs in cluster: %s", err.Error()) - return err - } - // if any of the addrs don't exist on the ip-holder (e.g. someone manually deleted it outside the CCM), - // we need to exclude that from the list - // TODO: also clean up the CiliumLoadBalancerIPPool for that missing IP if that happens - ipHolder, err := l.getIPHolder(ctx, ipHolderSuffix) - if err != nil { - return err - } - ipHolderAddrs, err := l.getExistingSharedIPs(ctx, ipHolder) - if err != nil { - klog.Infof("error getting shared IPs in cluster: %s", err.Error()) - return err - } - addrs := []string{} - for _, i := range inClusterAddrs { - if slices.Contains(ipHolderAddrs, i) { - addrs = append(addrs, i) - } - } - if err = l.shareIPs(ctx, addrs, node); err != nil { - klog.Infof("error sharing IPs: %s", err.Error()) - return err - } - - return nil -} - -// createSharedIP requests an additional IP that can be shared on Nodes to support -// loadbalancing via Cilium LB IPAM + BGP Control Plane. -func (l *loadbalancers) createSharedIP(ctx context.Context, nodes []*v1.Node, ipHolderSuffix string) (string, error) { - ipHolder, err := l.ensureIPHolder(ctx, ipHolderSuffix) - if err != nil { - return "", err - } - - newSharedIP, err := l.client.AddInstanceIPAddress(ctx, ipHolder.ID, linodego.InstanceIPAddOptions{Public: true}) - if err != nil { - return "", err - } - - // need to retrieve existing public IPs on the IP holder since ShareIPAddresses - // expects the full list of IPs to be shared - inClusterAddrs, err := l.getExistingSharedIPsInCluster(ctx) - if err != nil { - return "", err - } - // if any of the addrs don't exist on the ip-holder (e.g. someone manually deleted it outside the CCM), - // we need to exclude that from the list - // TODO: also clean up the CiliumLoadBalancerIPPool for that missing IP if that happens - ipHolderAddrs, err := l.getExistingSharedIPs(ctx, ipHolder) - if err != nil { - klog.Infof("error getting shared IPs in cluster: %s", err.Error()) - return "", err - } - addrs := []string{newSharedIP.Address} - for _, i := range inClusterAddrs { - if slices.Contains(ipHolderAddrs, i) { - addrs = append(addrs, i) - } - } - - // share the IPs with nodes participating in Cilium BGP peering - if options.Options.BGPNodeSelector == "" { - for _, node := range nodes { - if _, ok := node.Labels[commonControlPlaneLabel]; !ok { - if err = l.shareIPs(ctx, addrs, node); err != nil { - return "", err - } - } - } - } else { - kv := strings.Split(options.Options.BGPNodeSelector, "=") - for _, node := range nodes { - if val, ok := node.Labels[kv[0]]; ok && len(kv) == 2 && val == kv[1] { - if err = l.shareIPs(ctx, addrs, node); err != nil { - return "", err - } - } - } - } - - return newSharedIP.Address, nil -} - -// deleteSharedIP cleans up the shared IP for a LoadBalancer Service if it was assigned -// by Cilium LB IPAM, removing it from the ip-holder -func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) error { - err := l.retrieveKubeClient() - if err != nil { - return err - } - nodeList, err := l.kubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{ - LabelSelector: options.Options.BGPNodeSelector, - }) - if err != nil { - return err - } - bgpNodes := nodeList.Items - - serviceNn := getServiceNn(service) - var ipHolderSuffix string - if options.Options.IpHolderSuffix != "" { - ipHolderSuffix = options.Options.IpHolderSuffix - klog.V(3).Infof("using parameter-based IP Holder suffix %s for Service %s", ipHolderSuffix, serviceNn) - } - - ipHolder, err := l.getIPHolder(ctx, ipHolderSuffix) - if err != nil { - // return error or nil if not found since no IP holder means there - // is no IP to reclaim - return ccmUtils.IgnoreLinodeAPIError(err, http.StatusNotFound) - } - svcIngress := service.Status.LoadBalancer.Ingress - if len(svcIngress) > 0 && ipHolder != nil { - var nodeLinodeID int - - for _, ingress := range svcIngress { - // delete the shared IP on the Linodes it's shared on - for _, node := range bgpNodes { - nodeLinodeID, err = ccmUtils.ParseProviderID(node.Spec.ProviderID) - if err != nil { - return err - } - err = l.client.DeleteInstanceIPAddress(ctx, nodeLinodeID, ingress.IP) - if ccmUtils.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { - return err - } - } - - // finally delete the shared IP on the ip-holder - err = l.client.DeleteInstanceIPAddress(ctx, ipHolder.ID, ingress.IP) - if ccmUtils.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { - return err - } - } - } - - return nil -} - -// To hold the IP in lieu of a proper IP reservation system, a special Nanode is -// created but not booted and used to hold all shared IPs. -func (l *loadbalancers) ensureIPHolder(ctx context.Context, suffix string) (*linodego.Instance, error) { - ipHolder, err := l.getIPHolder(ctx, suffix) - if err != nil { - return nil, err - } - if ipHolder != nil { - return ipHolder, nil - } - label := generateClusterScopedIPHolderLinodeName(l.zone, suffix) - ipHolder, err = l.client.CreateInstance(ctx, linodego.InstanceCreateOptions{ - Region: l.zone, - Type: "g6-nanode-1", - Label: label, - RootPass: uuid.NewString(), - Image: "linode/ubuntu22.04", - Booted: ptr.To(false), - }) - if err != nil { - if linodego.ErrHasStatus(err, http.StatusBadRequest) && strings.Contains(err.Error(), "Label must be unique") { - // TODO (rk): should we handle more status codes on error? - klog.Errorf("failed to create new IP Holder instance %s since it already exists: %s", label, err.Error()) - return nil, err - } - return nil, err - } - klog.Infof("created new IP Holder instance %s", label) - - return ipHolder, nil -} - -func (l *loadbalancers) getIPHolder(ctx context.Context, suffix string) (*linodego.Instance, error) { - // even though we have updated the naming convention, leaving this in ensures we have backwards compatibility - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, l.zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - panic("this should not have failed") - } - var ipHolder *linodego.Instance - // TODO (rk): should we switch to using GET instead of LIST? we would be able to wrap logic around errors - linodes, err := l.client.ListInstances(ctx, linodego.NewListOptions(1, string(rawFilter))) - if err != nil { - return nil, err - } - if len(linodes) == 0 { - // since a list that returns 0 results has a 200/OK status code (no error) - - // we assume that either - // a) an ip holder instance does not exist yet - // or - // b) another cluster already holds the linode grant to an ip holder using the old naming convention - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(l.zone, suffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - panic("this should not have failed") - } - linodes, err = l.client.ListInstances(ctx, linodego.NewListOptions(1, string(rawFilter))) - if err != nil { - return nil, err - } - } - if len(linodes) > 0 { - ipHolder = &linodes[0] - } - return ipHolder, nil -} - -// generateClusterScopedIPHolderLinodeName attempts to generate a unique name for the IP Holder -// instance used alongside Cilium LoadBalancers and Shared IPs for Kubernetes Services. -// If the `--ip-holder-suffix` arg is passed when running Linode CCM, `suffix` is set to that value. -func generateClusterScopedIPHolderLinodeName(zone, suffix string) (label string) { - // since Linode CCM consumers are varied, we require a method of providing a - // suffix that does not rely on the use of a specific product (ex. LKE) to - // have a specific piece of metadata (ex. annotation(s), label(s) ) present to key off of. - - if suffix == "" { - // this avoids a trailing hyphen if suffix is empty (ex. linode-ccm-ip-holder-us-ord-) - label = fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone) - } else { - label = fmt.Sprintf("%s-%s-%s", ipHolderLabelPrefix, zone, suffix) - } - klog.V(5).Infof("generated IP Holder Linode label: %s", label) - return label -} - -func (l *loadbalancers) retrieveCiliumClientset() error { - if l.ciliumClient != nil { - return nil - } - var ( - kubeConfig *rest.Config - err error - ) - kubeconfigFlag := options.Options.KubeconfigFlag - if kubeconfigFlag == nil || kubeconfigFlag.Value.String() == "" { - kubeConfig, err = rest.InClusterConfig() - } else { - kubeConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfigFlag.Value.String()) - } - if err != nil { - return err - } - l.ciliumClient, err = ciliumclient.NewForConfig(kubeConfig) - - return err -} - -// for LoadBalancer Services not backed by a NodeBalancer, a CiliumLoadBalancerIPPool resource -// will be created specifically for the Service with the requested shared IP -// NOTE: Cilium CRDs must be installed for this to work -func (l *loadbalancers) createCiliumLBIPPool(ctx context.Context, service *v1.Service, sharedIP string) (*v2alpha1.CiliumLoadBalancerIPPool, error) { - if err := l.retrieveCiliumClientset(); err != nil { - return nil, err - } - ciliumLBIPPool := &v2alpha1.CiliumLoadBalancerIPPool{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s-pool", service.Namespace, service.Name), - Labels: map[string]string{"app.kubernetes.io/managed-by": "linode-ccm"}, - }, - Spec: v2alpha1.CiliumLoadBalancerIPPoolSpec{ - ServiceSelector: &slimv1.LabelSelector{ - MatchLabels: map[string]slimv1.MatchLabelsValue{ - "io.kubernetes.service.namespace": service.Namespace, - "io.kubernetes.service.name": service.Name, - }, - }, - Blocks: []v2alpha1.CiliumLoadBalancerIPPoolIPBlock{{ - Cidr: v2alpha1.IPv4orIPv6CIDR(fmt.Sprintf("%s/32", sharedIP)), - }}, - Disabled: false, - }, - } - - return l.ciliumClient.CiliumLoadBalancerIPPools().Create(ctx, ciliumLBIPPool, metav1.CreateOptions{}) -} - -// NOTE: Cilium CRDs must be installed for this to work -func (l *loadbalancers) deleteCiliumLBIPPool(ctx context.Context, service *v1.Service) error { - if err := l.retrieveCiliumClientset(); err != nil { - return err - } - - return l.ciliumClient.CiliumLoadBalancerIPPools().Delete( - ctx, - fmt.Sprintf("%s-%s-pool", service.Namespace, service.Name), - metav1.DeleteOptions{}, - ) -} - -// NOTE: Cilium CRDs must be installed for this to work -func (l *loadbalancers) getCiliumLBIPPool(ctx context.Context, service *v1.Service) (*v2alpha1.CiliumLoadBalancerIPPool, error) { - if err := l.retrieveCiliumClientset(); err != nil { - return nil, err - } - - return l.ciliumClient.CiliumLoadBalancerIPPools().Get( - ctx, - fmt.Sprintf("%s-%s-pool", service.Namespace, service.Name), - metav1.GetOptions{}, - ) -} - -// NOTE: Cilium CRDs must be installed for this to work -func (l *loadbalancers) ensureCiliumBGPPeeringPolicy(ctx context.Context) error { - if raw, ok := os.LookupEnv("BGP_CUSTOM_ID_MAP"); ok && raw != "" { - klog.Info("BGP_CUSTOM_ID_MAP env variable specified, using it instead of the default region map") - if err := json.Unmarshal([]byte(raw), ®ionIDMap); err != nil { - return err - } - } - regionID, ok := regionIDMap[l.zone] - if !ok { - return fmt.Errorf("unsupported region for BGP: %s", l.zone) - } - if err := l.retrieveCiliumClientset(); err != nil { - return err - } - // check if policy already exists - policy, err := l.ciliumClient.CiliumBGPPeeringPolicies().Get(ctx, ciliumBGPPeeringPolicyName, metav1.GetOptions{}) - if err != nil && !k8serrors.IsNotFound(err) { - klog.Infof("Failed to get CiliumBGPPeeringPolicy: %s", err.Error()) - return err - } - // if the CiliumBGPPeeringPolicy doesn't exist, it's not nil, just empty - if policy != nil && policy.Name != "" { - return nil - } - - // otherwise create it - var nodeSelector slimv1.LabelSelector - // If no BGPNodeSelector is specified, select all worker nodes. - if options.Options.BGPNodeSelector == "" { - nodeSelector = slimv1.LabelSelector{ - MatchExpressions: []slimv1.LabelSelectorRequirement{ - { - Key: commonControlPlaneLabel, - Operator: slimv1.LabelSelectorOpDoesNotExist, - }, - }, - } - } else { - kv := strings.Split(options.Options.BGPNodeSelector, "=") - if len(kv) != BGPNodeSelectorFlagInputLen { - return fmt.Errorf("invalid node selector %s", options.Options.BGPNodeSelector) - } - - nodeSelector = slimv1.LabelSelector{MatchLabels: map[string]string{kv[0]: kv[1]}} - } - - ciliumBGPPeeringPolicy := &v2alpha1.CiliumBGPPeeringPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: ciliumBGPPeeringPolicyName, - }, - Spec: v2alpha1.CiliumBGPPeeringPolicySpec{ - NodeSelector: &nodeSelector, - VirtualRouters: []v2alpha1.CiliumBGPVirtualRouter{{ - LocalASN: 65001, - ExportPodCIDR: ptr.To(true), - ServiceSelector: &slimv1.LabelSelector{ - // By default, virtual routers will not announce any services. - // This selector makes it so all services within the cluster are announced. - // See https://docs.cilium.io/en/stable/network/bgp-control-plane/#service-announcements - // for more information. - MatchExpressions: []slimv1.LabelSelectorRequirement{{ - Key: "somekey", - Operator: slimv1.LabelSelectorOpNotIn, - Values: []string{"never-used-value"}, - }}, - }, - }}, - }, - } - bgpPeerPrefix := defaultBGPPeerPrefix - if raw, ok := os.LookupEnv("BGP_PEER_PREFIX"); ok && raw != "" { - klog.Info("BGP_PEER_PREFIX env variable specified, using it instead of the default bgpPeer prefix") - bgpPeerPrefix = raw - } - // As in https://github.com/linode/lelastic, there are 4 peers per DC - for i := 1; i <= 4; i++ { - neighbor := v2alpha1.CiliumBGPNeighbor{ - PeerAddress: fmt.Sprintf("%s:%d:34::%d/64", bgpPeerPrefix, regionID, i), - PeerASN: 65000, - EBGPMultihopTTL: ptr.To(int32(10)), - ConnectRetryTimeSeconds: ptr.To(int32(5)), - HoldTimeSeconds: ptr.To(int32(9)), - KeepAliveTimeSeconds: ptr.To(int32(3)), - AdvertisedPathAttributes: []v2alpha1.CiliumBGPPathAttributes{ - { - SelectorType: "CiliumLoadBalancerIPPool", - Communities: &v2alpha1.BGPCommunities{ - Standard: []v2alpha1.BGPStandardCommunity{"65000:1", "65000:2"}, - }, - }, - }, - } - ciliumBGPPeeringPolicy.Spec.VirtualRouters[0].Neighbors = append(ciliumBGPPeeringPolicy.Spec.VirtualRouters[0].Neighbors, neighbor) - } - - klog.Info("Creating CiliumBGPPeeringPolicy") - _, err = l.ciliumClient.CiliumBGPPeeringPolicies().Create(ctx, ciliumBGPPeeringPolicy, metav1.CreateOptions{}) - - return err -} diff --git a/cloud/linode/cilium_loadbalancers_test.go b/cloud/linode/cilium_loadbalancers_test.go deleted file mode 100644 index a85a91a3..00000000 --- a/cloud/linode/cilium_loadbalancers_test.go +++ /dev/null @@ -1,817 +0,0 @@ -package linode - -import ( - "encoding/json" - "fmt" - "net" - "testing" - - ciliumfake "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/fake" - fakev2alpha1 "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/typed/cilium.io/v2alpha1/fake" - "github.com/golang/mock/gomock" - "github.com/linode/linodego/v2" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - k8sfake "k8s.io/client-go/kubernetes/fake" - - "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" - "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" - ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" -) - -const ( - clusterName string = "linodelb" - nodeSelector string = "cilium-bgp-peering=true" - dummyIP string = "45.76.101.26" -) - -var ( - zone = "us-ord" - nodes = []*v1.Node{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "node-1", - Labels: map[string]string{"cilium-bgp-peering": "true"}, - }, - Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 11111), - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "node-2", - Labels: map[string]string{"cilium-bgp-peering": "true"}, - }, - Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 22222), - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "node-3", - }, - Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 33333), - }, - }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "node-control", - Labels: map[string]string{ - commonControlPlaneLabel: "", - }, - }, - Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 44444), - }, - }, - } - additionalNodes = []*v1.Node{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "node-5", - Labels: map[string]string{"cilium-bgp-peering": "true"}, - }, - Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 55555), - }, - }, - } - publicIPv4 = net.ParseIP("45.76.101.25") - oldIpHolderInstance = linodego.Instance{ - ID: 12345, - Label: fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone), - Type: "g6-standard-1", - Region: "us-west", - IPv4: []net.IP{publicIPv4}, - } - newIpHolderInstance = linodego.Instance{} -) - -func TestCiliumCCMLoadBalancers(t *testing.T) { - testCases := []struct { - name string - f func(*testing.T, *mocks.MockClient) - }{ - { - name: "Create Cilium Load Balancer Without BGP Node Labels specified", - f: testNoBGPNodeLabel, - }, - { - name: "Create Cilium Load Balancer with unsupported region", - f: testUnsupportedRegion, - }, - { - name: "Create Cilium Load Balancer With explicit loadBalancerClass and existing IP holder nanode with old IP Holder naming convention", - f: testCreateWithExistingIPHolderWithOldIpHolderNamingConvention, - }, - { - name: "Create Cilium Load Balancer With explicit loadBalancerClass and existing IP holder nanode with new IP Holder naming convention", - f: testCreateWithExistingIPHolderWithNewIpHolderNamingConvention, - }, - { - name: "Create Cilium Load Balancer With explicit loadBalancerClass and existing IP holder nanode with new IP Holder naming convention and 63 char long suffix", - f: testCreateWithExistingIPHolderWithNewIpHolderNamingConventionUsingLongSuffix, - }, - { - name: "Create Cilium Load Balancer With no existing IP holder nanode and short suffix", - f: testCreateWithNoExistingIPHolderUsingShortSuffix, - }, - { - name: "Create Cilium Load Balancer With no existing IP holder nanode and no suffix", - f: testCreateWithNoExistingIPHolderUsingNoSuffix, - }, - { - name: "Create Cilium Load Balancer With no existing IP holder nanode and 63 char long suffix", - f: testCreateWithNoExistingIPHolderUsingLongSuffix, - }, - { - name: "Delete Cilium Load Balancer With Old IP Holder Naming Convention", - f: testEnsureCiliumLoadBalancerDeletedWithOldIpHolderNamingConvention, - }, - { - name: "Delete Cilium Load Balancer With New IP Holder Naming Convention", - f: testEnsureCiliumLoadBalancerDeletedWithNewIpHolderNamingConvention, - }, - { - name: "Add node to existing Cilium Load Balancer With Old IP Holder Naming Convention", - f: testCiliumUpdateLoadBalancerAddNodeWithOldIpHolderNamingConvention, - }, - { - name: "Add node to existing Cilium Load Balancer With New IP Holder Naming Convention", - f: testCiliumUpdateLoadBalancerAddNodeWithNewIpHolderNamingConvention, - }, - } - //nolint: paralleltest // two tests use t.Setenv, which fails after t.Parallel() call - for _, tc := range testCases { - ctrl := gomock.NewController(t) - mc := mocks.NewMockClient(ctrl) - t.Run(tc.name, func(t *testing.T) { - defer ctrl.Finish() - tc.f(t, mc) - }) - } -} - -func createTestService() *v1.Service { - svc := &v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: randString(), - Namespace: "test-ns", - UID: "foobar123", - }, - Spec: v1.ServiceSpec{ - Ports: []v1.ServicePort{ - { - Name: randString(), - Protocol: "TCP", - Port: int32(80), - NodePort: int32(30000), - }, - { - Name: randString(), - Protocol: "TCP", - Port: int32(8080), - NodePort: int32(30001), - }, - }, - }, - } - - return svc -} - -func addService(t *testing.T, kubeClient kubernetes.Interface, svc *v1.Service) { - t.Helper() - - _, err := kubeClient.CoreV1().Services(svc.Namespace).Create(t.Context(), svc, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("failed to add Service: %v", err) - } -} - -func addNodes(t *testing.T, kubeClient kubernetes.Interface, nodes []*v1.Node) { - t.Helper() - - for _, node := range nodes { - _, err := kubeClient.CoreV1().Nodes().Create(t.Context(), node, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("failed to add Node: %v", err) - } - } -} - -func createNewIpHolderInstance() linodego.Instance { - return linodego.Instance{ - ID: 123456, - Label: generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix), - Type: "g6-standard-1", - Region: "us-west", - IPv4: []net.IP{publicIPv4}, - } -} - -func testNoBGPNodeLabel(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = "" - options.Options.IpHolderSuffix = clusterName - t.Setenv("BGP_PEER_PREFIX", "2600:3cef") - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - dummySharedIP := dummyIP - mc.EXPECT().CreateInstance(gomock.Any(), gomock.Any()).Times(1).Return(&newIpHolderInstance, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), newIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), newIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 33333, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testUnsupportedRegion(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - svc := createTestService() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - lb := &loadbalancers{mc, "us-foobar", kubeClient, ciliumClient, ciliumLBType} - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err == nil { - t.Fatal("expected not nil error") - } - if lbStatus != nil { - t.Fatalf("expected a nil lbStatus, got %v", lbStatus) - } - - // Use BGP custom id map - t.Setenv("BGP_CUSTOM_ID_MAP", "{'us-foobar': 2}") - lb = &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - lbStatus, err = lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err == nil { - t.Fatal("expected not nil error") - } - if lbStatus != nil { - t.Fatalf("expected a nil lbStatus, got %v", lbStatus) - } -} - -func testCreateWithExistingIPHolderWithOldIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{oldIpHolderInstance}, nil) - dummySharedIP := dummyIP - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), oldIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), oldIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testCreateWithExistingIPHolderWithNewIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = clusterName - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{oldIpHolderInstance}, nil) - dummySharedIP := dummyIP - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), oldIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), oldIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testCreateWithExistingIPHolderWithNewIpHolderNamingConventionUsingLongSuffix(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = "OaTJrRuufacHVougjwkpBpmstiqvswvBNEMWXsRYfMBTCkKIUTXpbGIcIbDWSQp" - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{oldIpHolderInstance}, nil) - dummySharedIP := dummyIP - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), oldIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), oldIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testCreateWithNoExistingIPHolderUsingNoSuffix(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = "" - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - dummySharedIP := dummyIP - mc.EXPECT().CreateInstance(gomock.Any(), gomock.Any()).Times(1).Return(&newIpHolderInstance, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), newIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), newIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testCreateWithNoExistingIPHolderUsingShortSuffix(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = clusterName - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - dummySharedIP := dummyIP - mc.EXPECT().CreateInstance(gomock.Any(), gomock.Any()).Times(1).Return(&newIpHolderInstance, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), newIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), newIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testCreateWithNoExistingIPHolderUsingLongSuffix(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = "OaTJrRuufacHVougjwkpBpmstiqvswvBNEMWXsRYfMBTCkKIUTXpbGIcIbDWSQp" - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - dummySharedIP := dummyIP - mc.EXPECT().CreateInstance(gomock.Any(), gomock.Any()).Times(1).Return(&newIpHolderInstance, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), newIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), newIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testEnsureCiliumLoadBalancerDeletedWithOldIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - svc := createTestService() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - dummySharedIP := dummyIP - svc.Status.LoadBalancer = v1.LoadBalancerStatus{Ingress: []v1.LoadBalancerIngress{{IP: dummySharedIP}}} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{oldIpHolderInstance}, nil) - mc.EXPECT().DeleteInstanceIPAddress(gomock.Any(), 11111, dummySharedIP).Times(1).Return(nil) - mc.EXPECT().DeleteInstanceIPAddress(gomock.Any(), 22222, dummySharedIP).Times(1).Return(nil) - mc.EXPECT().DeleteInstanceIPAddress(gomock.Any(), oldIpHolderInstance.ID, dummySharedIP).Times(1).Return(nil) - - err = lb.EnsureLoadBalancerDeleted(t.Context(), clusterName, svc) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } -} - -func testEnsureCiliumLoadBalancerDeletedWithNewIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = clusterName - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - dummySharedIP := dummyIP - svc.Status.LoadBalancer = v1.LoadBalancerStatus{Ingress: []v1.LoadBalancerIngress{{IP: dummySharedIP}}} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{newIpHolderInstance}, nil) - mc.EXPECT().DeleteInstanceIPAddress(gomock.Any(), 11111, dummySharedIP).Times(1).Return(nil) - mc.EXPECT().DeleteInstanceIPAddress(gomock.Any(), 22222, dummySharedIP).Times(1).Return(nil) - mc.EXPECT().DeleteInstanceIPAddress(gomock.Any(), newIpHolderInstance.ID, dummySharedIP).Times(1).Return(nil) - - err = lb.EnsureLoadBalancerDeleted(t.Context(), clusterName, svc) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } -} - -func testCiliumUpdateLoadBalancerAddNodeWithOldIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - svc := createTestService() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{oldIpHolderInstance}, nil) - dummySharedIP := dummyIP - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), oldIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), oldIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } - - // Now add another node to the cluster and assert that it gets the shared IP - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{oldIpHolderInstance}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), oldIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 55555, - }).Times(1) - addNodes(t, kubeClient, additionalNodes) - - err = lb.UpdateLoadBalancer(t.Context(), clusterName, svc, additionalNodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } -} - -func testCiliumUpdateLoadBalancerAddNodeWithNewIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { - t.Helper() - - options.Options.BGPNodeSelector = nodeSelector - options.Options.IpHolderSuffix = clusterName - svc := createTestService() - newIpHolderInstance = createNewIpHolderInstance() - - kubeClient := k8sfake.NewClientset() - ciliumClientset := ciliumfake.NewSimpleClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &ciliumClientset.Fake} - addService(t, kubeClient, svc) - addNodes(t, kubeClient, nodes) - lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err := json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{newIpHolderInstance}, nil) - dummySharedIP := dummyIP - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), newIpHolderInstance.ID, linodego.InstanceIPAddOptions{Public: true}).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), newIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 11111, - }).Times(1) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 22222, - }).Times(1) - - lbStatus, err := lb.EnsureLoadBalancer(t.Context(), clusterName, svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } - - // Now add another node to the cluster and assert that it gets the shared IP - filter = map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} - rawFilter, err = json.Marshal(filter) - if err != nil { - t.Errorf("json marshal error: %v", err) - } - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{newIpHolderInstance}, nil) - - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), newIpHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []linodego.InstanceIP{{Address: publicIPv4.String()}, {Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ - IPs: []string{dummySharedIP}, - LinodeID: 55555, - }).Times(1) - addNodes(t, kubeClient, additionalNodes) - - err = lb.UpdateLoadBalancer(t.Context(), clusterName, svc, additionalNodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } -} diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index 7d8caa99..85a4cd60 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -35,7 +35,7 @@ const ( tokenHealthCheckPeriod = 5 * time.Minute ) -var supportedLoadBalancerTypes = []string{ciliumLBType, nodeBalancerLBType} +var supportedLoadBalancerTypes = []string{nodeBalancerLBType} type linodeCloud struct { client client.Client @@ -47,7 +47,6 @@ type linodeCloud struct { var ( instanceCache *services.Instances - ipHolderCharLimit int = 23 NodeBalancerPrefixCharLimit int = 19 ) @@ -244,6 +243,19 @@ func newCloud() (cloudprovider.Interface, error) { return nil, fmt.Errorf("routes client was not created successfully: %w", err) } + if options.Options.LoadBalancerType == ciliumLBType { + klog.Warningf("--load-balancer-type=%s is deprecated and has no effect; using %s", ciliumLBType, nodeBalancerLBType) + options.Options.LoadBalancerType = nodeBalancerLBType + } + + if options.Options.BGPNodeSelector != "" { + klog.Warning("--bgp-node-selector is deprecated and has no effect; it is retained for backwards compatibility") + } + + if options.Options.IpHolderSuffix != "" { + klog.Warning("--ip-holder-suffix is deprecated and has no effect; it is retained for backwards compatibility") + } + if options.Options.LoadBalancerType != "" && !slices.Contains(supportedLoadBalancerTypes, options.Options.LoadBalancerType) { return nil, fmt.Errorf( "unsupported default load-balancer type %s. options.Options are %v", @@ -252,16 +264,6 @@ func newCloud() (cloudprovider.Interface, error) { ) } - if options.Options.IpHolderSuffix != "" { - klog.Infof("Using IP holder suffix '%s'\n", options.Options.IpHolderSuffix) - } - - if len(options.Options.IpHolderSuffix) > ipHolderCharLimit { - msg := fmt.Sprintf("ip-holder-suffix must be %d characters or less: %s is %d characters\n", ipHolderCharLimit, options.Options.IpHolderSuffix, len(options.Options.IpHolderSuffix)) - klog.Error(msg) - return nil, fmt.Errorf("%s", msg) - } - if len(options.Options.NodeBalancerPrefix) > NodeBalancerPrefixCharLimit { msg := fmt.Sprintf("nodebalancer-prefix must be %d characters or less: %s is %d characters\n", NodeBalancerPrefixCharLimit, options.Options.NodeBalancerPrefix, len(options.Options.NodeBalancerPrefix)) klog.Error(msg) diff --git a/cloud/linode/cloud_test.go b/cloud/linode/cloud_test.go index 00087bbf..1aee6492 100644 --- a/cloud/linode/cloud_test.go +++ b/cloud/linode/cloud_test.go @@ -209,17 +209,25 @@ func TestNewCloud(t *testing.T) { assert.Error(t, err, "expected error if incorrect loadbalancertype is set") }) - t.Run("should fail if ipholdersuffix is longer than 23 chars", func(t *testing.T) { - suffix := options.Options.IpHolderSuffix - options.Options.IpHolderSuffix = strings.Repeat("a", 24) - rtEnabled := options.Options.EnableRouteController + t.Run("should accept deprecated cilium-bgp loadbalancer type as nodebalancer", func(t *testing.T) { + loadBalancerType := options.Options.LoadBalancerType + routeControllerEnabled := options.Options.EnableRouteController + bgpNodeSelector := options.Options.BGPNodeSelector + ipHolderSuffix := options.Options.IpHolderSuffix + options.Options.LoadBalancerType = ciliumLBType options.Options.EnableRouteController = false + options.Options.BGPNodeSelector = "cilium-bgp-peering=true" + options.Options.IpHolderSuffix = "legacy-cluster" defer func() { - options.Options.IpHolderSuffix = suffix - options.Options.EnableRouteController = rtEnabled + options.Options.LoadBalancerType = loadBalancerType + options.Options.EnableRouteController = routeControllerEnabled + options.Options.BGPNodeSelector = bgpNodeSelector + options.Options.IpHolderSuffix = ipHolderSuffix }() + _, err := newCloud() - assert.Error(t, err, "expected error if ipholdersuffix is longer than 23 chars") + require.NoError(t, err) + assert.Equal(t, nodeBalancerLBType, options.Options.LoadBalancerType) }) t.Run("should fail if nodebalancer-prefix is longer than 19 chars", func(t *testing.T) { diff --git a/cloud/linode/loadbalancers.go b/cloud/linode/loadbalancers.go index 549db380..3a1fc50e 100644 --- a/cloud/linode/loadbalancers.go +++ b/cloud/linode/loadbalancers.go @@ -14,11 +14,8 @@ import ( "strings" "time" - "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" - ciliumclient "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/typed/cilium.io/v2alpha1" "github.com/linode/linodego/v2" v1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -104,11 +101,9 @@ func (e lbNotFoundError) Error() string { } type loadbalancers struct { - client client.Client - zone string - kubeClient kubernetes.Interface - ciliumClient ciliumclient.CiliumV2alpha1Interface - loadBalancerType string + client client.Client + zone string + kubeClient kubernetes.Interface } type portConfigAnnotation struct { @@ -132,7 +127,7 @@ type portConfig struct { // newLoadbalancers returns a cloudprovider.LoadBalancer whose concrete type is a *loadbalancer. func newLoadbalancers(client client.Client, zone string) cloudprovider.LoadBalancer { - return &loadbalancers{client: client, zone: zone, loadBalancerType: options.Options.LoadBalancerType} + return &loadbalancers{client: client, zone: zone} } func (l *loadbalancers) getNodeBalancerForService(ctx context.Context, service *v1.Service) (*linodego.NodeBalancer, error) { @@ -241,13 +236,6 @@ func (l *loadbalancers) GetLoadBalancer(ctx context.Context, clusterName string, sentry.SetTag(ctx, "cluster_name", clusterName) sentry.SetTag(ctx, "service", service.Name) - // Handle LoadBalancers backed by Cilium - if l.loadBalancerType == ciliumLBType { - return &v1.LoadBalancerStatus{ - Ingress: service.Status.LoadBalancer.Ingress, - }, true, nil - } - nb, err := l.getNodeBalancerForService(ctx, service) if err != nil { var targetError lbNotFoundError @@ -272,56 +260,6 @@ func (l *loadbalancers) EnsureLoadBalancer(ctx context.Context, clusterName stri sentry.SetTag(ctx, "service", service.Name) serviceNn := getServiceNn(service) - // Handle LoadBalancers backed by Cilium - if l.loadBalancerType == ciliumLBType { - klog.Infof("handling LoadBalancer Service %s as %s", serviceNn, ciliumLBClass) - - if err = l.ensureCiliumBGPPeeringPolicy(ctx); err != nil { - klog.Infof("Failed to ensure CiliumBGPPeeringPolicy: %v", err) - return nil, err - } - - // check for existing CiliumLoadBalancerIPPool for service - var pool *v2alpha1.CiliumLoadBalancerIPPool - pool, err = l.getCiliumLBIPPool(ctx, service) - if err != nil && !k8serrors.IsNotFound(err) { - klog.Infof("Failed to get CiliumLoadBalancerIPPool: %s", err.Error()) - return nil, err - } - // if the CiliumLoadBalancerIPPool doesn't exist, it's not nil, instead an empty struct - // gets returned, so we check if this is so via the Name being empty - if pool != nil && pool.Name != "" { - klog.Infof("Cilium LB IP pool %s for Service %s ensured", pool.Name, serviceNn) - // ingress will be set by Cilium - return &v1.LoadBalancerStatus{ - Ingress: service.Status.LoadBalancer.Ingress, - }, nil - } - - var ipHolderSuffix string - if options.Options.IpHolderSuffix != "" { - ipHolderSuffix = options.Options.IpHolderSuffix - klog.Infof("using parameter-based IP Holder suffix %s for Service %s", ipHolderSuffix, serviceNn) - } - - // CiliumLoadBalancerIPPool does not yet exist for the service - var sharedIP string - if sharedIP, err = l.createSharedIP(ctx, nodes, ipHolderSuffix); err != nil { - klog.Errorf("Failed to request shared instance IP: %s", err.Error()) - return nil, err - } - if _, err = l.createCiliumLBIPPool(ctx, service, sharedIP); err != nil { - klog.Infof("Failed to create CiliumLoadBalancerIPPool: %s", err.Error()) - return nil, err - } - - // ingress will be set by Cilium - return &v1.LoadBalancerStatus{ - Ingress: service.Status.LoadBalancer.Ingress, - }, nil - } - - // Handle LoadBalancers backed by NodeBalancers var nb *linodego.NodeBalancer nb, err = l.getNodeBalancerForService(ctx, service) @@ -536,25 +474,6 @@ func (l *loadbalancers) UpdateLoadBalancer(ctx context.Context, clusterName stri sentry.SetTag(ctx, "cluster_name", clusterName) sentry.SetTag(ctx, "service", service.Name) - // handle LoadBalancers backed by Cilium - if l.loadBalancerType == ciliumLBType { - klog.Infof("handling update for LoadBalancer Service %s/%s as %s", service.Namespace, service.Name, ciliumLBClass) - serviceNn := getServiceNn(service) - var ipHolderSuffix string - if options.Options.IpHolderSuffix != "" { - ipHolderSuffix = options.Options.IpHolderSuffix - klog.V(3).Infof("using parameter-based IP Holder suffix %s for Service %s", ipHolderSuffix, serviceNn) - } - - // make sure that IPs are shared properly on the Node if using load-balancers not backed by NodeBalancers - for _, node := range nodes { - if err = l.handleIPSharing(ctx, node, ipHolderSuffix); err != nil { - return err - } - } - return nil - } - // UpdateLoadBalancer is invoked with a nil LoadBalancerStatus; we must fetch the latest // status for NodeBalancer discovery. serviceWithStatus := service.DeepCopy() @@ -623,23 +542,6 @@ func (l *loadbalancers) EnsureLoadBalancerDeleted(ctx context.Context, clusterNa sentry.SetTag(ctx, "cluster_name", clusterName) sentry.SetTag(ctx, "service", service.Name) - // Handle LoadBalancers backed by Cilium - if l.loadBalancerType == ciliumLBType { - klog.Infof("handling LoadBalancer Service %s/%s as %s", service.Namespace, service.Name, ciliumLBClass) - if err := l.deleteSharedIP(ctx, service); err != nil { - return err - } - // delete CiliumLoadBalancerIPPool for service - if err := l.deleteCiliumLBIPPool(ctx, service); err != nil && !k8serrors.IsNotFound(err) { - klog.Infof("Failed to delete CiliumLoadBalancerIPPool") - return err - } - - return nil - } - - // Handle LoadBalancers backed by NodeBalancers - serviceNn := getServiceNn(service) if len(service.Status.LoadBalancer.Ingress) == 0 { diff --git a/cloud/linode/loadbalancers_test.go b/cloud/linode/loadbalancers_test.go index 3bfdc4ef..49b2f02b 100644 --- a/cloud/linode/loadbalancers_test.go +++ b/cloud/linode/loadbalancers_test.go @@ -18,7 +18,6 @@ import ( "strings" "testing" - ciliumclient "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/typed/cilium.io/v2alpha1" "github.com/golang/mock/gomock" "github.com/linode/linodego/v2" v1 "k8s.io/api/core/v1" @@ -5777,11 +5776,9 @@ func Test_LoadbalNodeNameCoercion(t *testing.T) { func Test_loadbalancers_GetLinodeNBType(t *testing.T) { type fields struct { - client client.Client - zone string - kubeClient kubernetes.Interface - ciliumClient ciliumclient.CiliumV2alpha1Interface - loadBalancerType string + client client.Client + zone string + kubeClient kubernetes.Interface } type args struct { service *v1.Service @@ -5796,11 +5793,9 @@ func Test_loadbalancers_GetLinodeNBType(t *testing.T) { { name: "No annotation in service and common as default", fields: fields{ - client: nil, - zone: "", - kubeClient: nil, - ciliumClient: nil, - loadBalancerType: "nodebalancer", + client: nil, + zone: "", + kubeClient: nil, }, args: args{ service: &v1.Service{ @@ -5816,11 +5811,9 @@ func Test_loadbalancers_GetLinodeNBType(t *testing.T) { { name: "No annotation in service and premium as default", fields: fields{ - client: nil, - zone: "", - kubeClient: nil, - ciliumClient: nil, - loadBalancerType: "nodebalancer", + client: nil, + zone: "", + kubeClient: nil, }, args: args{ service: &v1.Service{ @@ -5836,11 +5829,9 @@ func Test_loadbalancers_GetLinodeNBType(t *testing.T) { { name: "Nodebalancer type annotation in service", fields: fields{ - client: nil, - zone: "", - kubeClient: nil, - ciliumClient: nil, - loadBalancerType: "nodebalancer", + client: nil, + zone: "", + kubeClient: nil, }, args: args{ service: &v1.Service{ @@ -5859,11 +5850,9 @@ func Test_loadbalancers_GetLinodeNBType(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &loadbalancers{ - client: tt.fields.client, - zone: tt.fields.zone, - kubeClient: tt.fields.kubeClient, - ciliumClient: tt.fields.ciliumClient, - loadBalancerType: tt.fields.loadBalancerType, + client: tt.fields.client, + zone: tt.fields.zone, + kubeClient: tt.fields.kubeClient, } options.Options.DefaultNBType = string(tt.defaultNB) if got := l.GetLinodeNBType(tt.args.service); !reflect.DeepEqual(got, tt.want) { diff --git a/cloud/linode/options/options.go b/cloud/linode/options/options.go index 08a9c1d5..047645e2 100644 --- a/cloud/linode/options/options.go +++ b/cloud/linode/options/options.go @@ -10,15 +10,16 @@ import ( // We expect it to be initialized with flags external to this package, likely in // main.go var Options struct { - KubeconfigFlag *pflag.Flag - LinodeGoDebug bool - EnableRouteController bool - EnableTokenHealthChecker bool - VPCNames []string - VPCIDs []int - SubnetNames []string - SubnetIDs []int - LoadBalancerType string + KubeconfigFlag *pflag.Flag + LinodeGoDebug bool + EnableRouteController bool + EnableTokenHealthChecker bool + VPCNames []string + VPCIDs []int + SubnetNames []string + SubnetIDs []int + LoadBalancerType string + // Deprecated no-op options retained so existing deployments continue to start. BGPNodeSelector string IpHolderSuffix string LinodeExternalNetwork *net.IPNet diff --git a/cloud/linode/service_controller_test.go b/cloud/linode/service_controller_test.go index 80ba8251..6e1a7f13 100644 --- a/cloud/linode/service_controller_test.go +++ b/cloud/linode/service_controller_test.go @@ -7,15 +7,31 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/util/workqueue" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" - "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" ) +func createTestService() *v1.Service { + return &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: randString(), + Namespace: "test-ns", + UID: "foobar123", + }, + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ + {Name: randString(), Protocol: "TCP", Port: 80, NodePort: 30000}, + {Name: randString(), Protocol: "TCP", Port: 8080, NodePort: 30001}, + }, + }, + } +} + func Test_serviceController_Run(t *testing.T) { // Mock dependencies ctrl := gomock.NewController(t) @@ -71,7 +87,7 @@ func Test_serviceController_processNextDeletion(t *testing.T) { loadbalancers: nil, }, Setup: func(f *fields) { - f.loadbalancers = &loadbalancers{client: f.Client, zone: "test", loadBalancerType: options.Options.LoadBalancerType} + f.loadbalancers = &loadbalancers{client: f.Client, zone: "test"} f.queue = workqueue.NewTypedDelayingQueueWithConfig(workqueue.TypedDelayingQueueConfig[any]{Name: "testQueue"}) f.queue.Add("test") }, @@ -84,7 +100,7 @@ func Test_serviceController_processNextDeletion(t *testing.T) { loadbalancers: nil, }, Setup: func(f *fields) { - f.loadbalancers = &loadbalancers{client: f.Client, zone: "test", loadBalancerType: options.Options.LoadBalancerType} + f.loadbalancers = &loadbalancers{client: f.Client, zone: "test"} f.queue = workqueue.NewTypedDelayingQueueWithConfig(workqueue.TypedDelayingQueueConfig[any]{Name: "testQueue"}) svc := createTestService() f.queue.Add(svc) diff --git a/deploy/chart/templates/clusterrole-rbac.yaml b/deploy/chart/templates/clusterrole-rbac.yaml index 65d0b4d5..4ea7df34 100644 --- a/deploy/chart/templates/clusterrole-rbac.yaml +++ b/deploy/chart/templates/clusterrole-rbac.yaml @@ -31,12 +31,4 @@ rules: - apiGroups: [""] resources: ["services/status"] verbs: ["get", "watch", "list", "update", "patch"] -{{- if .Values.sharedIPLoadBalancing }} - - apiGroups: ["cilium.io"] - resources: ["ciliumloadbalancerippools"] - verbs: ["get", "watch", "list", "update", "create", "delete"] - - apiGroups: ["cilium.io"] - resources: ["ciliumbgppeeringpolicies"] - verbs: ["get", "watch", "list", "create"] -{{- end }} {{- end }} diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index 753933a6..4b31dbba 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -77,7 +77,8 @@ tolerations: operator: Exists effect: NoSchedule -# Options for LoadBalancers backed by shared IPs instead of NodeBalancers +# DEPRECATED: retained as no-ops so upgrades from chart versions that configured +# Cilium BGP do not fail. CCM always uses NodeBalancers. # sharedIPLoadBalancing: # loadBalancerType: cilium-bgp # bgpNodeSelector: diff --git a/docs/configuration/README.md b/docs/configuration/README.md index b78a2bd3..3300d113 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -6,7 +6,6 @@ The Linode Cloud Controller Manager (CCM) offers extensive configuration options 1. **[LoadBalancer Services](loadbalancer.md)** - NodeBalancer implementation - - BGP-based IP sharing - Protocol configuration - Health checks - SSL/TLS setup @@ -32,7 +31,6 @@ The Linode Cloud Controller Manager (CCM) offers extensive configuration options - Cache settings - API configuration - Network settings - - BGP configuration - IPv6 configuration - [See configuration reference](environment.md#flags) diff --git a/docs/configuration/environment.md b/docs/configuration/environment.md index 31b5b4c3..91056871 100644 --- a/docs/configuration/environment.md +++ b/docs/configuration/environment.md @@ -9,7 +9,7 @@ The CCM can be configured using environment variables and flags. Environment var ### Cache Configuration | Variable | Default | Description | -|----------|---------|-------------| +| ---------- | --------- | ------------- | | `LINODE_INSTANCE_CACHE_TTL` | `15` | Default timeout of instance cache in seconds | | `LINODE_ROUTES_CACHE_TTL_SECONDS` | `60` | Default timeout of route cache in seconds | | `LINODE_METADATA_TTL` | `300` | Default linode metadata timeout in seconds | @@ -27,27 +27,25 @@ The CCM can be configured using environment variables and flags. Environment var | Variable | Default | Description | |----------|---------|-------------| | `LINODE_EXTERNAL_SUBNET` | "" | Mark private network as external. Example - `172.24.0.0/16` | -| `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 | Type | Default | Description | -|------|------|---------|-------------| +| ------ | ------ | --------- | ------------- | | `--linodego-debug` | Boolean | `false` | Enables debug output for the LinodeAPI wrapper | | `--enable-route-controller` | Boolean | `false` | Enables route_controller for CCM | | `--enable-token-health-checker` | Boolean | `false` | Enables Linode API token health checker | -| `--vpc-names` | String (comma separated) | | Comma separated VPC names whose routes will be managed by route-controller | +| `--vpc-names` | String (comma separated) | | Comma separated VPC names whose routes will be managed by route-controller | | `--subnet-names` | String (comma separated) | `"default"` | Comma separated subnet names whose routes will be managed by route-controller (requires vpc-names flag) | -| `--vpc-ids` | Int (comma separated) | | Comma separated VPC ids whose routes will be managed by route-controller | -| `--subnet-ids` | Int (comma separated) | | Comma separated subnet ids whose routes will be managed by route-controller (requires vpc-ids flag) | -| `--load-balancer-type` | String | `nodebalancer` | Configures which type of load-balancing to use (options: nodebalancer, cilium-bgp) | -| `--bgp-node-selector` | String | `""` | Node selector to use to perform shared IP fail-over with BGP | -| `--ip-holder-suffix` | String | `""` | Suffix to append to the IP holder name when using shared IP fail-over with BGP | +| `--vpc-ids` | Int (comma separated) | | Comma separated VPC ids whose routes will be managed by route-controller | +| `--subnet-ids` | Int (comma separated) | | Comma separated subnet ids whose routes will be managed by route-controller (requires vpc-ids flag) | +| `--load-balancer-type` | String | `nodebalancer` | Configures the load-balancing type for LoadBalancer Services. `cilium-bgp` is deprecated and treated as `nodebalancer`. | +| `--bgp-node-selector` | String | `""` | Deprecated no-op retained for Helm chart compatibility. | +| `--ip-holder-suffix` | String | `""` | Deprecated no-op retained for Helm chart compatibility. | | `--default-nodebalancer-type` | String | `common` | Default type of NodeBalancer to create (options: common, premium, premium_40gb). Note: NodeBalancer types should always be specified in lowercase. | -| `--nodebalancer-tags` | String (comma separated) | | Linode tags to apply to all NodeBalancers | +| `--nodebalancer-tags` | String (comma separated) | | Linode tags to apply to all NodeBalancers | | `--nodebalancer-backend-ipv4-subnet` | String | `""` | ipv4 subnet to use for NodeBalancer backends | | `--nodebalancer-backend-ipv4-subnet-id` | Int | `""` | ipv4 subnet id to use for NodeBalancer backends | | `--nodebalancer-backend-ipv4-subnet-name` | String | `""` | ipv4 subnet name to use for NodeBalancer backends | @@ -62,7 +60,9 @@ The CCM supports the following flags: ## Configuration Methods ### Helm Chart + Configure via `values.yaml`: + ```yaml env: - name: LINODE_INSTANCE_CACHE_TTL @@ -73,7 +73,9 @@ args: ``` ### Manual Deployment + Add to the CCM DaemonSet: + ```yaml spec: template: @@ -91,22 +93,26 @@ spec: ## Usage Guidelines ### Cache Settings + - Adjust cache TTL based on cluster size and update frequency - Monitor memory usage when modifying cache settings - Consider API rate limits when decreasing TTL (see [Linode API Rate Limits](@https://techdocs.akamai.com/linode-api/reference/rate-limits)) ### API Settings + - Increase timeout for slower network conditions - Use default API URL unless testing/development required - Consider regional latency when adjusting timeouts ### Network Settings + - Configure external subnet for custom networking needs -- Use BGP settings only when implementing IP sharing - Document any custom network configurations ### Nodebalancer backend settings when running within VPC + To use dedicated subnet within VPC for nodebalancer backend ips, one can use one of the following flags: + - `--nodebalancer-backend-ipv4-subnet-id` specifying subnet id - `--nodebalancer-backend-ipv4-subnet-name` specifying subnet name @@ -117,6 +123,7 @@ If no specific subnet is specified, by default, CCM will use the `default` subne `--nodebalancer-backend-ipv4-subnet` can be used to make sure if nodebalancer backend ips are manually specified in service annotation, they lie within the specified subnet range. If CCM is started with multiple flags for nodebalancer backend subnet, following order of precedence is used for backend ip addresses: + 1. NodeBalancerBackendIPv4Range annotation on service 2. NodeBalancerBackendVPCName and NodeBalancerBackendSubnetName annotation on service 3. NodeBalancerBackendIPv4SubnetID/NodeBalancerBackendIPv4SubnetName flag set when starting CCM @@ -138,5 +145,6 @@ If CCM is started with multiple flags for nodebalancer backend subnet, following - Check for stale data For more details, see: + - [Installation Guide](../getting-started/installation.md) - [Troubleshooting Guide](../getting-started/troubleshooting.md) diff --git a/docs/configuration/loadbalancer.md b/docs/configuration/loadbalancer.md index 52901e3c..7a166f02 100644 --- a/docs/configuration/loadbalancer.md +++ b/docs/configuration/loadbalancer.md @@ -2,10 +2,7 @@ ## Overview -The CCM supports two types of LoadBalancer implementations: - -1. Linode NodeBalancers (default) -2. BGP-based IP sharing +The CCM supports Linode NodeBalancers for LoadBalancer Services. For implementation examples, see [Basic Service Examples](../examples/basic.md#loadbalancer-services). @@ -220,42 +217,6 @@ metadata: service.beta.kubernetes.io/linode-loadbalancer-default-proxy-protocol: "v2" ``` -## BGP-based IP Sharing Implementation - -BGP-based IP sharing provides a more cost-effective solution for multiple LoadBalancer services. For detailed setup instructions, see [Cilium BGP Documentation](https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane/). - -### Prerequisites - -- [Cilium CNI](https://docs.cilium.io/en/stable/network/bgp-control-plane/bgp-control-plane/) with BGP control plane enabled -- Additional IP provisioning enabled on your account (contact [Linode Support](https://www.linode.com/support/)) -- Nodes labeled for BGP peering - -### Configuration - -1. Enable BGP in CCM deployment: - -```yaml -args: - - --load-balancer-type=cilium-bgp - - --bgp-node-selector=cilium-bgp-peering=true - - --ip-holder-suffix=mycluster -``` - -1. Label nodes that should participate in BGP peering: - -```bash -kubectl label node my-node cilium-bgp-peering=true -``` - -1. Create LoadBalancer services as normal - the CCM will automatically use BGP-based IP sharing instead of creating NodeBalancers. - -### Environment Variables - -- `BGP_CUSTOM_ID_MAP`: Use your own map instead of default region map for BGP -- `BGP_PEER_PREFIX`: Use your own BGP peer prefix instead of default one - -For more details, see [Environment Variables](environment.md#network-configuration). - ## Configuring NodeBalancers directly with VPC NodeBalancers can be configured to have VPC specific ips configured as backend nodes. It requires: @@ -410,6 +371,5 @@ metadata: - [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/) - [Basic Service Examples](../examples/basic.md) - [Advanced Configuration Examples](../examples/advanced.md) diff --git a/docs/examples/README.md b/docs/examples/README.md index 235891da..1372afa8 100644 --- a/docs/examples/README.md +++ b/docs/examples/README.md @@ -13,7 +13,6 @@ This section provides working examples of common CCM configurations. Each exampl - Custom Health Checks - Firewalled Services - Session Affinity - - Shared IP Load-Balancing - Custom Node Selection 3. **Frontend VPC NodeBalancer** @@ -24,6 +23,7 @@ Note: To test UDP based NBs, one can use [test-server](https://github.com/rahula For testing these examples, see the [test script](https://github.com/linode/linode-cloud-controller-manager/blob/master/examples/test.sh). For more configuration options, see: + - [Service Annotations](../configuration/annotations.md) - [LoadBalancer Configuration](../configuration/loadbalancer.md) - [Firewall Configuration](../configuration/firewall.md) diff --git a/docs/examples/advanced.md b/docs/examples/advanced.md index 42fd8e64..101acbc2 100644 --- a/docs/examples/advanced.md +++ b/docs/examples/advanced.md @@ -73,42 +73,6 @@ spec: targetPort: 8080 ``` -## Shared IP Load-Balancing - -```yaml -apiVersion: v1 -kind: Service -metadata: - name: shared-ip-service -spec: - type: LoadBalancer - selector: - app: web - ports: - - port: 80 - targetPort: 8080 ---- -# Required DaemonSet configuration for shared IP -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: ccm-linode - namespace: kube-system -spec: - template: - spec: - containers: - - image: linode/linode-cloud-controller-manager:latest - name: ccm-linode - env: - - name: LINODE_URL - value: https://api.linode.com/v4 - args: - - --bgp-node-selector=cilium-bgp-peering=true - - --load-balancer-type=cilium-bgp - - --ip-holder-suffix=myclustername1 -``` - ## Custom Node Selection ```yaml @@ -135,6 +99,7 @@ metadata: ``` For more examples, see: + - [Service Annotations](../configuration/annotations.md) - [Firewall Configuration](../configuration/firewall.md) - [LoadBalancer Configuration](../configuration/loadbalancer.md) diff --git a/docs/getting-started/helm-installation.md b/docs/getting-started/helm-installation.md index bec9ce00..2ac94e72 100644 --- a/docs/getting-started/helm-installation.md +++ b/docs/getting-started/helm-installation.md @@ -1,6 +1,7 @@ # Helm Installation ## Prerequisites + - Helm 3.x installed - kubectl configured to access your cluster - Linode API token @@ -9,12 +10,14 @@ ## Installation Steps 1. Add the CCM Helm repository: + ```bash helm repo add ccm-linode https://linode.github.io/linode-cloud-controller-manager/ helm repo update ccm-linode ``` 2. Create a values file (values.yaml): + ```yaml apiToken: "your-api-token" region: "us-east" @@ -29,12 +32,6 @@ routeController: # Not required if specified in routeController vpcNames: "" # Comma separated VPC names -# Optional: Configure shared IP load balancing instead of NodeBalancers (requires Cilium CNI and BGP Control Plane enabled) -sharedIPLoadBalancing: - loadBalancerType: cilium-bgp - bgpNodeSelector: cilium-bgp-peering=true - ipHolderSuffix: "" - # Optional: Allow /metrics scraping without authorization on secure HTTPS port (10253 by default) allowUnauthorizedMetrics=true ``` @@ -57,6 +54,7 @@ secretRef: ``` 3. Install the CCM: + ```bash helm install ccm-linode \ --namespace kube-system \ @@ -67,6 +65,7 @@ helm install ccm-linode \ ## Upgrading To upgrade an existing installation: + ```bash helm upgrade ccm-linode \ --namespace kube-system \ @@ -77,6 +76,7 @@ helm upgrade ccm-linode \ ## Uninstalling To remove the CCM: + ```bash helm uninstall ccm-linode -n kube-system ``` diff --git a/docs/getting-started/overview.md b/docs/getting-started/overview.md index 39fe3f0e..eeb805ca 100644 --- a/docs/getting-started/overview.md +++ b/docs/getting-started/overview.md @@ -5,22 +5,25 @@ The Linode Cloud Controller Manager provides several key features that enable a ## Features ### LoadBalancer Services + - Automatic deployment and configuration of Linode NodeBalancers - Support for HTTP, HTTPS, and TCP traffic - SSL/TLS termination - Custom health checks and session affinity ### Node Management + - Automatic configuration of node hostnames and network addresses - Proper node state management for Linode shutdowns - Region-based node annotation for failure domain scheduling ### Network Integration + - Support for private networking - VPC and VLAN compatibility -- BGP-based IP sharing capabilities ### Security + - Integrated firewall management - Support for TLS termination - Custom security rules and ACLs @@ -28,7 +31,8 @@ The Linode Cloud Controller Manager provides several key features that enable a ## When to Use CCM The Linode CCM is essential when: + - Running Kubernetes clusters on Linode infrastructure - Requiring automated load balancer provisioning - Needing integrated cloud provider features -- Managing multi-node clusters with complex networking requirements \ No newline at end of file +- Managing multi-node clusters with complex networking requirements diff --git a/e2e/bgp-test/lb-cilium-bgp/chainsaw-test.yaml b/e2e/bgp-test/lb-cilium-bgp/chainsaw-test.yaml deleted file mode 100644 index 1800fe79..00000000 --- a/e2e/bgp-test/lb-cilium-bgp/chainsaw-test.yaml +++ /dev/null @@ -1,169 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json -apiVersion: chainsaw.kyverno.io/v1alpha1 -kind: Test -metadata: - name: cilium-bgp-test - labels: {} -spec: - namespace: "cilium-bgp-test" - catch: - - script: - shell: bash - shellArgs: - - -c - content: | - set -uo pipefail - echo "Test failed. Fetching CCM logs..." - kubectl logs -n kube-system daemonsets/ccm-linode | grep "cilium-bgp-test" | tail -100 || true - - # In BGP mode CCM creates a nanode (a small Linode instance) to hold the - # shared LoadBalancer IP once the Service is created. The final step - # deletes it, but if an earlier step fails that step never runs and the - # nanode would leak. Delete it here by its label suffix (best-effort, - # idempotent). chainsaw does not track instances created via the API. - echo "Cleaning up BGP IP-holder nanode (if any)..." - if [[ -n "${CLUSTER_NAME:-}" ]]; then - nanode_id=$(curl -s -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances" \ - | jq -r --arg cluster "$CLUSTER_NAME" '.data[] | select(.label | endswith($cluster)) | .id' 2>/dev/null || true) - if [[ "$nanode_id" =~ ^[0-9]+$ ]]; then - echo "Deleting nanode $nanode_id" - curl -s -o /dev/null -X DELETE -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances/$nanode_id" || true - fi - fi - steps: - - name: Check if CCM is deployed - try: - - assert: - file: ../../test/assert-ccm-resources.yaml - - name: Create a pod and service with load balancer type cilium-bgp - try: - - apply: - file: create-pod-service.yaml - catch: - - describe: - apiVersion: v1 - kind: Pod - - describe: - apiVersion: v1 - kind: Service - - name: Verify CiliumLoadBalancerIPPool creation - try: - - assert: - resource: - apiVersion: cilium.io/v2alpha1 - kind: CiliumLoadBalancerIPPool - metadata: - name: cilium-bgp-test-test-bgp-svc-pool - spec: - disabled: false - - name: Verify CiliumBGPPeeringPolicy - try: - - assert: - resource: - apiVersion: cilium.io/v2alpha1 - kind: CiliumBGPPeeringPolicy - metadata: - name: linode-ccm-bgp-peering - spec: - nodeSelector: - matchLabels: - cilium-bgp-peering: "true" - - name: Check LoadBalancer IP assignment - try: - - assert: - resource: - apiVersion: v1 - kind: Service - metadata: - name: test-bgp-svc - status: - conditions: - - status: "True" - type: cilium.io/IPAMRequestSatisfied - - name: Verify IP sharing on labeled nodes - try: - - script: - shell: bash - shellArgs: - - -c - content: | - set -euo pipefail - - delete_nanode() { - local NANODE_RESPONSE=$(curl -s -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances") - - local NANODE_ID=$(echo "$NANODE_RESPONSE" | \ - jq -r --arg cluster "$CLUSTER_NAME" '.data[] | select(.label | endswith($cluster)) | .id') - - if [ -n "$NANODE_ID" ]; then - curl -s -X DELETE -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances/$NANODE_ID" || true - fi - } - - # Get the LoadBalancer IP - LB_IP=$(kubectl get svc test-bgp-svc -n cilium-bgp-test -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - - # Get nodes with BGP label - BGP_NODES=$(kubectl get nodes -l cilium-bgp-peering=true -o name) - - if [ -z "$BGP_NODES" ]; then - echo "No nodes found with label cilium-bgp-peering=true" - delete_nanode - exit 1 - fi - - # Check if IP is shared on each BGP node - for node in $BGP_NODES; do - NODE_ID=$(kubectl get $node -o jsonpath='{.spec.providerID}' | sed 's|linode://||') - echo "Node ID: $NODE_ID" - - NODE_IP_RESPONSE=$(curl -s -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances/$NODE_ID/ips") - - SHARED_IPS=$(echo "$NODE_IP_RESPONSE" | jq -r '.ipv4.shared[]?.address // empty') - echo "shared IPs: $SHARED_IPS" - - if [ -n "$SHARED_IPS" ] && ! echo "$SHARED_IPS" | grep -q "$LB_IP"; then - echo "LoadBalancer IP $LB_IP not found in shared IPs of node $node" - delete_nanode - exit 1 - fi - done - - # Check if the nanode has the shared IP - NANODE_RESPONSE=$(curl -s -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances") - - NANODE_ID=$(echo "$NANODE_RESPONSE" | \ - jq -r --arg cluster "$CLUSTER_NAME" '.data[] | select(.label | endswith($cluster)) | .id') - - if [ -z "$NANODE_ID" ]; then - echo "No nanode found for cluster $CLUSTER_NAME" - exit 0 - fi - - NANODE_IP_RESPONSE=$(curl -s -H "Authorization: Bearer $LINODE_TOKEN" \ - "$LINODE_URL/v4/linode/instances/$NANODE_ID/ips") - - NANODE_IPS=$(echo "$NANODE_IP_RESPONSE" | jq -r '.ipv4.public[]?.address // empty') - - if [ -n "$NANODE_IPS" ] && ! echo "$NANODE_IPS" | grep -q "$LB_IP"; then - echo "LoadBalancer IP not found in nanode IPs" - delete_nanode - exit 1 - fi - - echo "Successfully found LoadBalancer IP in nanode IPs" - - # Delete the nanode on success - delete_nanode - check: - ($error == null): true - (contains($stdout, 'LoadBalancer IP not found in shared IPs of node')): false - (contains($stdout, 'LoadBalancer IP not found in nanode IPs')): false - (contains($stdout, 'Successfully found LoadBalancer IP in nanode IPs')): true - diff --git a/e2e/bgp-test/lb-cilium-bgp/create-pod-service.yaml b/e2e/bgp-test/lb-cilium-bgp/create-pod-service.yaml deleted file mode 100644 index b2f96238..00000000 --- a/e2e/bgp-test/lb-cilium-bgp/create-pod-service.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: test-pod-1 - labels: - app: test-bgp -spec: - containers: - - name: nginx - image: nginx:latest - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: test-bgp-svc -spec: - type: LoadBalancer - ports: - - port: 80 - targetPort: 80 - selector: - app: test-bgp diff --git a/e2e/setup/cilium-setup.sh b/e2e/setup/cilium-setup.sh deleted file mode 100755 index 9e8a7afb..00000000 --- a/e2e/setup/cilium-setup.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Add bgp peering label to non control plane nodes. Needed to update the shared IP on the nodes -kubectl get nodes --no-headers | grep -v control-plane |\ - awk '{print $1}' | xargs -I {} kubectl label nodes {} cilium-bgp-peering=true --overwrite - -# Add RBAC permissions -kubectl patch clusterrole ccm-linode-clusterrole --type='json' -p='[{ - "op": "add", - "path": "/rules/-", - "value": { - "apiGroups": ["cilium.io"], - "resources": ["ciliumloadbalancerippools", "ciliumbgppeeringpolicies"], - "verbs": ["get", "list", "watch", "create", "update", "patch", "delete"] - } -}]' - -# Patch DaemonSet -kubectl patch daemonset ccm-linode -n kube-system --type='json' -p='[{ - "op": "add", - "path": "/spec/template/spec/containers/0/args/-", - "value": "--bgp-node-selector=cilium-bgp-peering=true" -}, { - "op": "add", - "path": "/spec/template/spec/containers/0/args/-", - "value": "--load-balancer-type=cilium-bgp" -}, { - "op": "add", - "path": "/spec/template/spec/containers/0/args/-", - "value": "--ip-holder-suffix='"${CLUSTER_SUFFIX}"'" -}]' diff --git a/go.mod b/go.mod index 9db3f422..b085113a 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,8 @@ go 1.26.4 require ( github.com/appscode/go v0.0.0-20201105063637-5613f3b8169f - github.com/cilium/cilium v1.18.10 github.com/getsentry/sentry-go v0.43.0 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.6.0 github.com/hexdigest/gowrap v1.4.3 github.com/linode/linodego/v2 v2.0.0 github.com/prometheus/client_golang v1.23.2 @@ -33,16 +31,10 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cilium/ebpf v0.20.1-0.20260218191617-ee67e7f43dd9 // indirect - github.com/cilium/hive v0.0.0-20250611195437-5a5dacdfb354 // indirect - github.com/cilium/proxy v0.0.0-20250623105955-2136f59a4ea1 // indirect - github.com/cilium/statedb v0.4.6 // indirect - github.com/cilium/stream v0.0.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -53,25 +45,18 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/loads v0.22.0 // indirect - github.com/go-openapi/runtime v0.28.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect - github.com/go-openapi/validate v0.24.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/cel-go v0.29.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect @@ -80,45 +65,28 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mackerelio/go-osstat v0.2.6 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mitchellh/copystructure v1.1.2 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/gomega v1.38.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/sagikazarmark/locafero v0.7.0 // indirect - github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/shopspring/decimal v1.2.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.9.2 // indirect github.com/spf13/cobra v1.10.0 // indirect - github.com/spf13/viper v1.20.1 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/vishvananda/netlink v1.3.2-0.20250926155043-cd3cb2e12c97 // indirect - github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/etcd/api/v3 v3.6.5 // indirect go.etcd.io/etcd/client/pkg/v3 v3.6.5 // indirect go.etcd.io/etcd/client/v3 v3.6.5 // indirect - go.mongodb.org/mongo-driver v1.17.7 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect @@ -129,12 +97,10 @@ require ( go.opentelemetry.io/otel/sdk v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect - go.uber.org/dig v1.17.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/crypto v0.53.0 // indirect golang.org/x/mod v0.37.0 // indirect golang.org/x/net v0.56.0 // indirect diff --git a/go.sum b/go.sum index cce6e8e4..e854866d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -17,8 +15,6 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/appscode/go v0.0.0-20201105063637-5613f3b8169f h1:heDuWjdnY2rJIgLwIQjWPgOc0BUWWX6OGOeB+0t8v/s= github.com/appscode/go v0.0.0-20201105063637-5613f3b8169f/go.mod h1:piHRpQ9+NTTuV3V98INxjU7o2KlAJMznaxvB6wHKkfU= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -29,20 +25,6 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cilium/cilium v1.18.10 h1:GjDK7JOASSSv0DfaKnN5BQsAAqANq4khLqP5dBpqK7U= -github.com/cilium/cilium v1.18.10/go.mod h1:+RFX25kBztUqqJGO19PT9Wf5VveOhssCSv1Kq0hWxiM= -github.com/cilium/ebpf v0.20.1-0.20260218191617-ee67e7f43dd9 h1:hQW7n5ePt/HDgeZLcyT3pFENyfa6vmaGU7M+tq2pa64= -github.com/cilium/ebpf v0.20.1-0.20260218191617-ee67e7f43dd9/go.mod h1:EGj6HpG/oejvbTAsMWwlA4UbMU7WBAgILd+9OSvcDTc= -github.com/cilium/hive v0.0.0-20250611195437-5a5dacdfb354 h1:4G9PC8nQw4pGW5wCMA0g8J8rK1l4C3owlceAOQggXzs= -github.com/cilium/hive v0.0.0-20250611195437-5a5dacdfb354/go.mod h1:pI2GJ1n3SLKIQVFrKF7W6A6gb6BQkZ+3Hp4PAEo5SuI= -github.com/cilium/proxy v0.0.0-20250623105955-2136f59a4ea1 h1:SOOtIfQmW/pF1iW1I4hVUx1pvgX7Xh2E8jHv+itBXQ0= -github.com/cilium/proxy v0.0.0-20250623105955-2136f59a4ea1/go.mod h1:Kwyyx+cC2H67Aj1sDuqBLvPn6TEmEJRPvULIrJ/kBRo= -github.com/cilium/statedb v0.4.6 h1:pundFmW0Dhinsv0ZINdFsxzlb6d3ZQkQM7aJW9eMtD8= -github.com/cilium/statedb v0.4.6/go.mod h1:DlxX9OQi/nM8oumUuz8VjxXUtVRiEfbfo8Ri1YWNCGI= -github.com/cilium/stream v0.0.1 h1:82zuM/WwkLiac2Jg5FrzPxZHvIBbxXTi4VY7M+EYLs0= -github.com/cilium/stream v0.0.1/go.mod h1:/e83AwqvNKpyg4n3C41qmnmj1x2G9DwzI+jb7GkF4lI= -github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= -github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/codeskyblue/go-sh v0.0.0-20200712050446-30169cf553fe/go.mod h1:VQx0hjo2oUeQkQUET7wRwradO6f+fN5jzXgB/zROxxE= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -62,11 +44,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= -github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= -github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= -github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= -github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -88,32 +65,14 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= -github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= -github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= -github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= -github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= -github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= -github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= -github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= -github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -122,8 +81,6 @@ github.com/gojuno/minimock/v3 v3.0.10/go.mod h1:CFXcUJYnBe+1QuNzm+WmdPYtvi/+7zQc github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -190,12 +147,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= -github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/linode/linodego/v2 v2.0.0-20260601073839-52f201e73a60 h1:hbIDlx3J40WXF1vB/36USHFEQcIoA/bZ6d3Q1qfMcTk= github.com/linode/linodego/v2 v2.0.0-20260601073839-52f201e73a60/go.mod h1:rsITrtsKFKBi+pEnznDbbF9ZGBou9S8UccCm1yHLbpI= -github.com/mackerelio/go-osstat v0.2.6 h1:gs4U8BZeS1tjrL08tt5VUliVvSWP26Ai2Ob8Lr7f2i0= -github.com/mackerelio/go-osstat v0.2.6/go.mod h1:lRy8V9ZuHpuRVZh+vyTkODeDPl3/d5MgXHtLSaqG8bA= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -203,10 +156,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.1.2 h1:Th2TIvG1+6ma3e/0/bopBKohOTY7s4dA8V2q4EUcBJ0= github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -222,28 +171,17 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWu github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= -github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= -github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -260,10 +198,6 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= -github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= -github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -272,10 +206,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= -github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= @@ -286,8 +216,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.8/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -296,20 +224,13 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= -github.com/vishvananda/netlink v1.3.2-0.20250926155043-cd3cb2e12c97 h1:fLr9AqDqfl9FLLOlPPPs9QujTXDvehe9fH1hR7JY0jY= -github.com/vishvananda/netlink v1.3.2-0.20250926155043-cd3cb2e12c97/go.mod h1:lEui7SPMd9fgxzHVGRAvTxsBGCF6PRH81o2kLWLWHgw= -github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= -github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= @@ -331,8 +252,6 @@ go.etcd.io/etcd/server/v3 v3.6.5 h1:4RbUb1Bd4y1WkBHmuF+cZII83JNQMuNXzyjwigQ06y0= go.etcd.io/etcd/server/v3 v3.6.5/go.mod h1:PLuhyVXz8WWRhzXDsl3A3zv/+aK9e4A9lpQkqawIaH0= go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= -go.mongodb.org/mongo-driver v1.17.7 h1:a9w+U3Vt67eYzcfq3k/OAv284/uUUkL0uP75VE5rCOU= -go.mongodb.org/mongo-driver v1.17.7/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= @@ -355,8 +274,6 @@ go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/ go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= -go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -367,8 +284,6 @@ go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= -go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/main.go b/main.go index 950c8ffa..a9f2cab0 100644 --- a/main.go +++ b/main.go @@ -88,9 +88,9 @@ func main() { command.Flags().StringSliceVar(&ccmOptions.Options.SubnetNames, "subnet-names", []string{"default"}, "comma separated subnet names whose routes will be managed by route-controller (requires vpc-names flag to also be set)") command.Flags().IntSliceVar(&ccmOptions.Options.VPCIDs, "vpc-ids", nil, "comma separated vpc ids whose routes will be managed by route-controller") command.Flags().IntSliceVar(&ccmOptions.Options.SubnetIDs, "subnet-ids", nil, "comma separated subnet ids whose routes will be managed by route-controller (requires vpc-ids flag to also be set)") - command.Flags().StringVar(&ccmOptions.Options.LoadBalancerType, "load-balancer-type", "nodebalancer", "configures which type of load-balancing to use for LoadBalancer Services (options: nodebalancer, cilium-bgp)") - command.Flags().StringVar(&ccmOptions.Options.BGPNodeSelector, "bgp-node-selector", "", "node selector to use to perform shared IP fail-over with BGP (e.g. cilium-bgp-peering=true") - command.Flags().StringVar(&ccmOptions.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(&ccmOptions.Options.LoadBalancerType, "load-balancer-type", "nodebalancer", "configures the load-balancing type for LoadBalancer Services; cilium-bgp is deprecated and treated as nodebalancer") + command.Flags().StringVar(&ccmOptions.Options.BGPNodeSelector, "bgp-node-selector", "", "DEPRECATED: no effect; retained for backwards compatibility") + command.Flags().StringVar(&ccmOptions.Options.IpHolderSuffix, "ip-holder-suffix", "", "DEPRECATED: no effect; retained for backwards compatibility") command.Flags().StringVar(&ccmOptions.Options.DefaultNBType, "default-nodebalancer-type", string(linodego.NBTypeCommon), "default type of NodeBalancer to create (options: common, premium, premium_40GB)") command.Flags().StringVar(&ccmOptions.Options.NodeBalancerBackendIPv4Subnet, "nodebalancer-backend-ipv4-subnet", "", "ipv4 subnet to use for NodeBalancer backends") command.Flags().StringSliceVar(&ccmOptions.Options.NodeBalancerTags, "nodebalancer-tags", []string{}, "Linode tags to apply to all NodeBalancers") diff --git a/mise.toml b/mise.toml index fed8aed3..cbda06d8 100644 --- a/mise.toml +++ b/mise.toml @@ -77,10 +77,6 @@ run = "make e2e-test" description = "Run the IPv6 backend e2e slice" run = "make e2e-test-ipv6-backends" -[tasks.e2e-test-bgp] -description = "Run the Cilium BGP e2e test" -run = "make e2e-test-bgp" - [tasks.e2e-test-subnet] description = "Run the subnet filtering e2e test" run = "make e2e-test-subnet"