diff --git a/README.md b/README.md index 1c91de39..b3e24a34 100644 --- a/README.md +++ b/README.md @@ -126,17 +126,6 @@ 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 e9d4b15c..3b9bb74d 100644 --- a/cloud/linode/client/client.go +++ b/cloud/linode/client/client.go @@ -16,11 +16,6 @@ type Client interface { ListInstances(context.Context, *linodego.ListOptions) ([]linodego.Instance, error) GetInstanceIPAddresses(context.Context, int) (*linodego.InstanceIPAddressResponse, error) - ListInstanceConfigs(context.Context, int, *linodego.ListOptions) ([]linodego.InstanceConfig, error) - UpdateInstanceConfigInterface(context.Context, int, int, int, linodego.InstanceConfigInterfaceUpdateOptions) (*linodego.InstanceConfigInterface, error) - - ListVPCs(context.Context, *linodego.ListOptions) ([]linodego.VPC, 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/mock_client_test.go b/cloud/linode/client/mock_client_test.go index b39dca03..c7535897 100644 --- a/cloud/linode/client/mock_client_test.go +++ b/cloud/linode/client/mock_client_test.go @@ -226,21 +226,6 @@ func (mr *MockClientMockRecorder) ListFirewallDevices(arg0, arg1, arg2 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListFirewallDevices", reflect.TypeOf((*MockClient)(nil).ListFirewallDevices), arg0, arg1, arg2) } -// ListInstanceConfigs mocks base method. -func (m *MockClient) ListInstanceConfigs(arg0 context.Context, arg1 int, arg2 *linodego.ListOptions) ([]linodego.InstanceConfig, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListInstanceConfigs", arg0, arg1, arg2) - ret0, _ := ret[0].([]linodego.InstanceConfig) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListInstanceConfigs indicates an expected call of ListInstanceConfigs. -func (mr *MockClientMockRecorder) ListInstanceConfigs(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListInstanceConfigs", reflect.TypeOf((*MockClient)(nil).ListInstanceConfigs), arg0, arg1, arg2) -} - // ListInstances mocks base method. func (m *MockClient) ListInstances(arg0 context.Context, arg1 *linodego.ListOptions) ([]linodego.Instance, error) { m.ctrl.T.Helper() @@ -301,21 +286,6 @@ func (mr *MockClientMockRecorder) ListNodeBalancers(arg0, arg1 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListNodeBalancers", reflect.TypeOf((*MockClient)(nil).ListNodeBalancers), arg0, arg1) } -// 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() @@ -346,21 +316,6 @@ 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 5e9b0436..123c2039 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -25,17 +25,14 @@ 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 - EnableRouteController bool - VPCName string + KubeconfigFlag *pflag.Flag + LinodeGoDebug bool } type linodeCloud struct { client client.Client instances cloudprovider.InstancesV2 loadbalancers cloudprovider.LoadBalancer - routes cloudprovider.Routes } func init() { @@ -70,19 +67,12 @@ func newCloud() (cloudprovider.Interface, error) { linodeClient.SetDebug(true) } - 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{ + // Return struct that satisfies cloudprovider.Interface + return &linodeCloud{ client: linodeClient, instances: newInstances(linodeClient), loadbalancers: newLoadbalancers(linodeClient, region), - routes: routes, - } - return lcloud, nil + }, nil } func (c *linodeCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stopCh <-chan struct{}) { @@ -119,9 +109,6 @@ 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/instances.go b/cloud/linode/instances.go index ee027425..55362439 100644 --- a/cloud/linode/instances.go +++ b/cloud/linode/instances.go @@ -9,72 +9,21 @@ import ( "time" "github.com/linode/linodego" - "golang.org/x/sync/errgroup" 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]linodeInstance + nodes map[int]*linodego.Instance lastUpdate time.Time ttl time.Duration } -// getInstanceIPv4Addresses returns all ipv4 addresses configured on a linode. -func (nc *nodeCache) getInstanceIPv4Addresses(ctx context.Context, id int, client client.Client) ([]nodeIP, error) { - // Retrieve ipaddresses for the linode - addresses, err := client.GetInstanceIPAddresses(ctx, id) - if err != nil { - return nil, err - } - - var ips []nodeIP - if len(addresses.IPv4.Public) != 0 { - for _, ip := range addresses.IPv4.Public { - ips = append(ips, nodeIP{ip: ip.Address, ipType: v1.NodeExternalIP}) - } - } - - // Retrieve instance configs for the linode - configs, err := client.ListInstanceConfigs(ctx, id, &linodego.ListOptions{}) - if err != nil || len(configs) == 0 { - return nil, err - } - - // Iterate over interfaces in config and find VPC specific ips - for _, iface := range configs[0].Interfaces { - if iface.VPCID != nil && iface.IPv4.VPC != "" { - ips = append(ips, nodeIP{ip: iface.IPv4.VPC, ipType: v1.NodeInternalIP}) - } - } - - // NOTE: We specifically store VPC ips first so that if they exist, they are - // used as internal ip for the nodes than the private ip - if len(addresses.IPv4.Private) != 0 { - for _, ip := range addresses.IPv4.Private { - ips = append(ips, nodeIP{ip: ip.Address, ipType: v1.NodeInternalIP}) - } - } - - return ips, nil -} - // 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 { @@ -90,29 +39,11 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) return err } - nc.nodes = make(map[int]linodeInstance, len(instances)) + nc.nodes = make(map[int]*linodego.Instance) - mtx := sync.Mutex{} - g := new(errgroup.Group) for _, instance := range instances { instance := instance - g.Go(func() error { - addresses, err := nc.getInstanceIPv4Addresses(ctx, instance.ID, client) - if err != nil { - klog.Errorf("Failed fetching ip addresses for instance id %d. Error: %s", instance.ID, err.Error()) - return err - } - // take lock on map so that concurrent writes are safe - mtx.Lock() - defer mtx.Unlock() - node := linodeInstance{instance: &instance, ips: addresses} - nc.nodes[instance.ID] = node - return nil - }) - } - - if err := g.Wait(); err != nil { - return err + nc.nodes[instance.ID] = &instance } nc.lastUpdate = time.Now() @@ -133,10 +64,9 @@ 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]linodeInstance, 0), + nodes: make(map[int]*linodego.Instance), ttl: time.Duration(timeout) * time.Second, }} } @@ -153,8 +83,8 @@ func (i *instances) linodeByName(nodeName types.NodeName) (*linodego.Instance, e i.nodeCache.RLock() defer i.nodeCache.RUnlock() for _, node := range i.nodeCache.nodes { - if node.instance.Label == string(nodeName) { - return node.instance, nil + if node.Label == string(nodeName) { + return node, nil } } @@ -164,24 +94,11 @@ func (i *instances) linodeByName(nodeName types.NodeName) (*linodego.Instance, e func (i *instances) linodeByID(id int) (*linodego.Instance, error) { i.nodeCache.RLock() defer i.nodeCache.RUnlock() - linodeInstance, ok := i.nodeCache.nodes[id] + instance, ok := i.nodeCache.nodes[id] if !ok { return nil, cloudprovider.InstanceNotFound } - 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 + return instance, nil } func (i *instances) lookupLinode(ctx context.Context, node *v1.Node) (*linodego.Instance, error) { @@ -248,13 +165,7 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud return nil, err } - ips, err := i.getLinodeIPv4Addresses(ctx, node) - if err != nil { - sentry.CaptureError(ctx, err) - return nil, err - } - - if len(ips) == 0 { + if len(linode.IPv4) == 0 { err := instanceNoIPAddressesError{linode.ID} sentry.CaptureError(ctx, err) return nil, err @@ -262,8 +173,12 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud addresses := []v1.NodeAddress{{Type: v1.NodeHostName, Address: linode.Label}} - for _, ip := range ips { - addresses = append(addresses, v1.NodeAddress{Type: ip.ipType, Address: ip.ip}) + for _, ip := range linode.IPv4 { + ipType := v1.NodeExternalIP + if ip.IsPrivate() { + ipType = v1.NodeInternalIP + } + addresses = append(addresses, v1.NodeAddress{Type: ipType, Address: ip.String()}) } // note that Zone is omitted as it's not a thing in Linode @@ -276,23 +191,3 @@ 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 2eadd4e4..a93711a1 100644 --- a/cloud/linode/instances_test.go +++ b/cloud/linode/instances_test.go @@ -64,18 +64,6 @@ func TestInstanceExists(t *testing.T) { Type: "g6-standard-2", }, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), 123).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []*linodego.InstanceIP{ - {Address: "45.76.101.25"}, - }, - Private: []*linodego.InstanceIP{ - {Address: "192.168.133.65"}, - }, - }, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), 123, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{}, nil) exists, err := instances.InstanceExists(ctx, node) assert.NoError(t, err) @@ -90,18 +78,6 @@ func TestInstanceExists(t *testing.T) { client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: 123, Label: name}, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), 123).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []*linodego.InstanceIP{ - {Address: "45.76.101.25"}, - }, - Private: []*linodego.InstanceIP{ - {Address: "192.168.133.65"}, - }, - }, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), 123, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{}, nil) exists, err := instances.InstanceExists(ctx, node) assert.NoError(t, err) @@ -151,27 +127,13 @@ func TestMetadataRetrieval(t *testing.T) { client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: name, Type: linodeType, Region: region, IPv4: []*net.IP{&publicIPv4, &privateIPv4}}, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), id).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []*linodego.InstanceIP{ - {Address: "45.76.101.25"}, - }, - Private: []*linodego.InstanceIP{ - {Address: "192.168.133.65"}, - }, - }, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), 123, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{ - {ID: 123456}, - }, nil) meta, err := instances.InstanceMetadata(ctx, node) assert.NoError(t, err) assert.Equal(t, providerIDPrefix+strconv.Itoa(id), meta.ProviderID) assert.Equal(t, region, meta.Region) assert.Equal(t, linodeType, meta.InstanceType) - assert.Equal(t, []v1.NodeAddress{ + assert.Equal(t, meta.NodeAddresses, []v1.NodeAddress{ { Type: v1.NodeHostName, Address: name, @@ -184,7 +146,7 @@ func TestMetadataRetrieval(t *testing.T) { Type: v1.NodeInternalIP, Address: privateIPv4.String(), }, - }, meta.NodeAddresses) + }) }) ipTests := []struct { @@ -235,24 +197,12 @@ func TestMetadataRetrieval(t *testing.T) { node := nodeWithProviderID(providerID) ips := make([]*net.IP, 0, len(test.inputIPs)) - pubIPs := make([]*linodego.InstanceIP, 0) - privIPs := make([]*linodego.InstanceIP, 0) for _, ip := range test.inputIPs { parsed := net.ParseIP(ip) if parsed == nil { t.Fatalf("cannot parse %v as an ipv4", ip) } ips = append(ips, &parsed) - if parsed.IsPrivate() { - privIPs = append(privIPs, &linodego.InstanceIP{Address: ip}) - } else { - pubIPs = append(pubIPs, &linodego.InstanceIP{Address: ip}) - } - } - - ipv4s := &linodego.InstanceIPv4Response{ - Public: pubIPs, - Private: privIPs, } linodeType := "g6-standard-1" @@ -260,13 +210,6 @@ func TestMetadataRetrieval(t *testing.T) { client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: name, Type: linodeType, Region: region, IPv4: ips}, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), id).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: ipv4s, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), id, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{ - {ID: 123456}, - }, nil) meta, err := instances.InstanceMetadata(ctx, node) @@ -338,16 +281,6 @@ func TestInstanceShutdown(t *testing.T) { client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: "offline-linode", Status: linodego.InstanceOffline}, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), id).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []*linodego.InstanceIP{}, - Private: []*linodego.InstanceIP{}, - }, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), id, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{ - {ID: 123456}, - }, nil) shutdown, err := instances.InstanceShutdown(ctx, node) assert.NoError(t, err) @@ -361,16 +294,6 @@ func TestInstanceShutdown(t *testing.T) { client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: "shutting-down-linode", Status: linodego.InstanceShuttingDown}, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), id).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []*linodego.InstanceIP{}, - Private: []*linodego.InstanceIP{}, - }, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), id, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{ - {ID: 123456}, - }, nil) shutdown, err := instances.InstanceShutdown(ctx, node) assert.NoError(t, err) @@ -384,16 +307,6 @@ func TestInstanceShutdown(t *testing.T) { client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: "running-linode", Status: linodego.InstanceRunning}, }, nil) - client.EXPECT().GetInstanceIPAddresses(gomock.Any(), id).Times(1).Return(&linodego.InstanceIPAddressResponse{ - IPv4: &linodego.InstanceIPv4Response{ - Public: []*linodego.InstanceIP{}, - Private: []*linodego.InstanceIP{}, - }, - IPv6: nil, - }, nil) - client.EXPECT().ListInstanceConfigs(gomock.Any(), id, gomock.Any()).Times(1).Return([]linodego.InstanceConfig{ - {ID: 123456}, - }, nil) shutdown, err := instances.InstanceShutdown(ctx, node) assert.NoError(t, err) diff --git a/cloud/linode/node_controller.go b/cloud/linode/node_controller.go index 461407c9..e77eb20f 100644 --- a/cloud/linode/node_controller.go +++ b/cloud/linode/node_controller.go @@ -127,9 +127,8 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { lastUpdate := s.LastMetadataUpdate(node.Name) - uuid, foundLabel := node.Labels[annotations.AnnLinodeHostUUID] - configuredPrivateIP, foundAnnotation := node.Annotations[annotations.AnnLinodeNodePrivateIP] - if foundLabel && foundAnnotation && time.Since(lastUpdate) < s.ttl { + uuid, ok := node.Labels[annotations.AnnLinodeHostUUID] + if ok && time.Since(lastUpdate) < s.ttl { return nil } @@ -139,19 +138,7 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { return err } - 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 && configuredPrivateIP == expectedPrivateIP { + if uuid == linode.HostUUID { s.SetLastMetadataUpdate(node.Name) return nil } @@ -163,16 +150,13 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { return err } - // It may be that the UUID label and private ip annotation has been set - if n.Labels[annotations.AnnLinodeHostUUID] == linode.HostUUID && n.Annotations[annotations.AnnLinodeNodePrivateIP] == expectedPrivateIP { + // It may be that the UUID has been set + if n.Labels[annotations.AnnLinodeHostUUID] == linode.HostUUID { return nil } // Try to update the node n.Labels[annotations.AnnLinodeHostUUID] = linode.HostUUID - if 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 deleted file mode 100644 index dd634c29..00000000 --- a/cloud/linode/route_controller.go +++ /dev/null @@ -1,254 +0,0 @@ -package linode - -import ( - "context" - "fmt" - "os" - "strconv" - "sync" - "time" - - "github.com/linode/linodego" - "golang.org/x/sync/errgroup" - "golang.org/x/exp/slices" - 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.InstanceConfig - 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 - } - - instances, err := client.ListInstances(ctx, nil) - if err != nil { - return err - } - - rc.routes = make(map[int][]linodego.InstanceConfig, len(instances)) - - mtx := sync.Mutex{} - g := new(errgroup.Group) - for _, instance := range instances { - id := instance.ID - g.Go(func() error { - configs, err := client.ListInstanceConfigs(ctx, id, &linodego.ListOptions{}) - if err != nil { - klog.Errorf("Failed fetching instance configs for instance id %d. Error: %s", id, err.Error()) - return err - } - // take lock on map so that concurrent writes are safe - mtx.Lock() - defer mtx.Unlock() - rc.routes[id] = configs - return nil - }) - } - - if err := g.Wait(); err != nil { - return err - } - - 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"); ok { - if t, _ := strconv.Atoi(raw); t > 0 { - timeout = t - } - } - klog.V(3).Infof("TTL for routeCache set to %d", timeout) - - vpcid := 0 - if Options.EnableRouteController { - id, err := getVPCID(client, Options.VPCName) - if err != nil { - return nil, err - } - vpcid = id - } - - return &routes{ - vpcid: vpcid, - client: client, - instances: newInstances(client), - routeCache: &routeCache{ - routes: make(map[int][]linodego.InstanceConfig, 0), - ttl: time.Duration(timeout) * time.Second, - }, - }, nil -} - -// instanceConfigsByID returns InstanceConfigs for given instance id -func (r *routes) instanceConfigsByID(id int) ([]linodego.InstanceConfig, error) { - r.routeCache.RLock() - defer r.routeCache.RUnlock() - instanceConfigs, ok := r.routeCache.routes[id] - if !ok { - return nil, fmt.Errorf("no configs found for instance %d", id) - } - return instanceConfigs, nil -} - -// getInstanceConfigs returns InstanceConfigs for given instance id -// It refreshes routeCache if it has expired -func (r *routes) getInstanceConfigs(ctx context.Context, id int) ([]linodego.InstanceConfig, error) { - if err := r.routeCache.refreshRoutes(ctx, r.client); err != nil { - return nil, err - } - - return r.instanceConfigsByID(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 configs - configs, err := r.getInstanceConfigs(ctx, instance.ID) - if err != nil { - return err - } - - // find VPC interface and add route to it - for _, iface := range configs[0].Interfaces { - if iface.VPCID == nil || r.vpcid != *iface.VPCID || iface.IPv4.VPC == "" { - continue - } - - if slices.Contains(iface.IPRanges, route.DestinationCIDR) { - klog.V(4).Infof("Route already exists for node %s", route.TargetNode) - return nil - } - - ipRanges := append(iface.IPRanges, route.DestinationCIDR) - interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{ - IPRanges: ipRanges, - } - resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, configs[0].ID, iface.ID, interfaceUpdateOptions) - if err != nil { - return err - } - klog.V(4).Infof("Added routes for node %s. Current routes: %v", route.TargetNode, resp.IPRanges) - return nil - } - - return fmt.Errorf("unable to add route %s for node %s. no valid interface found", route.DestinationCIDR, route.TargetNode) -} - -// 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 - } - - configs, err := r.getInstanceConfigs(ctx, instance.ID) - if err != nil { - return err - } - - for _, iface := range configs[0].Interfaces { - if iface.VPCID == nil || r.vpcid != *iface.VPCID || iface.IPv4.VPC == "" { - continue - } - - ipRanges := []string{} - for _, configured_route := range iface.IPRanges { - if configured_route != route.DestinationCIDR { - ipRanges = append(ipRanges, configured_route) - } - } - - interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{ - IPRanges: ipRanges, - } - resp, err := r.client.UpdateInstanceConfigInterface(ctx, instance.ID, configs[0].ID, iface.ID, interfaceUpdateOptions) - if err != nil { - return err - } - klog.V(4).Infof("Deleted route for node %s. Current routes: %v", route.TargetNode, resp.IPRanges) - return nil - } - return fmt.Errorf("unable to remove route %s for node %s", route.DestinationCIDR, route.TargetNode) -} - -// 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 routes []*cloudprovider.Route - for _, instance := range instances { - configs, err := r.getInstanceConfigs(ctx, instance.ID) - if err != nil { - klog.Errorf("Failed finding routes for instance id %d. Error: %v", instance.ID, err) - continue - } - - for _, iface := range configs[0].Interfaces { - if iface.VPCID == nil || r.vpcid != *iface.VPCID || iface.IPv4.VPC == "" { - continue - } - - for _, ipsubnet := range iface.IPRanges { - route := &cloudprovider.Route{ - TargetNode: types.NodeName(instance.Label), - DestinationCIDR: ipsubnet, - } - klog.V(4).Infof("Found route: node %s, route %s", instance.Label, route.DestinationCIDR) - routes = append(routes, route) - } - } - } - return routes, nil -} diff --git a/cloud/linode/vpc.go b/cloud/linode/vpc.go deleted file mode 100644 index fd40e731..00000000 --- a/cloud/linode/vpc.go +++ /dev/null @@ -1,31 +0,0 @@ -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 4b2b3d41..6a38e6e0 100644 --- a/deploy/chart/templates/daemonset.yaml +++ b/deploy/chart/templates/daemonset.yaml @@ -36,15 +36,6 @@ spec: {{- if .Values.linodegoDebug }} - --linodego-debug={{ .Values.linodegoDebug }} {{- end }} - {{- if .Values.routeController }} - - --enable-route-controller=true - - --vpc-name={{ .Values.routeController.vpcName }} - - --configure-cloud-routes={{ .Values.routeController.configureCloudRoutes }} - - --cluster-cidr={{ .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 2df40a14..d8bf72ee 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -44,12 +44,6 @@ 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/main.go b/main.go index dfcf10bd..f82c9e18 100644 --- a/main.go +++ b/main.go @@ -76,8 +76,6 @@ 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) {