diff --git a/.golangci.yml b/.golangci.yml index d27484f8..af7c086e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -38,4 +38,4 @@ linters: issues: exclude-rules: - path: cloud/linode/fake_linode_test.go - text: 'SA1019: (.+).(NodeBalancersPagedResponse|NodeBalancerConfigsPagedResponse|NodeBalancerNodesPagedResponse|FirewallDevicesPagedResponse) is deprecated: (NodeBalancersPagedResponse|NodeBalancerConfigsPagedResponse|NodeBalancerNodesPagedResponse|FirewallDevicesPagedResponse) exists for historical compatibility and should not be used.' + text: 'SA1019: (.+).(NodeBalancersPagedResponse|NodeBalancerConfigsPagedResponse|NodeBalancerNodesPagedResponse|FirewallDevicesPagedResponse|NodeBalancerFirewallsPagedResponse) is deprecated: (NodeBalancersPagedResponse|NodeBalancerConfigsPagedResponse|NodeBalancerNodesPagedResponse|FirewallDevicesPagedResponse|NodeBalancerFirewallsPagedResponse) exists for historical compatibility and should not be used.' diff --git a/README.md b/README.md index 69380bd8..1504a117 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ For general feature and usage notes, refer to the [Getting Started with Linode N #### Using IP Sharing instead of NodeBalancers Alternatively, the Linode CCM can integrate with [Cilium's BGP Control Plane](https://docs.cilium.io/en/stable/network/bgp-control-plane/) to perform load-balancing via IP sharing on labeled Nodes. This option does not create a backing NodeBalancer and instead -provisions a new IP on an ip-holder Nanode to share for the desired region. See [Shared IP LoadBalancing](#shared-ip-load-balancing). +provisions a new reserved IP to share for the desired region. See [Shared IP LoadBalancing](#shared-ip-load-balancing). #### Annotations The Linode CCM accepts several annotations which affect the properties of the underlying NodeBalancer deployment. diff --git a/cloud/linode/cilium_loadbalancers.go b/cloud/linode/cilium_loadbalancers.go index 4e74adca..1010e597 100644 --- a/cloud/linode/cilium_loadbalancers.go +++ b/cloud/linode/cilium_loadbalancers.go @@ -2,16 +2,13 @@ package linode import ( "context" - "encoding/json" "fmt" "net/http" - "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/linode-cloud-controller-manager/cloud/annotations" "github.com/linode/linodego" v1 "k8s.io/api/core/v1" @@ -26,10 +23,8 @@ import ( const ( ciliumLBClass = "io.cilium/bgp-control-plane" - ipHolderLabelPrefix = "linode-ccm-ip-holder" ciliumBGPPeeringPolicyName = "linode-ccm-bgp-peering" - - commonControlPlaneLabel = "node-role.kubernetes.io/control-plane" + commonControlPlaneLabel = "node-role.kubernetes.io/control-plane" ) // This mapping is unfortunately necessary since there is no way to get the @@ -88,21 +83,6 @@ func (l *loadbalancers) getExistingSharedIPsInCluster(ctx context.Context) ([]st 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.Shared)) - for _, addr := range ipHolderAddrs.IPv4.Shared { - 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 := parseProviderID(node.Spec.ProviderID) @@ -176,25 +156,8 @@ func (l *loadbalancers) handleIPSharing(ctx context.Context, node *v1.Node) erro 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) - 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 { + + if err = l.shareIPs(ctx, inClusterAddrs, node); err != nil { klog.Infof("error sharing IPs: %s", err.Error()) return err } @@ -205,12 +168,9 @@ func (l *loadbalancers) handleIPSharing(ctx context.Context, node *v1.Node) erro // 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) (string, error) { - ipHolder, err := l.ensureIPHolder(ctx) - if err != nil { - return "", err - } - - newSharedIP, err := l.client.AddInstanceIPAddress(ctx, ipHolder.ID, true) + newSharedIP, err := l.client.ReserveIPAddress(ctx, linodego.ReserveIPOptions{ + Region: l.zone, + }) if err != nil { return "", err } @@ -221,20 +181,8 @@ func (l *loadbalancers) createSharedIP(ctx context.Context, nodes []*v1.Node) (s 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) - } - } + + addrs := append(inClusterAddrs, newSharedIP.Address) // share the IPs with nodes participating in Cilium BGP peering if Options.BGPNodeSelector == "" { @@ -273,14 +221,9 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) return err } bgpNodes := nodeList.Items - ipHolder, err := l.getIPHolder(ctx) - if err != nil { - // return error or nil if not found since no IP holder means there - // is no IP to reclaim - return IgnoreLinodeAPIError(err, http.StatusNotFound) - } + svcIngress := service.Status.LoadBalancer.Ingress - if len(svcIngress) > 0 && ipHolder != nil { + if len(svcIngress) > 0 { for _, ingress := range svcIngress { // delete the shared IP on the Linodes it's shared on for _, node := range bgpNodes { @@ -294,8 +237,8 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) } } - // finally delete the shared IP on the ip-holder - err = l.client.DeleteInstanceIPAddress(ctx, ipHolder.ID, ingress.IP) + // finally delete the shared IP + err = l.client.DeleteReservedIPAddress(ctx, ingress.IP) if IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { return err } @@ -305,50 +248,6 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) 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) (*linodego.Instance, error) { - ipHolder, err := l.getIPHolder(ctx) - if err != nil { - return nil, err - } - if ipHolder != nil { - return ipHolder, nil - } - - ipHolder, err = l.client.CreateInstance(ctx, linodego.InstanceCreateOptions{ - Region: l.zone, - Type: "g6-nanode-1", - Label: fmt.Sprintf("%s-%s", ipHolderLabelPrefix, l.zone), - RootPass: uuid.NewString(), - Image: "linode/ubuntu22.04", - Booted: ptr.To(false), - }) - if err != nil { - return nil, err - } - - return ipHolder, nil -} - -func (l *loadbalancers) getIPHolder(ctx context.Context) (*linodego.Instance, error) { - 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 - 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 -} - func (l *loadbalancers) retrieveCiliumClientset() error { if l.ciliumClient != nil { return nil diff --git a/cloud/linode/cilium_loadbalancers_test.go b/cloud/linode/cilium_loadbalancers_test.go index 9d44b469..5910a1c4 100644 --- a/cloud/linode/cilium_loadbalancers_test.go +++ b/cloud/linode/cilium_loadbalancers_test.go @@ -2,9 +2,7 @@ package linode import ( "context" - "encoding/json" "fmt" - "net" "testing" k8sClient "github.com/cilium/cilium/pkg/k8s/client" @@ -58,14 +56,6 @@ var ( }, }, } - publicIPv4 = net.ParseIP("45.76.101.25") - ipHolderInstance = linodego.Instance{ - ID: 12345, - Label: fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone), - Type: "g6-standard-1", - Region: "us-west", - IPv4: []*net.IP{&publicIPv4}, - } ) func TestCiliumCCMLoadBalancers(t *testing.T) { @@ -82,12 +72,8 @@ func TestCiliumCCMLoadBalancers(t *testing.T) { f: testUnsupportedRegion, }, { - name: "Create Cilium Load Balancer With explicit loadBalancerClass and existing IP holder nanode", - f: testCreateWithExistingIPHolder, - }, - { - name: "Create Cilium Load Balancer With no existing IP holder nanode", - f: testCreateWithNoExistingIPHolder, + name: "Create Cilium Load Balancer With explicit loadBalancerClass", + f: testCreate, }, { name: "Delete Cilium Load Balancer", @@ -158,17 +144,8 @@ func testNoBGPNodeLabel(t *testing.T, mc *mocks.MockClient) { addNodes(t, kubeClient, nodes) lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, _ := json.Marshal(filter) - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) dummySharedIP := "45.76.101.26" - mc.EXPECT().CreateInstance(gomock.Any(), gomock.Any()).Times(1).Return(&ipHolderInstance, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), ipHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Shared: []*linodego.InstanceIP{{Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), ipHolderInstance.ID, true).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) + mc.EXPECT().ReserveIPAddress(gomock.Any(), gomock.Any()).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ IPs: []string{dummySharedIP}, LinodeID: 11111, @@ -209,45 +186,7 @@ func testUnsupportedRegion(t *testing.T, mc *mocks.MockClient) { } } -func testCreateWithExistingIPHolder(t *testing.T, mc *mocks.MockClient) { - Options.BGPNodeSelector = "cilium-bgp-peering=true" - svc := createTestService() - - kubeClient, _ := k8sClient.NewFakeClientset() - ciliumClient := &fakev2alpha1.FakeCiliumV2alpha1{Fake: &kubeClient.CiliumFakeClientset.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, _ := json.Marshal(filter) - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{ipHolderInstance}, nil) - dummySharedIP := "45.76.101.26" - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), ipHolderInstance.ID, true).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), ipHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Shared: []*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(context.TODO(), "linodelb", svc, nodes) - if err != nil { - t.Fatalf("expected a nil error, got %v", err) - } - if lbStatus == nil { - t.Fatal("expected non-nil lbStatus") - } -} - -func testCreateWithNoExistingIPHolder(t *testing.T, mc *mocks.MockClient) { +func testCreate(t *testing.T, mc *mocks.MockClient) { Options.BGPNodeSelector = "cilium-bgp-peering=true" svc := createTestService() @@ -257,17 +196,8 @@ func testCreateWithNoExistingIPHolder(t *testing.T, mc *mocks.MockClient) { addNodes(t, kubeClient, nodes) lb := &loadbalancers{mc, zone, kubeClient, ciliumClient, ciliumLBType} - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, _ := json.Marshal(filter) - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) dummySharedIP := "45.76.101.26" - mc.EXPECT().CreateInstance(gomock.Any(), gomock.Any()).Times(1).Return(&ipHolderInstance, nil) - mc.EXPECT().GetInstanceIPAddresses(gomock.Any(), ipHolderInstance.ID).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Shared: []*linodego.InstanceIP{{Address: dummySharedIP}}, - }, - }, nil) - mc.EXPECT().AddInstanceIPAddress(gomock.Any(), ipHolderInstance.ID, true).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) + mc.EXPECT().ReserveIPAddress(gomock.Any(), gomock.Any()).Times(1).Return(&linodego.InstanceIP{Address: dummySharedIP}, nil) mc.EXPECT().ShareIPAddresses(gomock.Any(), linodego.IPAddressesShareOptions{ IPs: []string{dummySharedIP}, LinodeID: 11111, @@ -299,12 +229,9 @@ func testEnsureCiliumLoadBalancerDeleted(t *testing.T, mc *mocks.MockClient) { dummySharedIP := "45.76.101.26" svc.Status.LoadBalancer = v1.LoadBalancerStatus{Ingress: []v1.LoadBalancerIngress{{IP: dummySharedIP}}} - filter := map[string]string{"label": fmt.Sprintf("%s-%s", ipHolderLabelPrefix, zone)} - rawFilter, _ := json.Marshal(filter) - mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{ipHolderInstance}, 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(), ipHolderInstance.ID, dummySharedIP).Times(1).Return(nil) + mc.EXPECT().DeleteReservedIPAddress(gomock.Any(), dummySharedIP).Times(1).Return(nil) err := lb.EnsureLoadBalancerDeleted(context.TODO(), "linodelb", svc) if err != nil { diff --git a/cloud/linode/client/client.go b/cloud/linode/client/client.go index 639bdcfe..ff39381d 100644 --- a/cloud/linode/client/client.go +++ b/cloud/linode/client/client.go @@ -24,10 +24,13 @@ type Client interface { CreateInstance(ctx context.Context, opts linodego.InstanceCreateOptions) (*linodego.Instance, error) GetInstanceIPAddresses(context.Context, int) (*linodego.InstanceIPAddressResponse, error) - AddInstanceIPAddress(ctx context.Context, linodeID int, public bool) (*linodego.InstanceIP, error) DeleteInstanceIPAddress(ctx context.Context, linodeID int, ipAddress string) error ShareIPAddresses(ctx context.Context, opts linodego.IPAddressesShareOptions) error + GetReservedIPAddress(context.Context, string) (*linodego.InstanceIP, error) + ReserveIPAddress(context.Context, linodego.ReserveIPOptions) (*linodego.InstanceIP, error) + DeleteReservedIPAddress(context.Context, string) error + UpdateInstanceConfigInterface(context.Context, int, int, int, linodego.InstanceConfigInterfaceUpdateOptions) (*linodego.InstanceConfigInterface, error) ListVPCs(context.Context, *linodego.ListOptions) ([]linodego.VPC, error) diff --git a/cloud/linode/client/mocks/mock_client.go b/cloud/linode/client/mocks/mock_client.go index f8baf6b2..ad255d0b 100644 --- a/cloud/linode/client/mocks/mock_client.go +++ b/cloud/linode/client/mocks/mock_client.go @@ -35,21 +35,6 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } -// AddInstanceIPAddress mocks base method. -func (m *MockClient) AddInstanceIPAddress(arg0 context.Context, arg1 int, arg2 bool) (*linodego.InstanceIP, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddInstanceIPAddress", arg0, arg1, arg2) - ret0, _ := ret[0].(*linodego.InstanceIP) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AddInstanceIPAddress indicates an expected call of AddInstanceIPAddress. -func (mr *MockClientMockRecorder) AddInstanceIPAddress(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddInstanceIPAddress", reflect.TypeOf((*MockClient)(nil).AddInstanceIPAddress), arg0, arg1, arg2) -} - // CreateFirewall mocks base method. func (m *MockClient) CreateFirewall(arg0 context.Context, arg1 linodego.FirewallCreateOptions) (*linodego.Firewall, error) { m.ctrl.T.Helper() @@ -195,6 +180,20 @@ func (mr *MockClientMockRecorder) DeleteNodeBalancerConfig(arg0, arg1, arg2 inte return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNodeBalancerConfig", reflect.TypeOf((*MockClient)(nil).DeleteNodeBalancerConfig), arg0, arg1, arg2) } +// DeleteReservedIPAddress mocks base method. +func (m *MockClient) DeleteReservedIPAddress(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteReservedIPAddress", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteReservedIPAddress indicates an expected call of DeleteReservedIPAddress. +func (mr *MockClientMockRecorder) DeleteReservedIPAddress(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReservedIPAddress", reflect.TypeOf((*MockClient)(nil).DeleteReservedIPAddress), arg0, arg1) +} + // GetFirewall mocks base method. func (m *MockClient) GetFirewall(arg0 context.Context, arg1 int) (*linodego.Firewall, error) { m.ctrl.T.Helper() @@ -255,6 +254,21 @@ func (mr *MockClientMockRecorder) GetNodeBalancer(arg0, arg1 interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNodeBalancer", reflect.TypeOf((*MockClient)(nil).GetNodeBalancer), arg0, arg1) } +// GetReservedIPAddress mocks base method. +func (m *MockClient) GetReservedIPAddress(arg0 context.Context, arg1 string) (*linodego.InstanceIP, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReservedIPAddress", arg0, arg1) + ret0, _ := ret[0].(*linodego.InstanceIP) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReservedIPAddress indicates an expected call of GetReservedIPAddress. +func (mr *MockClientMockRecorder) GetReservedIPAddress(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReservedIPAddress", reflect.TypeOf((*MockClient)(nil).GetReservedIPAddress), arg0, arg1) +} + // ListFirewallDevices mocks base method. func (m *MockClient) ListFirewallDevices(arg0 context.Context, arg1 int, arg2 *linodego.ListOptions) ([]linodego.FirewallDevice, error) { m.ctrl.T.Helper() @@ -390,6 +404,21 @@ func (mr *MockClientMockRecorder) RebuildNodeBalancerConfig(arg0, arg1, arg2, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RebuildNodeBalancerConfig", reflect.TypeOf((*MockClient)(nil).RebuildNodeBalancerConfig), arg0, arg1, arg2, arg3) } +// ReserveIPAddress mocks base method. +func (m *MockClient) ReserveIPAddress(arg0 context.Context, arg1 linodego.ReserveIPOptions) (*linodego.InstanceIP, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReserveIPAddress", arg0, arg1) + ret0, _ := ret[0].(*linodego.InstanceIP) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReserveIPAddress indicates an expected call of ReserveIPAddress. +func (mr *MockClientMockRecorder) ReserveIPAddress(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReserveIPAddress", reflect.TypeOf((*MockClient)(nil).ReserveIPAddress), arg0, arg1) +} + // ShareIPAddresses mocks base method. func (m *MockClient) ShareIPAddresses(arg0 context.Context, arg1 linodego.IPAddressesShareOptions) error { m.ctrl.T.Helper() diff --git a/go.mod b/go.mod index 4c721f77..3c98a465 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,10 @@ require ( github.com/cilium/cilium v1.15.5 github.com/getsentry/sentry-go v0.4.0 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.6.0 - github.com/linode/linodego v1.36.2 + github.com/linode/linodego v1.41.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a - golang.org/x/oauth2 v0.21.0 k8s.io/api v0.29.3 k8s.io/apimachinery v0.29.3 k8s.io/client-go v0.29.3 @@ -64,6 +62,7 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect @@ -129,13 +128,14 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.27.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect diff --git a/go.sum b/go.sum index a365ff47..3440da3a 100644 --- a/go.sum +++ b/go.sum @@ -183,8 +183,8 @@ github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= @@ -247,8 +247,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/linode/linodego v1.36.2 h1:F5ZIXjLq0Zyyy0Kk9UgD/OffUTBx4TQuCkjMSYbqeVA= -github.com/linode/linodego v1.36.2/go.mod h1:KyV4OO/9/tAxaLSjyjFyOQBcS9bYUdei1hwk3nl0UjI= +github.com/linode/linodego v1.40.0 h1:7ESY0PwK94hoggoCtIroT1Xk6b1flrFBNZ6KwqbTqlI= +github.com/linode/linodego v1.40.0/go.mod h1:NsUw4l8QrLdIofRg1NYFBbW5ZERnmbZykVBszPZLORM= +github.com/linode/linodego v1.40.1-0.20240923155930-ac81d65fb5d2 h1:tk9IsDzEHlwHJ5U/+9RJ7UBUIHjeZNg8sFvGfyhN9yk= +github.com/linode/linodego v1.40.1-0.20240923155930-ac81d65fb5d2/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY= +github.com/linode/linodego v1.41.0 h1:GcP7JIBr9iLRJ9FwAtb9/WCT1DuPJS/xUApapfdjtiY= +github.com/linode/linodego v1.41.0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -489,8 +493,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -520,10 +526,14 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -531,8 +541,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -559,16 +569,20 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -578,8 +592,10 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=