From e3c2045776b045a95bef3be09ba8b322780ecde2 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Fri, 15 Nov 2024 00:57:14 +0530 Subject: [PATCH] manage routes for instances in multiple vpcs in a single region (#241) * manage routes for instances in multiple vpcs in a single region * fix readme and helm chart * address review comments * add test for multiple vpcs * remove vpc from cache if it doesn't exist * address review comments --- README.md | 2 +- cloud/linode/cloud.go | 43 ++------- cloud/linode/instances.go | 15 ++- cloud/linode/route_controller.go | 126 ++++++++++++++------------ cloud/linode/route_controller_test.go | 126 +++++++++++++++++++------- cloud/linode/vpc.go | 54 ++++++++++- deploy/chart/templates/daemonset.yaml | 13 ++- deploy/chart/values.yaml | 3 +- main.go | 3 +- 9 files changed, 247 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 957de726..93aefe00 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ When running k8s clusters within VPC, node specific podCIDRs need to be allowed ##### Example usage in values.yaml ```yaml routeController: - vpcName: + vpcNames: clusterCIDR: 10.0.0.0/8 configureCloudRoutes: true ``` diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index 46230bbf..f6ec2f7c 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -6,13 +6,13 @@ import ( "net" "os" "strconv" - "sync" "time" "github.com/spf13/pflag" "golang.org/x/exp/slices" "k8s.io/client-go/informers" cloudprovider "k8s.io/cloud-provider" + "k8s.io/klog/v2" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" ) @@ -35,41 +35,14 @@ var Options struct { KubeconfigFlag *pflag.Flag LinodeGoDebug bool EnableRouteController bool + // Deprecated: use VPCNames instead VPCName string + VPCNames string LoadBalancerType string BGPNodeSelector string LinodeExternalNetwork *net.IPNet } -// 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 @@ -114,11 +87,13 @@ func newCloud() (cloudprovider.Interface, error) { linodeClient.SetDebug(true) } + if Options.VPCName != "" && Options.VPCNames != "" { + return nil, fmt.Errorf("cannot have both vpc-name and vpc-names set") + } + if Options.VPCName != "" { - err := vpcInfo.setDetails(linodeClient, Options.VPCName) - if err != nil { - return nil, fmt.Errorf("failed finding VPC ID: %w", err) - } + klog.Warningf("vpc-name flag is deprecated. Use vpc-names instead") + Options.VPCNames = Options.VPCName } routes, err := newRoutes(linodeClient) diff --git a/cloud/linode/instances.go b/cloud/linode/instances.go index f5cc449a..358aa569 100644 --- a/cloud/linode/instances.go +++ b/cloud/linode/instances.go @@ -79,11 +79,16 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) // 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, "")) + vpcNames := strings.Split(Options.VPCNames, ",") + for _, v := range vpcNames { + vpcName := strings.TrimSpace(v) + if vpcName == "" { + continue + } + resp, err := GetVPCIPAddresses(ctx, client, vpcName) if err != nil { - return err + klog.Errorf("failed updating instances cache for VPC %s. Error: %s", vpcName, err.Error()) + continue } for _, r := range resp { if r.Address == nil { @@ -97,7 +102,7 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) for i, instance := range instances { // if running within VPC, only store instances in cache which are part of VPC - if vpcID != 0 && len(vpcNodes[instance.ID]) == 0 { + if Options.VPCNames != "" && len(vpcNodes[instance.ID]) == 0 { continue } node := linodeInstance{ diff --git a/cloud/linode/route_controller.go b/cloud/linode/route_controller.go index 909ea76f..8b3bdf47 100644 --- a/cloud/linode/route_controller.go +++ b/cloud/linode/route_controller.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strconv" + "strings" "sync" "time" @@ -19,37 +20,43 @@ import ( ) type routeCache struct { - sync.RWMutex + Mu 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() +// RefreshCache checks if cache has expired and updates it accordingly +func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) { + rc.Mu.Lock() + defer rc.Mu.Unlock() if time.Since(rc.lastUpdate) < rc.ttl { - return nil + return } 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) + vpcNames := strings.Split(Options.VPCNames, ",") + for _, v := range vpcNames { + vpcName := strings.TrimSpace(v) + if vpcName == "" { + continue + } + resp, err := GetVPCIPAddresses(ctx, client, vpcName) + if err != nil { + klog.Errorf("failed updating cache for VPC %s. Error: %s", vpcName, err.Error()) + continue + } + 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 @@ -64,13 +71,11 @@ func newRoutes(client client.Client) (cloudprovider.Routes, error) { } 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) + if Options.EnableRouteController && Options.VPCNames == "" { + return nil, fmt.Errorf("cannot enable route controller as vpc-names is empty") } return &routes{ - vpcid: vpcid, client: client, instances: newInstances(client), routeCache: &routeCache{ @@ -82,8 +87,8 @@ func newRoutes(client client.Client) (cloudprovider.Routes, error) { // instanceRoutesByID returns routes for given instance id func (r *routes) instanceRoutesByID(id int) ([]linodego.VPCIP, error) { - r.routeCache.RLock() - defer r.routeCache.RUnlock() + r.routeCache.Mu.RLock() + defer r.routeCache.Mu.RUnlock() instanceRoutes, ok := r.routeCache.routes[id] if !ok { return nil, fmt.Errorf("no routes found for instance %d", id) @@ -94,10 +99,7 @@ func (r *routes) instanceRoutesByID(id int) ([]linodego.VPCIP, error) { // 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 - } - + r.routeCache.refreshRoutes(ctx, r.client) return r.instanceRoutesByID(id) } @@ -135,22 +137,25 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s // 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 - } + for _, vpcid := range GetAllVPCIDs() { + for _, ir := range instanceRoutes { + if ir.VPCID != vpcid { + continue + } - if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR { - klog.V(4).Infof("Route already exists for node %s", route.TargetNode) - return nil - } + if ir.Address != nil { + intfVPCIP = ir + continue + } - intfRoutes = append(intfRoutes, *ir.AddressRange) + 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 { @@ -185,21 +190,24 @@ func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *clo // 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 - } + for _, vpcid := range GetAllVPCIDs() { + for _, ir := range instanceRoutes { + if ir.VPCID != vpcid { + continue + } - if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR { - continue - } + if ir.Address != nil { + intfVPCIP = ir + continue + } + + if ir.AddressRange != nil && *ir.AddressRange == route.DestinationCIDR { + continue + } - intfRoutes = append(intfRoutes, *ir.AddressRange) + intfRoutes = append(intfRoutes, *ir.AddressRange) + } } if intfVPCIP.Address == nil { @@ -234,17 +242,19 @@ func (r *routes) ListRoutes(ctx context.Context, clusterName string) ([]*cloudpr } // check for configured routes - for _, ir := range instanceRoutes { - if ir.Address != nil || ir.VPCID != r.vpcid { - continue - } + for _, vpcid := range GetAllVPCIDs() { + for _, ir := range instanceRoutes { + if ir.Address != nil || ir.VPCID != vpcid { + continue + } - if ir.AddressRange != nil { - route := &cloudprovider.Route{ - TargetNode: types.NodeName(instance.Label), - DestinationCIDR: *ir.AddressRange, + if ir.AddressRange != nil { + route := &cloudprovider.Route{ + TargetNode: types.NodeName(instance.Label), + DestinationCIDR: *ir.AddressRange, + } + configuredRoutes = append(configuredRoutes, route) } - configuredRoutes = append(configuredRoutes, route) } } } diff --git a/cloud/linode/route_controller_test.go b/cloud/linode/route_controller_test.go index 2235b18c..6b2efc64 100644 --- a/cloud/linode/route_controller_test.go +++ b/cloud/linode/route_controller_test.go @@ -10,17 +10,17 @@ import ( "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/types" cloudprovider "k8s.io/cloud-provider" + "k8s.io/utils/ptr" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" ) func TestListRoutes(t *testing.T) { - Options.VPCName = "test" + Options.VPCNames = "test,abc" + vpcIDs["test"] = 1 + vpcIDs["abc"] = 2 Options.EnableRouteController = true - vpcInfo.id = 1 - vpcid := vpcInfo.getID() - nodeID := 123 name := "mock-instance" publicIPv4 := net.ParseIP("45.76.101.25") @@ -37,8 +37,8 @@ func TestListRoutes(t *testing.T) { 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") + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return([]linodego.VPCIP{}, nil) + routes, err := routeController.ListRoutes(ctx, "test") assert.NoError(t, err) assert.Empty(t, routes) }) @@ -60,8 +60,8 @@ func TestListRoutes(t *testing.T) { 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") + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return([]linodego.VPCIP{}, nil) + routes, err := routeController.ListRoutes(ctx, "test") assert.NoError(t, err) assert.Empty(t, routes) }) @@ -71,7 +71,7 @@ func TestListRoutes(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcid, + VPCID: vpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, @@ -86,8 +86,8 @@ func TestListRoutes(t *testing.T) { 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") + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(4).Return(noRoutesInVPC, nil) + routes, err := routeController.ListRoutes(ctx, "test") assert.NoError(t, err) assert.Empty(t, routes) }) @@ -98,21 +98,21 @@ func TestListRoutes(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcid, + VPCID: vpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcid, + VPCID: vpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange2, - VPCID: vpcid, + VPCID: vpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, @@ -127,8 +127,8 @@ func TestListRoutes(t *testing.T) { 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") + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(4).Return(routesInVPC, nil) + routes, err := routeController.ListRoutes(ctx, "test") assert.NoError(t, err) assert.NotEmpty(t, routes) assert.Equal(t, addressRange1, routes[0].DestinationCIDR) @@ -168,21 +168,81 @@ func TestListRoutes(t *testing.T) { 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") + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(4).Return(routesInDifferentVPC, nil) + routes, err := routeController.ListRoutes(ctx, "test") assert.NoError(t, err) assert.Empty(t, routes) }) + + t.Run("should return routes if multiple instances exists, connected to VPCs 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) + + vpcIP2 := "10.0.0.3" + addressRange3 := "10.192.40.0/24" + addressRange4 := "10.192.50.0/24" + + validInstance2 := linodego.Instance{ + ID: 124, + Label: "mock-instance2", + Type: linodeType, + Region: region, + IPv4: []*net.IP{&publicIPv4, &privateIPv4}, + } + + routesInVPC2 := []linodego.VPCIP{ + { + Address: &vpcIP2, + AddressRange: nil, + VPCID: vpcIDs["abc"], + NAT1To1: nil, + LinodeID: 124, + }, + { + Address: nil, + AddressRange: &addressRange3, + VPCID: vpcIDs["abc"], + NAT1To1: nil, + LinodeID: 124, + }, + { + Address: nil, + AddressRange: &addressRange4, + VPCID: vpcIDs["abc"], + NAT1To1: nil, + LinodeID: 124, + }, + } + + client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{validInstance, validInstance2}, nil) + c1 := client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(routesInVPC, nil) + c2 := client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).After(c1).Times(1).Return(routesInVPC2, nil) + c3 := client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).After(c2).Times(1).Return(routesInVPC, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).After(c3).Times(1).Return(routesInVPC2, nil) + routes, err := routeController.ListRoutes(ctx, "test") + assert.NoError(t, err) + assert.NotEmpty(t, routes) + cidrs := make([]string, len(routes)) + for i, value := range routes { + cidrs[i] = value.DestinationCIDR + } + assert.Contains(t, cidrs, addressRange1) + assert.Contains(t, cidrs, addressRange2) + assert.Contains(t, cidrs, addressRange3) + assert.Contains(t, cidrs, addressRange4) + }) } func TestCreateRoute(t *testing.T) { ctx := context.Background() - Options.VPCName = "test" + Options.VPCNames = "dummy" + vpcIDs["dummy"] = 1 Options.EnableRouteController = true - vpcInfo.id = 1 - vpcid := vpcInfo.getID() - nodeID := 123 name := "mock-instance" publicIPv4 := net.ParseIP("45.76.101.25") @@ -202,14 +262,14 @@ func TestCreateRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcid, + VPCID: vpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, } instanceConfigIntfWithVPCAndRoute := linodego.InstanceConfigInterface{ - VPCID: &vpcid, + VPCID: ptr.To(vpcIDs["dummy"]), IPv4: &linodego.VPCIPv4{VPC: vpcIP}, IPRanges: []string{"10.10.10.0/24"}, } @@ -238,14 +298,14 @@ func TestCreateRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcid, + VPCID: vpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcid, + VPCID: vpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, @@ -279,14 +339,12 @@ func TestCreateRoute(t *testing.T) { } func TestDeleteRoute(t *testing.T) { - Options.VPCName = "test" + Options.VPCNames = "dummy" + vpcIDs["dummy"] = 1 Options.EnableRouteController = true ctx := context.Background() - vpcInfo.id = 1 - vpcid := vpcInfo.getID() - nodeID := 123 name := "mock-instance" publicIPv4 := net.ParseIP("45.76.101.25") @@ -326,14 +384,14 @@ func TestDeleteRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcid, + VPCID: vpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, } instanceConfigIntfWithVPCAndNoRoute := linodego.InstanceConfigInterface{ - VPCID: &vpcid, + VPCID: ptr.To(vpcIDs["dummy"]), IPv4: &linodego.VPCIPv4{VPC: vpcIP}, IPRanges: []string{}, } @@ -356,14 +414,14 @@ func TestDeleteRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcid, + VPCID: vpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcid, + VPCID: vpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, diff --git a/cloud/linode/vpc.go b/cloud/linode/vpc.go index fd40e731..01c1ed14 100644 --- a/cloud/linode/vpc.go +++ b/cloud/linode/vpc.go @@ -3,9 +3,19 @@ package linode import ( "context" "fmt" + "net/http" + "strings" + "sync" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" "github.com/linode/linodego" + "k8s.io/klog/v2" +) + +var ( + Mu sync.RWMutex + // vpcIDs map stores vpc id's for given vpc labels + vpcIDs = make(map[string]int, 0) ) type vpcLookupError struct { @@ -16,16 +26,54 @@ 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{}) +// GetAllVPCIDs returns vpc ids stored in map +func GetAllVPCIDs() []int { + Mu.Lock() + defer Mu.Unlock() + values := make([]int, 0, len(vpcIDs)) + for _, v := range vpcIDs { + values = append(values, v) + } + return values +} + +// GetVPCID returns the VPC id of given VPC label +func GetVPCID(ctx context.Context, client client.Client, vpcName string) (int, error) { + Mu.Lock() + defer Mu.Unlock() + + // check if map contains vpc id for given label + if vpcid, ok := vpcIDs[vpcName]; ok { + return vpcid, nil + } + vpcs, err := client.ListVPCs(ctx, &linodego.ListOptions{}) if err != nil { return 0, err } for _, vpc := range vpcs { if vpc.Label == vpcName { + vpcIDs[vpcName] = vpc.ID return vpc.ID, nil } } return 0, vpcLookupError{vpcName} } + +// GetVPCIPAddresses returns vpc ip's for given VPC label +func GetVPCIPAddresses(ctx context.Context, client client.Client, vpcName string) ([]linodego.VPCIP, error) { + vpcID, err := GetVPCID(ctx, client, strings.TrimSpace(vpcName)) + if err != nil { + return nil, err + } + resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, "")) + if err != nil { + if linodego.ErrHasStatus(err, http.StatusNotFound) { + Mu.Lock() + defer Mu.Unlock() + klog.Errorf("vpc %s not found. Deleting entry from cache", vpcName) + delete(vpcIDs, vpcName) + } + return nil, err + } + return resp, nil +} diff --git a/deploy/chart/templates/daemonset.yaml b/deploy/chart/templates/daemonset.yaml index f8ed0397..0a46c517 100644 --- a/deploy/chart/templates/daemonset.yaml +++ b/deploy/chart/templates/daemonset.yaml @@ -38,7 +38,18 @@ spec: {{- end }} {{- if .Values.routeController }} - --enable-route-controller=true - - --vpc-name={{ required "A valid .Values.routeController.vpcName is required" .Values.routeController.vpcName }} + {{- if and .Values.routeController.vpcName .Values.routeController.vpcNames }} + {{- fail "Both vpcName and vpcNames are set. Please use only vpcNames." }} + {{- end }} + {{- if not (or .Values.routeController.vpcName .Values.routeController.vpcNames) }} + {{- fail "Neither vpcName nor vpcNames is set. Please set one of them." }} + {{- end }} + {{- if .Values.routeController.vpcName }} + - --vpc-name={{ .Values.routeController.vpcName }} + {{- end }} + {{- if .Values.routeController.vpcNames }} + - --vpc-names={{ .Values.routeController.vpcNames }} + {{- end }} - --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 }} diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index d360beef..5bd4546c 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -51,7 +51,8 @@ tolerations: # This section adds ability to enable route-controller for ccm # routeController: -# vpcName: +# vpcName: [Deprecated: use vpcNames instead] +# vpcNames: # clusterCIDR: 10.0.0.0/8 # configureCloudRoutes: true diff --git a/main.go b/main.go index f8b3c1ee..2ef42d02 100644 --- a/main.go +++ b/main.go @@ -81,7 +81,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") + command.Flags().StringVar(&linode.Options.VPCName, "vpc-name", "", "[deprecated: use vpc-names instead] vpc name whose routes will be managed by route-controller") + command.Flags().StringVar(&linode.Options.VPCNames, "vpc-names", "", "comma separated vpc names whose routes will be managed by route-controller") command.Flags().StringVar(&linode.Options.LoadBalancerType, "load-balancer-type", "nodebalancer", "configures which type of load-balancing to use for LoadBalancer Services (options: nodebalancer, cilium-bgp)") command.Flags().StringVar(&linode.Options.BGPNodeSelector, "bgp-node-selector", "", "node selector to use to perform shared IP fail-over with BGP (e.g. cilium-bgp-peering=true")