From e4a2ad56c7c4faf20726ffb1f37222283eddce3a Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Wed, 24 Apr 2024 12:30:56 -0400 Subject: [PATCH] feat: Add route_controller to linode CCM (#199) * add back changes reverted in PR #195 * get instanceConfig only when running within VPC * add and fix unittests * use lock when reading/writing vpc id * updated route-controller using /v4/vpcs/ips api * fix tests * switch to new api returning ips for specific vpc * when running with vpc set, only cache instances which are part of VPC * address review comments * update linodego to v1.33.0 * address review comment, make variable required if routecontroller is enabled --------- Co-authored-by: Rahul Sharma --- README.md | 11 + cloud/linode/client/client.go | 5 + cloud/linode/client/mocks/mock_client.go | 45 +++ cloud/linode/cloud.go | 60 +++- cloud/linode/cloud_test.go | 30 ++ cloud/linode/instances.go | 131 ++++++-- cloud/linode/instances_test.go | 4 +- cloud/linode/node_controller.go | 23 +- cloud/linode/route_controller.go | 252 +++++++++++++++ cloud/linode/route_controller_test.go | 385 +++++++++++++++++++++++ cloud/linode/vpc.go | 31 ++ deploy/chart/templates/daemonset.yaml | 9 + deploy/chart/values.yaml | 6 + e2e/go.mod | 23 +- e2e/go.sum | 79 +++-- go.mod | 19 +- go.sum | 45 ++- main.go | 2 + 18 files changed, 1054 insertions(+), 106 deletions(-) create mode 100644 cloud/linode/cloud_test.go create mode 100644 cloud/linode/route_controller.go create mode 100644 cloud/linode/route_controller_test.go create mode 100644 cloud/linode/vpc.go diff --git a/README.md b/README.md index 42bda9f1..1b357b0a 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,17 @@ Users can create CloudFirewall instances, supply their own rules and attach them **Note**
If the user supplies a firewall-id, and later switches to using an ACL, the CCM will take over the CloudFirewall Instance. To avoid this, delete the service, and re-create it so the original CloudFirewall is left undisturbed. +#### Routes +When running k8s clusters within VPC, node specific podCIDRs need to be allowed on the VPC interface. Linode CCM comes with route-controller functionality which can be enabled for automatically adding/deleting routes on VPC interfaces. When installing CCM with helm, make sure to specify routeController settings. + +##### Example usage in values.yaml +```yaml +routeController: + vpcName: + clusterCIDR: 10.0.0.0/8 + configureCloudRoutes: true +``` + ### Nodes Kubernetes Nodes can be configured with the following annotations. diff --git a/cloud/linode/client/client.go b/cloud/linode/client/client.go index c6839529..dd4c09de 100644 --- a/cloud/linode/client/client.go +++ b/cloud/linode/client/client.go @@ -16,6 +16,11 @@ type Client interface { ListInstances(context.Context, *linodego.ListOptions) ([]linodego.Instance, error) GetInstanceIPAddresses(context.Context, int) (*linodego.InstanceIPAddressResponse, error) + UpdateInstanceConfigInterface(context.Context, int, int, int, linodego.InstanceConfigInterfaceUpdateOptions) (*linodego.InstanceConfigInterface, error) + + ListVPCs(context.Context, *linodego.ListOptions) ([]linodego.VPC, error) + ListVPCIPAddresses(context.Context, int, *linodego.ListOptions) ([]linodego.VPCIP, error) + CreateNodeBalancer(context.Context, linodego.NodeBalancerCreateOptions) (*linodego.NodeBalancer, error) GetNodeBalancer(context.Context, int) (*linodego.NodeBalancer, error) UpdateNodeBalancer(context.Context, int, linodego.NodeBalancerUpdateOptions) (*linodego.NodeBalancer, error) diff --git a/cloud/linode/client/mocks/mock_client.go b/cloud/linode/client/mocks/mock_client.go index 04333c1a..9307bc5a 100644 --- a/cloud/linode/client/mocks/mock_client.go +++ b/cloud/linode/client/mocks/mock_client.go @@ -301,6 +301,36 @@ func (mr *MockClientMockRecorder) ListNodeBalancers(arg0, arg1 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListNodeBalancers", reflect.TypeOf((*MockClient)(nil).ListNodeBalancers), arg0, arg1) } +// ListVPCIPAddresses mocks base method. +func (m *MockClient) ListVPCIPAddresses(arg0 context.Context, arg1 int, arg2 *linodego.ListOptions) ([]linodego.VPCIP, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListVPCIPAddresses", arg0, arg1, arg2) + ret0, _ := ret[0].([]linodego.VPCIP) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListVPCIPAddresses indicates an expected call of ListVPCIPAddresses. +func (mr *MockClientMockRecorder) ListVPCIPAddresses(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListVPCIPAddresses", reflect.TypeOf((*MockClient)(nil).ListVPCIPAddresses), arg0, arg1, arg2) +} + +// ListVPCs mocks base method. +func (m *MockClient) ListVPCs(arg0 context.Context, arg1 *linodego.ListOptions) ([]linodego.VPC, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListVPCs", arg0, arg1) + ret0, _ := ret[0].([]linodego.VPC) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListVPCs indicates an expected call of ListVPCs. +func (mr *MockClientMockRecorder) ListVPCs(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListVPCs", reflect.TypeOf((*MockClient)(nil).ListVPCs), arg0, arg1) +} + // RebuildNodeBalancerConfig mocks base method. func (m *MockClient) RebuildNodeBalancerConfig(arg0 context.Context, arg1, arg2 int, arg3 linodego.NodeBalancerConfigRebuildOptions) (*linodego.NodeBalancerConfig, error) { m.ctrl.T.Helper() @@ -331,6 +361,21 @@ func (mr *MockClientMockRecorder) UpdateFirewallRules(arg0, arg1, arg2 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFirewallRules", reflect.TypeOf((*MockClient)(nil).UpdateFirewallRules), arg0, arg1, arg2) } +// UpdateInstanceConfigInterface mocks base method. +func (m *MockClient) UpdateInstanceConfigInterface(arg0 context.Context, arg1, arg2, arg3 int, arg4 linodego.InstanceConfigInterfaceUpdateOptions) (*linodego.InstanceConfigInterface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateInstanceConfigInterface", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(*linodego.InstanceConfigInterface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateInstanceConfigInterface indicates an expected call of UpdateInstanceConfigInterface. +func (mr *MockClientMockRecorder) UpdateInstanceConfigInterface(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateInstanceConfigInterface", reflect.TypeOf((*MockClient)(nil).UpdateInstanceConfigInterface), arg0, arg1, arg2, arg3, arg4) +} + // UpdateNodeBalancer mocks base method. func (m *MockClient) UpdateNodeBalancer(arg0 context.Context, arg1 int, arg2 linodego.NodeBalancerUpdateOptions) (*linodego.NodeBalancer, error) { m.ctrl.T.Helper() diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index 123c2039..a1f230d9 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "sync" "github.com/linode/linodego" "github.com/spf13/pflag" @@ -25,14 +26,46 @@ const ( // We expect it to be initialized with flags external to this package, likely in // main.go var Options struct { - KubeconfigFlag *pflag.Flag - LinodeGoDebug bool + KubeconfigFlag *pflag.Flag + LinodeGoDebug bool + EnableRouteController bool + VPCName string } +// vpcDetails is set when VPCName options flag is set. +// We use it to list instances running within the VPC if set +type vpcDetails struct { + mu sync.RWMutex + id int + name string +} + +func (v *vpcDetails) setDetails(client client.Client, name string) error { + v.mu.Lock() + defer v.mu.Unlock() + + id, err := getVPCID(client, Options.VPCName) + if err != nil { + return fmt.Errorf("failed finding VPC ID: %w", err) + } + v.id = id + v.name = name + return nil +} + +func (v *vpcDetails) getID() int { + v.mu.Lock() + defer v.mu.Unlock() + return v.id +} + +var vpcInfo vpcDetails = vpcDetails{id: 0, name: ""} + type linodeCloud struct { client client.Client instances cloudprovider.InstancesV2 loadbalancers cloudprovider.LoadBalancer + routes cloudprovider.Routes } func init() { @@ -67,12 +100,26 @@ func newCloud() (cloudprovider.Interface, error) { linodeClient.SetDebug(true) } - // Return struct that satisfies cloudprovider.Interface - return &linodeCloud{ + if Options.VPCName != "" { + err := vpcInfo.setDetails(linodeClient, Options.VPCName) + if err != nil { + return nil, fmt.Errorf("failed finding VPC ID: %w", err) + } + } + + routes, err := newRoutes(linodeClient) + if err != nil { + return nil, fmt.Errorf("routes client was not created successfully: %w", err) + } + + // create struct that satisfies cloudprovider.Interface + lcloud := &linodeCloud{ client: linodeClient, instances: newInstances(linodeClient), loadbalancers: newLoadbalancers(linodeClient, region), - }, nil + routes: routes, + } + return lcloud, nil } func (c *linodeCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stopCh <-chan struct{}) { @@ -109,6 +156,9 @@ func (c *linodeCloud) Clusters() (cloudprovider.Clusters, bool) { } func (c *linodeCloud) Routes() (cloudprovider.Routes, bool) { + if Options.EnableRouteController { + return c.routes, true + } return nil, false } diff --git a/cloud/linode/cloud_test.go b/cloud/linode/cloud_test.go new file mode 100644 index 00000000..7f46fadf --- /dev/null +++ b/cloud/linode/cloud_test.go @@ -0,0 +1,30 @@ +package linode + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func TestNewCloudRouteControllerDisabled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + t.Setenv("LINODE_API_TOKEN", "dummyapitoken") + t.Setenv("LINODE_REGION", "us-east") + + t.Run("should not fail if vpc is empty and routecontroller is disabled", func(t *testing.T) { + Options.VPCName = "" + Options.EnableRouteController = false + _, err := newCloud() + assert.NoError(t, err) + }) + + t.Run("fail if vpcname is empty and routecontroller is enabled", func(t *testing.T) { + Options.VPCName = "" + Options.EnableRouteController = true + _, err := newCloud() + assert.Error(t, err) + }) +} diff --git a/cloud/linode/instances.go b/cloud/linode/instances.go index 3f8818c6..7c89f7d2 100644 --- a/cloud/linode/instances.go +++ b/cloud/linode/instances.go @@ -13,18 +13,50 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" cloudprovider "k8s.io/cloud-provider" + "k8s.io/klog/v2" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" "github.com/linode/linode-cloud-controller-manager/sentry" ) +type nodeIP struct { + ip string + ipType v1.NodeAddressType +} + +type linodeInstance struct { + instance *linodego.Instance + ips []nodeIP +} + type nodeCache struct { sync.RWMutex - nodes map[int]*linodego.Instance + nodes map[int]linodeInstance lastUpdate time.Time ttl time.Duration } +// getInstanceIPv4Addresses returns all ipv4 addresses configured on a linode. +func (nc *nodeCache) getInstanceIPv4Addresses(instance linodego.Instance, vpcips []string) []nodeIP { + ips := []nodeIP{} + + // If vpc ips are present, list them first + for _, ip := range vpcips { + ipType := v1.NodeInternalIP + ips = append(ips, nodeIP{ip: ip, ipType: ipType}) + } + + for _, ip := range instance.IPv4 { + ipType := v1.NodeExternalIP + if ip.IsPrivate() { + ipType = v1.NodeInternalIP + } + ips = append(ips, nodeIP{ip: ip.String(), ipType: ipType}) + } + + return ips +} + // refreshInstances conditionally loads all instances from the Linode API and caches them. // It does not refresh if the last update happened less than `nodeCache.ttl` ago. func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) error { @@ -40,15 +72,38 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) return err } - nc.nodes = make(map[int]*linodego.Instance) + // If running within VPC, find instances and store their ips + vpcNodes := map[int][]string{} + vpcID := vpcInfo.getID() + if vpcID != 0 { + resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, "")) + if err != nil { + return err + } + for _, r := range resp { + if r.Address == nil { + continue + } + vpcNodes[r.LinodeID] = append(vpcNodes[r.LinodeID], *r.Address) + } + } + + newNodes := make(map[int]linodeInstance, len(instances)) + for i, instance := range instances { - for _, instance := range instances { - instance := instance - nc.nodes[instance.ID] = &instance + // if running within VPC, only store instances in cache which are part of VPC + if vpcID != 0 && len(vpcNodes[instance.ID]) == 0 { + continue + } + node := linodeInstance{ + instance: &instances[i], + ips: nc.getInstanceIPv4Addresses(instance, vpcNodes[instance.ID]), + } + newNodes[instance.ID] = node } + nc.nodes = newNodes nc.lastUpdate = time.Now() - return nil } @@ -65,9 +120,10 @@ func newInstances(client client.Client) *instances { timeout = t } } + klog.V(3).Infof("TTL for nodeCache set to %d", timeout) return &instances{client, &nodeCache{ - nodes: make(map[int]*linodego.Instance), + nodes: make(map[int]linodeInstance, 0), ttl: time.Duration(timeout) * time.Second, }} } @@ -93,9 +149,9 @@ func (i *instances) linodeByIP(kNode *v1.Node) (*linodego.Instance, error) { return nil, fmt.Errorf("no IP address found on node %s", kNode.Name) } for _, node := range i.nodeCache.nodes { - for _, nodeIP := range node.IPv4 { + for _, nodeIP := range node.instance.IPv4 { if !nodeIP.IsPrivate() && slices.Contains(kNodeAddresses, nodeIP.String()) { - return node, nil + return node.instance, nil } } } @@ -107,8 +163,8 @@ func (i *instances) linodeByName(nodeName types.NodeName) *linodego.Instance { i.nodeCache.RLock() defer i.nodeCache.RUnlock() for _, node := range i.nodeCache.nodes { - if node.Label == string(nodeName) { - return node + if node.instance.Label == string(nodeName) { + return node.instance } } @@ -118,11 +174,24 @@ func (i *instances) linodeByName(nodeName types.NodeName) *linodego.Instance { func (i *instances) linodeByID(id int) (*linodego.Instance, error) { i.nodeCache.RLock() defer i.nodeCache.RUnlock() - instance, ok := i.nodeCache.nodes[id] + linodeInstance, ok := i.nodeCache.nodes[id] if !ok { return nil, cloudprovider.InstanceNotFound } - return instance, nil + return linodeInstance.instance, nil +} + +// listAllInstances returns all instances in nodeCache +func (i *instances) listAllInstances(ctx context.Context) ([]linodego.Instance, error) { + if err := i.nodeCache.refreshInstances(ctx, i.client); err != nil { + return nil, err + } + + instances := []linodego.Instance{} + for _, linodeInstance := range i.nodeCache.nodes { + instances = append(instances, *linodeInstance.instance) + } + return instances, nil } func (i *instances) lookupLinode(ctx context.Context, node *v1.Node) (*linodego.Instance, error) { @@ -193,7 +262,13 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud return nil, err } - if len(linode.IPv4) == 0 { + ips, err := i.getLinodeIPv4Addresses(ctx, node) + if err != nil { + sentry.CaptureError(ctx, err) + return nil, err + } + + if len(ips) == 0 { err := instanceNoIPAddressesError{linode.ID} sentry.CaptureError(ctx, err) return nil, err @@ -201,12 +276,8 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud addresses := []v1.NodeAddress{{Type: v1.NodeHostName, Address: linode.Label}} - for _, ip := range linode.IPv4 { - ipType := v1.NodeExternalIP - if ip.IsPrivate() { - ipType = v1.NodeInternalIP - } - addresses = append(addresses, v1.NodeAddress{Type: ipType, Address: ip.String()}) + for _, ip := range ips { + addresses = append(addresses, v1.NodeAddress{Type: ip.ipType, Address: ip.ip}) } // note that Zone is omitted as it's not a thing in Linode @@ -219,3 +290,23 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud return meta, nil } + +func (i *instances) getLinodeIPv4Addresses(ctx context.Context, node *v1.Node) ([]nodeIP, error) { + ctx = sentry.SetHubOnContext(ctx) + instance, err := i.lookupLinode(ctx, node) + if err != nil { + sentry.CaptureError(ctx, err) + return nil, err + } + + i.nodeCache.RLock() + defer i.nodeCache.RUnlock() + linodeInstance, ok := i.nodeCache.nodes[instance.ID] + if !ok || len(linodeInstance.ips) == 0 { + err := instanceNoIPAddressesError{instance.ID} + sentry.CaptureError(ctx, err) + return nil, err + } + + return linodeInstance.ips, nil +} diff --git a/cloud/linode/instances_test.go b/cloud/linode/instances_test.go index 28096bcf..89e1cfe6 100644 --- a/cloud/linode/instances_test.go +++ b/cloud/linode/instances_test.go @@ -126,7 +126,7 @@ func TestMetadataRetrieval(t *testing.T) { assert.Equal(t, providerIDPrefix+strconv.Itoa(id), meta.ProviderID) assert.Equal(t, region, meta.Region) assert.Equal(t, linodeType, meta.InstanceType) - assert.Equal(t, meta.NodeAddresses, []v1.NodeAddress{ + assert.Equal(t, []v1.NodeAddress{ { Type: v1.NodeHostName, Address: name, @@ -139,7 +139,7 @@ func TestMetadataRetrieval(t *testing.T) { Type: v1.NodeInternalIP, Address: privateIPv4.String(), }, - }) + }, meta.NodeAddresses) }) ipTests := []struct { diff --git a/cloud/linode/node_controller.go b/cloud/linode/node_controller.go index b069a968..85847fd1 100644 --- a/cloud/linode/node_controller.go +++ b/cloud/linode/node_controller.go @@ -129,8 +129,9 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { lastUpdate := s.LastMetadataUpdate(node.Name) - uuid, ok := node.Labels[annotations.AnnLinodeHostUUID] - if ok && time.Since(lastUpdate) < s.ttl { + uuid, foundLabel := node.Labels[annotations.AnnLinodeHostUUID] + configuredPrivateIP, foundAnnotation := node.Annotations[annotations.AnnLinodeNodePrivateIP] + if foundLabel && foundAnnotation && time.Since(lastUpdate) < s.ttl { return nil } @@ -140,7 +141,19 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { return err } - if uuid == linode.HostUUID && node.Spec.ProviderID != "" { + expectedPrivateIP := "" + // linode API response for linode will contain only one private ip + // if any private ip is configured. If it changes in future or linode + // supports other subnets with nodebalancer, this logic needs to be updated. + // https://www.linode.com/docs/api/linode-instances/#linode-view + for _, addr := range linode.IPv4 { + if addr.IsPrivate() { + expectedPrivateIP = addr.String() + break + } + } + + if uuid == linode.HostUUID && node.Spec.ProviderID != "" && configuredPrivateIP == expectedPrivateIP { s.SetLastMetadataUpdate(node.Name) return nil } @@ -162,6 +175,10 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { n.Spec.ProviderID = providerIDPrefix + strconv.Itoa(linode.ID) } + // Try to update the expectedPrivateIP if its not set or doesn't match + if n.Annotations[annotations.AnnLinodeNodePrivateIP] != expectedPrivateIP && expectedPrivateIP != "" { + n.Annotations[annotations.AnnLinodeNodePrivateIP] = expectedPrivateIP + } _, err = s.kubeclient.CoreV1().Nodes().Update(ctx, n, metav1.UpdateOptions{}) return err }); err != nil { diff --git a/cloud/linode/route_controller.go b/cloud/linode/route_controller.go new file mode 100644 index 00000000..909ea76f --- /dev/null +++ b/cloud/linode/route_controller.go @@ -0,0 +1,252 @@ +package linode + +import ( + "context" + "fmt" + "os" + "strconv" + "sync" + "time" + + "github.com/linode/linodego" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + cloudprovider "k8s.io/cloud-provider" + "k8s.io/klog/v2" + + "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" +) + +type routeCache struct { + sync.RWMutex + routes map[int][]linodego.VPCIP + lastUpdate time.Time + ttl time.Duration +} + +func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) error { + rc.Lock() + defer rc.Unlock() + + if time.Since(rc.lastUpdate) < rc.ttl { + return nil + } + + vpcNodes := map[int][]linodego.VPCIP{} + vpcID := vpcInfo.getID() + resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, "")) + if err != nil { + return err + } + for _, r := range resp { + vpcNodes[r.LinodeID] = append(vpcNodes[r.LinodeID], r) + } + + rc.routes = vpcNodes + rc.lastUpdate = time.Now() + return nil +} + +type routes struct { + vpcid int + client client.Client + instances *instances + routeCache *routeCache +} + +func newRoutes(client client.Client) (cloudprovider.Routes, error) { + timeout := 60 + if raw, ok := os.LookupEnv("LINODE_ROUTES_CACHE_TTL_SECONDS"); ok { + if t, _ := strconv.Atoi(raw); t > 0 { + timeout = t + } + } + klog.V(3).Infof("TTL for routeCache set to %d seconds", timeout) + + vpcid := vpcInfo.getID() + if Options.EnableRouteController && vpcid == 0 { + return nil, fmt.Errorf("cannot enable route controller as vpc [%s] not found", Options.VPCName) + } + + return &routes{ + vpcid: vpcid, + client: client, + instances: newInstances(client), + routeCache: &routeCache{ + routes: make(map[int][]linodego.VPCIP, 0), + ttl: time.Duration(timeout) * time.Second, + }, + }, nil +} + +// instanceRoutesByID returns routes for given instance id +func (r *routes) instanceRoutesByID(id int) ([]linodego.VPCIP, error) { + r.routeCache.RLock() + defer r.routeCache.RUnlock() + instanceRoutes, ok := r.routeCache.routes[id] + if !ok { + return nil, fmt.Errorf("no routes found for instance %d", id) + } + return instanceRoutes, nil +} + +// getInstanceRoutes returns routes for given instance id +// It refreshes routeCache if it has expired +func (r *routes) getInstanceRoutes(ctx context.Context, id int) ([]linodego.VPCIP, error) { + if err := r.routeCache.refreshRoutes(ctx, r.client); err != nil { + return nil, err + } + + return r.instanceRoutesByID(id) +} + +// getInstanceFromName returns linode instance with given name if it exists +func (r *routes) getInstanceFromName(ctx context.Context, name string) (*linodego.Instance, error) { + // create node object + node := &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + + // fetch instance with specified node name + instance, err := r.instances.lookupLinode(ctx, node) + if err != nil { + klog.Errorf("failed getting linode %s", name) + return nil, err + } + return instance, nil +} + +// CreateRoute adds route's subnet to ip_ranges of target node's VPC interface +func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint string, route *cloudprovider.Route) error { + instance, err := r.getInstanceFromName(ctx, string(route.TargetNode)) + if err != nil { + return err + } + + // fetch instance routes + instanceRoutes, err := r.getInstanceRoutes(ctx, instance.ID) + if err != nil { + return err + } + + // check already configured routes + intfRoutes := []string{} + intfVPCIP := linodego.VPCIP{} + for _, ir := range instanceRoutes { + if ir.VPCID != r.vpcid { + continue + } + + if ir.Address != nil { + intfVPCIP = ir + continue + } + + if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR { + klog.V(4).Infof("Route already exists for node %s", route.TargetNode) + return nil + } + + intfRoutes = append(intfRoutes, *ir.AddressRange) + } + + if intfVPCIP.Address == nil { + return fmt.Errorf("unable to add route %s for node %s. no valid interface found", route.DestinationCIDR, route.TargetNode) + } + + intfRoutes = append(intfRoutes, route.DestinationCIDR) + interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{ + IPRanges: &intfRoutes, + } + + resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, intfVPCIP.ConfigID, intfVPCIP.InterfaceID, interfaceUpdateOptions) + if err != nil { + return err + } + klog.V(4).Infof("Added routes for node %s. Current routes: %v", route.TargetNode, resp.IPRanges) + return nil +} + +// DeleteRoute removes route's subnet from ip_ranges of target node's VPC interface +func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *cloudprovider.Route) error { + instance, err := r.getInstanceFromName(ctx, string(route.TargetNode)) + if err != nil { + return err + } + + instanceRoutes, err := r.getInstanceRoutes(ctx, instance.ID) + if err != nil { + return err + } + + // check already configured routes + intfRoutes := []string{} + intfVPCIP := linodego.VPCIP{} + for _, ir := range instanceRoutes { + if ir.VPCID != r.vpcid { + continue + } + + if ir.Address != nil { + intfVPCIP = ir + continue + } + + if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR { + continue + } + + intfRoutes = append(intfRoutes, *ir.AddressRange) + } + + if intfVPCIP.Address == nil { + return fmt.Errorf("unable to remove route %s for node %s. no valid interface found", route.DestinationCIDR, route.TargetNode) + } + + interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{ + IPRanges: &intfRoutes, + } + resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, intfVPCIP.ConfigID, intfVPCIP.InterfaceID, interfaceUpdateOptions) + if err != nil { + return err + } + klog.V(4).Infof("Deleted route for node %s. Current routes: %v", route.TargetNode, resp.IPRanges) + return nil +} + +// ListRoutes fetches routes configured on all instances which have VPC interfaces +func (r *routes) ListRoutes(ctx context.Context, clusterName string) ([]*cloudprovider.Route, error) { + klog.V(4).Infof("Fetching routes configured on the cluster") + instances, err := r.instances.listAllInstances(ctx) + if err != nil { + return nil, err + } + + var configuredRoutes []*cloudprovider.Route + for _, instance := range instances { + instanceRoutes, err := r.getInstanceRoutes(ctx, instance.ID) + if err != nil { + klog.Errorf("Failed finding routes for instance id %d. Error: %v", instance.ID, err) + continue + } + + // check for configured routes + for _, ir := range instanceRoutes { + if ir.Address != nil || ir.VPCID != r.vpcid { + continue + } + + if ir.AddressRange != nil { + route := &cloudprovider.Route{ + TargetNode: types.NodeName(instance.Label), + DestinationCIDR: *ir.AddressRange, + } + configuredRoutes = append(configuredRoutes, route) + } + } + } + return configuredRoutes, nil +} diff --git a/cloud/linode/route_controller_test.go b/cloud/linode/route_controller_test.go new file mode 100644 index 00000000..2235b18c --- /dev/null +++ b/cloud/linode/route_controller_test.go @@ -0,0 +1,385 @@ +package linode + +import ( + "context" + "net" + "testing" + + "github.com/golang/mock/gomock" + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/types" + cloudprovider "k8s.io/cloud-provider" + + "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" +) + +func TestListRoutes(t *testing.T) { + Options.VPCName = "test" + Options.EnableRouteController = true + + vpcInfo.id = 1 + vpcid := vpcInfo.getID() + + nodeID := 123 + name := "mock-instance" + publicIPv4 := net.ParseIP("45.76.101.25") + privateIPv4 := net.ParseIP("192.168.133.65") + linodeType := "g6-standard-1" + region := "us-east" + + t.Run("should return empty if no instance exists in cluster", func(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.Instance{}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil) + routes, err := routeController.ListRoutes(ctx, "abc") + assert.NoError(t, err) + assert.Empty(t, routes) + }) + + validInstance := linodego.Instance{ + ID: nodeID, + Label: name, + Type: linodeType, + Region: region, + IPv4: []*net.IP{&publicIPv4, &privateIPv4}, + } + + t.Run("should return no routes if instance exists but is not connected to VPC", func(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil) + routes, err := routeController.ListRoutes(ctx, "abc") + assert.NoError(t, err) + assert.Empty(t, routes) + }) + + vpcIP := "10.0.0.2" + noRoutesInVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + t.Run("should return no routes if instance exists, connected to VPC but no ip_ranges configured", func(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(noRoutesInVPC, nil) + routes, err := routeController.ListRoutes(ctx, "abc") + assert.NoError(t, err) + assert.Empty(t, routes) + }) + + addressRange1 := "10.192.0.0/24" + addressRange2 := "10.192.10.0/24" + routesInVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + { + Address: nil, + AddressRange: &addressRange1, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + { + Address: nil, + AddressRange: &addressRange2, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + t.Run("should return routes if instance exists, connected to VPC and ip_ranges configured", func(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(routesInVPC, nil) + routes, err := routeController.ListRoutes(ctx, "abc") + assert.NoError(t, err) + assert.NotEmpty(t, routes) + assert.Equal(t, addressRange1, routes[0].DestinationCIDR) + assert.Equal(t, addressRange2, routes[1].DestinationCIDR) + }) + + routesInDifferentVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: 100, + NAT1To1: nil, + LinodeID: nodeID, + }, + { + Address: nil, + AddressRange: &addressRange1, + VPCID: 100, + NAT1To1: nil, + LinodeID: nodeID, + }, + { + Address: nil, + AddressRange: &addressRange2, + VPCID: 100, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + t.Run("should return no routes if instance exists, connected to VPC and ip_ranges configured but vpc id doesn't match", func(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(routesInDifferentVPC, nil) + routes, err := routeController.ListRoutes(ctx, "abc") + assert.NoError(t, err) + assert.Empty(t, routes) + }) +} + +func TestCreateRoute(t *testing.T) { + ctx := context.Background() + Options.VPCName = "test" + Options.EnableRouteController = true + + vpcInfo.id = 1 + vpcid := vpcInfo.getID() + + nodeID := 123 + name := "mock-instance" + publicIPv4 := net.ParseIP("45.76.101.25") + privateIPv4 := net.ParseIP("192.168.133.65") + linodeType := "g6-standard-1" + region := "us-east" + validInstance := linodego.Instance{ + ID: nodeID, + Label: name, + Type: linodeType, + Region: region, + IPv4: []*net.IP{&publicIPv4, &privateIPv4}, + } + + vpcIP := "10.0.0.2" + noRoutesInVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + instanceConfigIntfWithVPCAndRoute := linodego.InstanceConfigInterface{ + VPCID: &vpcid, + IPv4: &linodego.VPCIPv4{VPC: vpcIP}, + IPRanges: []string{"10.10.10.0/24"}, + } + route := &cloudprovider.Route{ + Name: "route1", + TargetNode: types.NodeName(name), + DestinationCIDR: "10.10.10.0/24", + } + + t.Run("should return no error if instance exists, connected to VPC we add a route", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(noRoutesInVPC, nil) + client.EXPECT().UpdateInstanceConfigInterface(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&instanceConfigIntfWithVPCAndRoute, nil) + err = routeController.CreateRoute(ctx, "dummy", "dummy", route) + assert.NoError(t, err) + }) + + addressRange1 := "10.10.10.0/24" + routesInVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + { + Address: nil, + AddressRange: &addressRange1, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + t.Run("should return no error if instance exists, connected to VPC and route already exists", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(routesInVPC, nil) + err = routeController.CreateRoute(ctx, "dummy", "dummy", route) + assert.NoError(t, err) + }) + + t.Run("should return error if instance doesn't exist", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil) + err = routeController.CreateRoute(ctx, "dummy", "dummy", route) + assert.Error(t, err) + }) +} + +func TestDeleteRoute(t *testing.T) { + Options.VPCName = "test" + Options.EnableRouteController = true + + ctx := context.Background() + + vpcInfo.id = 1 + vpcid := vpcInfo.getID() + + nodeID := 123 + name := "mock-instance" + publicIPv4 := net.ParseIP("45.76.101.25") + privateIPv4 := net.ParseIP("192.168.133.65") + linodeType := "g6-standard-1" + region := "us-east" + validInstance := linodego.Instance{ + ID: nodeID, + Label: name, + Type: linodeType, + Region: region, + IPv4: []*net.IP{&publicIPv4, &privateIPv4}, + } + + vpcIP := "10.0.0.2" + route := &cloudprovider.Route{ + Name: "route1", + TargetNode: types.NodeName(name), + DestinationCIDR: "10.10.10.0/24", + } + + t.Run("should return error if instance doesn't exist", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil) + err = routeController.DeleteRoute(ctx, "dummy", route) + assert.Error(t, err) + }) + + addressRange1 := "10.10.10.0/24" + noRoutesInVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + instanceConfigIntfWithVPCAndNoRoute := linodego.InstanceConfigInterface{ + VPCID: &vpcid, + IPv4: &linodego.VPCIPv4{VPC: vpcIP}, + IPRanges: []string{}, + } + + t.Run("should return no error if instance exists, connected to VPC, route doesn't exist and we try to delete route", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(noRoutesInVPC, nil) + client.EXPECT().UpdateInstanceConfigInterface(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&instanceConfigIntfWithVPCAndNoRoute, nil) + err = routeController.DeleteRoute(ctx, "dummy", route) + assert.NoError(t, err) + }) + + routesInVPC := []linodego.VPCIP{ + { + Address: &vpcIP, + AddressRange: nil, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + { + Address: nil, + AddressRange: &addressRange1, + VPCID: vpcid, + NAT1To1: nil, + LinodeID: nodeID, + }, + } + + t.Run("should return no error if instance exists, connected to VPC and route is deleted", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mocks.NewMockClient(ctrl) + routeController, err := newRoutes(client) + assert.NoError(t, err) + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(routesInVPC, nil) + client.EXPECT().UpdateInstanceConfigInterface(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&instanceConfigIntfWithVPCAndNoRoute, nil) + err = routeController.DeleteRoute(ctx, "dummy", route) + assert.NoError(t, err) + }) +} diff --git a/cloud/linode/vpc.go b/cloud/linode/vpc.go new file mode 100644 index 00000000..fd40e731 --- /dev/null +++ b/cloud/linode/vpc.go @@ -0,0 +1,31 @@ +package linode + +import ( + "context" + "fmt" + + "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" + "github.com/linode/linodego" +) + +type vpcLookupError struct { + value string +} + +func (e vpcLookupError) Error() string { + return fmt.Sprintf("failed to find VPC: %q", e.value) +} + +// getVPCID returns the VPC id using the VPC label +func getVPCID(client client.Client, vpcName string) (int, error) { + vpcs, err := client.ListVPCs(context.TODO(), &linodego.ListOptions{}) + if err != nil { + return 0, err + } + for _, vpc := range vpcs { + if vpc.Label == vpcName { + return vpc.ID, nil + } + } + return 0, vpcLookupError{vpcName} +} diff --git a/deploy/chart/templates/daemonset.yaml b/deploy/chart/templates/daemonset.yaml index d40fb8eb..b9a1dc4f 100644 --- a/deploy/chart/templates/daemonset.yaml +++ b/deploy/chart/templates/daemonset.yaml @@ -36,6 +36,15 @@ spec: {{- if .Values.linodegoDebug }} - --linodego-debug={{ .Values.linodegoDebug }} {{- end }} + {{- if .Values.routeController }} + - --enable-route-controller=true + - --vpc-name={{ required "A valid .Values.routeController.vpcName is required" .Values.routeController.vpcName }} + - --configure-cloud-routes={{ default true .Values.routeController.configureCloudRoutes }} + - --cluster-cidr={{ required "A valid .Values.routeController.clusterCIDR is required" .Values.routeController.clusterCIDR }} + {{- if .Values.routeController.routeReconciliationPeriod }} + - --route-reconciliation-period={{ .Values.routeController.routeReconciliationPeriod }} + {{- end }} + {{- end }} volumeMounts: - mountPath: /etc/kubernetes name: k8s diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index d8bf72ee..2df40a14 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -44,6 +44,12 @@ tolerations: operator: Exists effect: NoSchedule +# This section adds ability to enable route-controller for ccm +# routeController: +# vpcName: +# clusterCIDR: 10.0.0.0/8 +# configureCloudRoutes: true + # This section adds the ability to pass environment variables to adjust CCM defaults # https://github.com/linode/linode-cloud-controller-manager/blob/master/cloud/linode/loadbalancers.go # LINODE_HOSTNAME_ONLY_INGRESS type bool is supported diff --git a/e2e/go.mod b/e2e/go.mod index 95596a65..c3365eca 100644 --- a/e2e/go.mod +++ b/e2e/go.mod @@ -4,9 +4,9 @@ go 1.22 require ( github.com/appscode/go v0.0.0-20200323182826-54e98e09185a - github.com/linode/linodego v1.30.0 - github.com/onsi/ginkgo/v2 v2.17.0 - github.com/onsi/gomega v1.30.0 + github.com/linode/linodego v1.33.0 + github.com/onsi/ginkgo/v2 v2.17.1 + github.com/onsi/gomega v1.33.0 k8s.io/api v0.23.17 k8s.io/apimachinery v0.23.17 k8s.io/client-go v1.5.2 @@ -15,7 +15,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.4.1 // indirect - github.com/go-resty/resty/v2 v2.11.0 // indirect + github.com/go-resty/resty/v2 v2.12.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -27,19 +27,18 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/onsi/ginkgo v1.16.5 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/oauth2 v0.19.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.17.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.6 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.30.0 // indirect diff --git a/e2e/go.sum b/e2e/go.sum index 2df5ab7c..01f742ce 100644 --- a/e2e/go.sum +++ b/e2e/go.sum @@ -37,8 +37,9 @@ github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= -github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= +github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -46,7 +47,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -80,6 +80,7 @@ github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97Dwqy github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -98,8 +99,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linode/linodego v1.30.0 h1:6HJli+LX7NGu+Sne2G+ux790EkVOWOV/SR4mK3jcs6k= -github.com/linode/linodego v1.30.0/go.mod h1:/46h/XpmWi//oSA92GX2p3FIxb8HbX7grslPPQalR2o= +github.com/linode/linodego v1.33.0 h1:cX2FYry7r6CA1ujBMsdqiM4VhvIQtnWsOuVblzfBhCw= +github.com/linode/linodego v1.33.0/go.mod h1:dSJJgIwqZCF5wnpuC6w5cyIbRtcexAm7uVvuJopGB40= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -115,16 +116,21 @@ github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOA github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.17.0 h1:kdnunFXpBjbzN56hcJHrXZ8M+LOkenKA7NnBzTNigTI= -github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -141,8 +147,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -152,7 +158,8 @@ golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -164,25 +171,27 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -192,12 +201,18 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ 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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -205,16 +220,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.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.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -222,11 +239,10 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -236,6 +252,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -249,8 +266,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gomodules.xyz/version v0.1.0/go.mod h1:Y8xuV02mL/45psyPKG3NCVOwvAOy6T5Kx0l3rCjKSjU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= @@ -270,13 +285,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= @@ -285,6 +301,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/go.mod b/go.mod index 88da4483..de8bb06f 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ require ( github.com/appscode/go v0.0.0-20200323182826-54e98e09185a github.com/getsentry/sentry-go v0.4.0 github.com/golang/mock v1.6.0 - github.com/linode/linodego v1.30.0 + github.com/linode/linodego v1.33.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a k8s.io/api v0.29.3 k8s.io/apimachinery v0.29.3 @@ -39,7 +39,7 @@ require ( github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-resty/resty/v2 v2.11.0 // indirect + github.com/go-resty/resty/v2 v2.12.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -83,17 +83,16 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.19.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.22.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/oauth2 v0.19.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.17.0 // indirect - google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect diff --git a/go.sum b/go.sum index a47ca5c9..d10dea1d 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= -github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= +github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -201,8 +201,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/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.30.0 h1:6HJli+LX7NGu+Sne2G+ux790EkVOWOV/SR4mK3jcs6k= -github.com/linode/linodego v1.30.0/go.mod h1:/46h/XpmWi//oSA92GX2p3FIxb8HbX7grslPPQalR2o= +github.com/linode/linodego v1.33.0 h1:cX2FYry7r6CA1ujBMsdqiM4VhvIQtnWsOuVblzfBhCw= +github.com/linode/linodego v1.33.0/go.mod h1:dSJJgIwqZCF5wnpuC6w5cyIbRtcexAm7uVvuJopGB40= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -294,8 +294,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -373,9 +373,10 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 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-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -392,7 +393,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -402,11 +402,12 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -434,27 +435,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.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.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -474,8 +475,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/version v0.1.0/go.mod h1:Y8xuV02mL/45psyPKG3NCVOwvAOy6T5Kx0l3rCjKSjU= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= diff --git a/main.go b/main.go index 21e1d914..ed8a981b 100644 --- a/main.go +++ b/main.go @@ -78,6 +78,8 @@ func main() { // Add Linode-specific flags command.Flags().BoolVar(&linode.Options.LinodeGoDebug, "linodego-debug", false, "enables debug output for the LinodeAPI wrapper") + command.Flags().BoolVar(&linode.Options.EnableRouteController, "enable-route-controller", false, "enables route_controller for ccm") + command.Flags().StringVar(&linode.Options.VPCName, "vpc-name", "", "vpc name whose routes will be managed by route-controller") // Set static flags command.Flags().VisitAll(func(fl *pflag.Flag) {