diff --git a/Makefile b/Makefile index a2136c40..20dbdaa8 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ fmt: .PHONY: test # we say code is not worth testing unless it's formatted test: fmt codegen - go test -v -coverpkg=./sentry,./cloud/linode/client,./cloud/linode/firewall,./cloud/linode,./cloud/nodeipam,./cloud/nodeipam/ipam -coverprofile ./coverage.out -cover ./sentry/... ./cloud/... $(TEST_ARGS) + go test -v -coverpkg=./sentry,./cloud/linode/client,./cloud/linode,./cloud/linode/utils,./cloud/linode/services,./cloud/nodeipam,./cloud/nodeipam/ipam -coverprofile ./coverage.out -cover ./sentry/... ./cloud/... $(TEST_ARGS) .PHONY: build-linux build-linux: codegen diff --git a/cloud/linode/cilium_loadbalancers.go b/cloud/linode/cilium_loadbalancers.go index c22f7317..f086f6c6 100644 --- a/cloud/linode/cilium_loadbalancers.go +++ b/cloud/linode/cilium_loadbalancers.go @@ -24,6 +24,8 @@ import ( "k8s.io/utils/ptr" "github.com/linode/linode-cloud-controller-manager/cloud/annotations" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" ) const ( @@ -111,7 +113,7 @@ func (l *loadbalancers) getExistingSharedIPs(ctx context.Context, ipHolder *lino // shareIPs shares the given list of IP addresses on the given Node func (l *loadbalancers) shareIPs(ctx context.Context, addrs []string, node *v1.Node) error { - nodeLinodeID, err := parseProviderID(node.Spec.ProviderID) + nodeLinodeID, err := ccmUtils.ParseProviderID(node.Spec.ProviderID) if err != nil { return err } @@ -159,8 +161,8 @@ func (l *loadbalancers) handleIPSharing(ctx context.Context, node *v1.Node, ipHo } // If performing Service load-balancing via IP sharing + BGP, check for a special annotation // added by the CCM gets set when load-balancer IPs have been successfully shared on the node - if Options.BGPNodeSelector != "" { - kv := strings.Split(Options.BGPNodeSelector, "=") + if options.Options.BGPNodeSelector != "" { + kv := strings.Split(options.Options.BGPNodeSelector, "=") // Check if node should be participating in IP sharing via the given selector if val, ok := node.Labels[kv[0]]; !ok || len(kv) != 2 || val != kv[1] { // not a selected Node @@ -243,7 +245,7 @@ func (l *loadbalancers) createSharedIP(ctx context.Context, nodes []*v1.Node, ip } // share the IPs with nodes participating in Cilium BGP peering - if Options.BGPNodeSelector == "" { + if options.Options.BGPNodeSelector == "" { for _, node := range nodes { if _, ok := node.Labels[commonControlPlaneLabel]; !ok { if err = l.shareIPs(ctx, addrs, node); err != nil { @@ -252,7 +254,7 @@ func (l *loadbalancers) createSharedIP(ctx context.Context, nodes []*v1.Node, ip } } } else { - kv := strings.Split(Options.BGPNodeSelector, "=") + kv := strings.Split(options.Options.BGPNodeSelector, "=") for _, node := range nodes { if val, ok := node.Labels[kv[0]]; ok && len(kv) == 2 && val == kv[1] { if err = l.shareIPs(ctx, addrs, node); err != nil { @@ -273,7 +275,7 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) return err } nodeList, err := l.kubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{ - LabelSelector: Options.BGPNodeSelector, + LabelSelector: options.Options.BGPNodeSelector, }) if err != nil { return err @@ -282,8 +284,8 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) serviceNn := getServiceNn(service) var ipHolderSuffix string - if Options.IpHolderSuffix != "" { - ipHolderSuffix = Options.IpHolderSuffix + if options.Options.IpHolderSuffix != "" { + ipHolderSuffix = options.Options.IpHolderSuffix klog.V(3).Infof("using parameter-based IP Holder suffix %s for Service %s", ipHolderSuffix, serviceNn) } @@ -291,7 +293,7 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) if err != nil { // return error or nil if not found since no IP holder means there // is no IP to reclaim - return IgnoreLinodeAPIError(err, http.StatusNotFound) + return ccmUtils.IgnoreLinodeAPIError(err, http.StatusNotFound) } svcIngress := service.Status.LoadBalancer.Ingress if len(svcIngress) > 0 && ipHolder != nil { @@ -300,19 +302,19 @@ func (l *loadbalancers) deleteSharedIP(ctx context.Context, service *v1.Service) for _, ingress := range svcIngress { // delete the shared IP on the Linodes it's shared on for _, node := range bgpNodes { - nodeLinodeID, err = parseProviderID(node.Spec.ProviderID) + nodeLinodeID, err = ccmUtils.ParseProviderID(node.Spec.ProviderID) if err != nil { return err } err = l.client.DeleteInstanceIPAddress(ctx, nodeLinodeID, ingress.IP) - if IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { + if ccmUtils.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { return err } } // finally delete the shared IP on the ip-holder err = l.client.DeleteInstanceIPAddress(ctx, ipHolder.ID, ingress.IP) - if IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { + if ccmUtils.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil { return err } } @@ -415,7 +417,7 @@ func (l *loadbalancers) retrieveCiliumClientset() error { kubeConfig *rest.Config err error ) - kubeconfigFlag := Options.KubeconfigFlag + kubeconfigFlag := options.Options.KubeconfigFlag if kubeconfigFlag == nil || kubeconfigFlag.Value.String() == "" { kubeConfig, err = rest.InClusterConfig() } else { @@ -513,7 +515,7 @@ func (l *loadbalancers) ensureCiliumBGPPeeringPolicy(ctx context.Context) error // otherwise create it var nodeSelector slimv1.LabelSelector // If no BGPNodeSelector is specified, select all worker nodes. - if Options.BGPNodeSelector == "" { + if options.Options.BGPNodeSelector == "" { nodeSelector = slimv1.LabelSelector{ MatchExpressions: []slimv1.LabelSelectorRequirement{ { @@ -523,9 +525,9 @@ func (l *loadbalancers) ensureCiliumBGPPeeringPolicy(ctx context.Context) error }, } } else { - kv := strings.Split(Options.BGPNodeSelector, "=") + kv := strings.Split(options.Options.BGPNodeSelector, "=") if len(kv) != BGPNodeSelectorFlagInputLen { - return fmt.Errorf("invalid node selector %s", Options.BGPNodeSelector) + return fmt.Errorf("invalid node selector %s", options.Options.BGPNodeSelector) } nodeSelector = slimv1.LabelSelector{MatchLabels: map[string]string{kv[0]: kv[1]}} diff --git a/cloud/linode/cilium_loadbalancers_test.go b/cloud/linode/cilium_loadbalancers_test.go index 2d3aa17b..4c7a0929 100644 --- a/cloud/linode/cilium_loadbalancers_test.go +++ b/cloud/linode/cilium_loadbalancers_test.go @@ -15,6 +15,8 @@ import ( "k8s.io/client-go/kubernetes" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" ) const ( @@ -32,7 +34,7 @@ var ( Labels: map[string]string{"cilium-bgp-peering": "true"}, }, Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", providerIDPrefix, 11111), + ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 11111), }, }, { @@ -41,7 +43,7 @@ var ( Labels: map[string]string{"cilium-bgp-peering": "true"}, }, Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", providerIDPrefix, 22222), + ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 22222), }, }, { @@ -49,7 +51,7 @@ var ( Name: "node-3", }, Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", providerIDPrefix, 33333), + ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 33333), }, }, { @@ -60,7 +62,7 @@ var ( }, }, Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", providerIDPrefix, 44444), + ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 44444), }, }, } @@ -71,7 +73,7 @@ var ( Labels: map[string]string{"cilium-bgp-peering": "true"}, }, Spec: v1.NodeSpec{ - ProviderID: fmt.Sprintf("%s%d", providerIDPrefix, 55555), + ProviderID: fmt.Sprintf("%s%d", ccmUtils.ProviderIDPrefix, 55555), }, }, } @@ -202,7 +204,7 @@ func addNodes(t *testing.T, kubeClient kubernetes.Interface, nodes []*v1.Node) { func createNewIpHolderInstance() linodego.Instance { return linodego.Instance{ ID: 123456, - Label: generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix), + Label: generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix), Type: "g6-standard-1", Region: "us-west", IPv4: []*net.IP{&publicIPv4}, @@ -212,8 +214,8 @@ func createNewIpHolderInstance() linodego.Instance { func testNoBGPNodeLabel(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = "" - Options.IpHolderSuffix = clusterName + options.Options.BGPNodeSelector = "" + options.Options.IpHolderSuffix = clusterName t.Setenv("BGP_PEER_PREFIX", "2600:3cef") svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -231,7 +233,7 @@ func testNoBGPNodeLabel(t *testing.T, mc *mocks.MockClient) { } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) @@ -271,7 +273,7 @@ func testNoBGPNodeLabel(t *testing.T, mc *mocks.MockClient) { func testUnsupportedRegion(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector + options.Options.BGPNodeSelector = nodeSelector svc := createTestService() kubeClient, _ := k8sClient.NewFakeClientset() @@ -302,7 +304,7 @@ func testUnsupportedRegion(t *testing.T, mc *mocks.MockClient) { func testCreateWithExistingIPHolderWithOldIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector + options.Options.BGPNodeSelector = nodeSelector svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -346,8 +348,8 @@ func testCreateWithExistingIPHolderWithOldIpHolderNamingConvention(t *testing.T, func testCreateWithExistingIPHolderWithNewIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = clusterName + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = clusterName svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -391,8 +393,8 @@ func testCreateWithExistingIPHolderWithNewIpHolderNamingConvention(t *testing.T, func testCreateWithExistingIPHolderWithNewIpHolderNamingConventionUsingLongSuffix(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = "OaTJrRuufacHVougjwkpBpmstiqvswvBNEMWXsRYfMBTCkKIUTXpbGIcIbDWSQp" + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = "OaTJrRuufacHVougjwkpBpmstiqvswvBNEMWXsRYfMBTCkKIUTXpbGIcIbDWSQp" svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -436,8 +438,8 @@ func testCreateWithExistingIPHolderWithNewIpHolderNamingConventionUsingLongSuffi func testCreateWithNoExistingIPHolderUsingNoSuffix(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = "" + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = "" svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -453,7 +455,7 @@ func testCreateWithNoExistingIPHolderUsingNoSuffix(t *testing.T, mc *mocks.MockC t.Errorf("json marshal error: %v", err) } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) @@ -488,8 +490,8 @@ func testCreateWithNoExistingIPHolderUsingNoSuffix(t *testing.T, mc *mocks.MockC func testCreateWithNoExistingIPHolderUsingShortSuffix(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = clusterName + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = clusterName svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -505,7 +507,7 @@ func testCreateWithNoExistingIPHolderUsingShortSuffix(t *testing.T, mc *mocks.Mo t.Errorf("json marshal error: %v", err) } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) @@ -540,8 +542,8 @@ func testCreateWithNoExistingIPHolderUsingShortSuffix(t *testing.T, mc *mocks.Mo func testCreateWithNoExistingIPHolderUsingLongSuffix(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = "OaTJrRuufacHVougjwkpBpmstiqvswvBNEMWXsRYfMBTCkKIUTXpbGIcIbDWSQp" + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = "OaTJrRuufacHVougjwkpBpmstiqvswvBNEMWXsRYfMBTCkKIUTXpbGIcIbDWSQp" svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -557,7 +559,7 @@ func testCreateWithNoExistingIPHolderUsingLongSuffix(t *testing.T, mc *mocks.Moc t.Errorf("json marshal error: %v", err) } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) @@ -592,7 +594,7 @@ func testCreateWithNoExistingIPHolderUsingLongSuffix(t *testing.T, mc *mocks.Moc func testEnsureCiliumLoadBalancerDeletedWithOldIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector + options.Options.BGPNodeSelector = nodeSelector svc := createTestService() kubeClient, _ := k8sClient.NewFakeClientset() @@ -623,8 +625,8 @@ func testEnsureCiliumLoadBalancerDeletedWithOldIpHolderNamingConvention(t *testi func testEnsureCiliumLoadBalancerDeletedWithNewIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = clusterName + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = clusterName svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -643,7 +645,7 @@ func testEnsureCiliumLoadBalancerDeletedWithNewIpHolderNamingConvention(t *testi t.Errorf("json marshal error: %v", err) } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) @@ -662,7 +664,7 @@ func testEnsureCiliumLoadBalancerDeletedWithNewIpHolderNamingConvention(t *testi func testCiliumUpdateLoadBalancerAddNodeWithOldIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector + options.Options.BGPNodeSelector = nodeSelector svc := createTestService() kubeClient, _ := k8sClient.NewFakeClientset() @@ -723,8 +725,8 @@ func testCiliumUpdateLoadBalancerAddNodeWithOldIpHolderNamingConvention(t *testi func testCiliumUpdateLoadBalancerAddNodeWithNewIpHolderNamingConvention(t *testing.T, mc *mocks.MockClient) { t.Helper() - Options.BGPNodeSelector = nodeSelector - Options.IpHolderSuffix = clusterName + options.Options.BGPNodeSelector = nodeSelector + options.Options.IpHolderSuffix = clusterName svc := createTestService() newIpHolderInstance = createNewIpHolderInstance() @@ -740,7 +742,7 @@ func testCiliumUpdateLoadBalancerAddNodeWithNewIpHolderNamingConvention(t *testi t.Errorf("json marshal error: %v", err) } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) @@ -777,7 +779,7 @@ func testCiliumUpdateLoadBalancerAddNodeWithNewIpHolderNamingConvention(t *testi t.Errorf("json marshal error: %v", err) } mc.EXPECT().ListInstances(gomock.Any(), linodego.NewListOptions(1, string(rawFilter))).Times(1).Return([]linodego.Instance{}, nil) - filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, Options.IpHolderSuffix)} + filter = map[string]string{"label": generateClusterScopedIPHolderLinodeName(zone, options.Options.IpHolderSuffix)} rawFilter, err = json.Marshal(filter) if err != nil { t.Errorf("json marshal error: %v", err) diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index 8b1d67a4..01a5b003 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -4,19 +4,19 @@ import ( "context" "fmt" "io" - "net" "os" "regexp" "strconv" "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" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" ) const ( @@ -31,38 +31,6 @@ const ( var supportedLoadBalancerTypes = []string{ciliumLBType, nodeBalancerLBType} -// Options is a configuration object for this cloudprovider implementation. -// We expect it to be initialized with flags external to this package, likely in -// main.go -var Options struct { - KubeconfigFlag *pflag.Flag - LinodeGoDebug bool - EnableRouteController bool - EnableTokenHealthChecker bool - VPCNames []string - VPCIDs []int - SubnetNames []string - SubnetIDs []int - LoadBalancerType string - BGPNodeSelector string - IpHolderSuffix string - LinodeExternalNetwork *net.IPNet - NodeBalancerTags []string - DefaultNBType string - NodeBalancerBackendIPv4Subnet string - NodeBalancerBackendIPv4SubnetID int - NodeBalancerBackendIPv4SubnetName string - DisableNodeBalancerVPCBackends bool - GlobalStopChannel chan<- struct{} - EnableIPv6ForLoadBalancers bool - AllocateNodeCIDRs bool - DisableIPv6NodeCIDRAllocation bool - ClusterCIDRIPv4 string - NodeCIDRMaskSizeIPv4 int - NodeCIDRMaskSizeIPv6 int - NodeBalancerPrefix string -} - type linodeCloud struct { client client.Client instances cloudprovider.InstancesV2 @@ -72,7 +40,7 @@ type linodeCloud struct { } var ( - instanceCache *instances + instanceCache *services.Instances ipHolderCharLimit int = 23 NodeBalancerPrefixCharLimit int = 19 ) @@ -94,7 +62,7 @@ func newLinodeClientWithPrometheus(apiToken string, timeout time.Duration) (clie return nil, fmt.Errorf("client was not created successfully: %w", err) } - if Options.LinodeGoDebug { + if options.Options.LinodeGoDebug { linodeClient.SetDebug(true) } @@ -128,7 +96,7 @@ func newCloud() (cloudprovider.Interface, error) { var healthChecker *healthChecker - if Options.EnableTokenHealthChecker { + if options.Options.EnableTokenHealthChecker { var authenticated bool authenticated, err = client.CheckClientAuthenticated(context.TODO(), linodeClient) if err != nil { @@ -139,63 +107,63 @@ func newCloud() (cloudprovider.Interface, error) { return nil, fmt.Errorf("linode api token %q is invalid", accessTokenEnv) } - healthChecker = newHealthChecker(linodeClient, tokenHealthCheckPeriod, Options.GlobalStopChannel) + healthChecker = newHealthChecker(linodeClient, tokenHealthCheckPeriod, options.Options.GlobalStopChannel) } - err = validateAndSetVPCSubnetFlags(linodeClient) + err = services.ValidateAndSetVPCSubnetFlags(linodeClient) if err != nil { return nil, fmt.Errorf("failed to validate VPC and subnet flags: %w", err) } - if Options.NodeBalancerBackendIPv4SubnetID != 0 && Options.NodeBalancerBackendIPv4SubnetName != "" { + if options.Options.NodeBalancerBackendIPv4SubnetID != 0 && options.Options.NodeBalancerBackendIPv4SubnetName != "" { return nil, fmt.Errorf("cannot have both --nodebalancer-backend-ipv4-subnet-id and --nodebalancer-backend-ipv4-subnet-name set") } - if Options.DisableNodeBalancerVPCBackends { + if options.Options.DisableNodeBalancerVPCBackends { klog.Infof("NodeBalancer VPC backends are disabled, no VPC backends will be created for NodeBalancers") - Options.NodeBalancerBackendIPv4SubnetID = 0 - Options.NodeBalancerBackendIPv4SubnetName = "" - } else if Options.NodeBalancerBackendIPv4SubnetName != "" { - Options.NodeBalancerBackendIPv4SubnetID, err = getNodeBalancerBackendIPv4SubnetID(linodeClient) + options.Options.NodeBalancerBackendIPv4SubnetID = 0 + options.Options.NodeBalancerBackendIPv4SubnetName = "" + } else if options.Options.NodeBalancerBackendIPv4SubnetName != "" { + options.Options.NodeBalancerBackendIPv4SubnetID, err = services.GetNodeBalancerBackendIPv4SubnetID(linodeClient) if err != nil { - return nil, fmt.Errorf("failed to get backend IPv4 subnet ID for subnet name %s: %w", Options.NodeBalancerBackendIPv4SubnetName, err) + return nil, fmt.Errorf("failed to get backend IPv4 subnet ID for subnet name %s: %w", options.Options.NodeBalancerBackendIPv4SubnetName, err) } - klog.Infof("Using NodeBalancer backend IPv4 subnet ID %d for subnet name %s", Options.NodeBalancerBackendIPv4SubnetID, Options.NodeBalancerBackendIPv4SubnetName) + klog.Infof("Using NodeBalancer backend IPv4 subnet ID %d for subnet name %s", options.Options.NodeBalancerBackendIPv4SubnetID, options.Options.NodeBalancerBackendIPv4SubnetName) } - instanceCache = newInstances(linodeClient) + instanceCache = services.NewInstances(linodeClient) routes, err := newRoutes(linodeClient, instanceCache) if err != nil { return nil, fmt.Errorf("routes client was not created successfully: %w", err) } - if Options.LoadBalancerType != "" && !slices.Contains(supportedLoadBalancerTypes, Options.LoadBalancerType) { + if options.Options.LoadBalancerType != "" && !slices.Contains(supportedLoadBalancerTypes, options.Options.LoadBalancerType) { return nil, fmt.Errorf( - "unsupported default load-balancer type %s. Options are %v", - Options.LoadBalancerType, + "unsupported default load-balancer type %s. options.Options are %v", + options.Options.LoadBalancerType, supportedLoadBalancerTypes, ) } - if Options.IpHolderSuffix != "" { - klog.Infof("Using IP holder suffix '%s'\n", Options.IpHolderSuffix) + if options.Options.IpHolderSuffix != "" { + klog.Infof("Using IP holder suffix '%s'\n", options.Options.IpHolderSuffix) } - if len(Options.IpHolderSuffix) > ipHolderCharLimit { - msg := fmt.Sprintf("ip-holder-suffix must be %d characters or less: %s is %d characters\n", ipHolderCharLimit, Options.IpHolderSuffix, len(Options.IpHolderSuffix)) + if len(options.Options.IpHolderSuffix) > ipHolderCharLimit { + msg := fmt.Sprintf("ip-holder-suffix must be %d characters or less: %s is %d characters\n", ipHolderCharLimit, options.Options.IpHolderSuffix, len(options.Options.IpHolderSuffix)) klog.Error(msg) return nil, fmt.Errorf("%s", msg) } - if len(Options.NodeBalancerPrefix) > NodeBalancerPrefixCharLimit { - msg := fmt.Sprintf("nodebalancer-prefix must be %d characters or less: %s is %d characters\n", NodeBalancerPrefixCharLimit, Options.NodeBalancerPrefix, len(Options.NodeBalancerPrefix)) + if len(options.Options.NodeBalancerPrefix) > NodeBalancerPrefixCharLimit { + msg := fmt.Sprintf("nodebalancer-prefix must be %d characters or less: %s is %d characters\n", NodeBalancerPrefixCharLimit, options.Options.NodeBalancerPrefix, len(options.Options.NodeBalancerPrefix)) klog.Error(msg) return nil, fmt.Errorf("%s", msg) } validPrefix := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) - if !validPrefix.MatchString(Options.NodeBalancerPrefix) { - msg := fmt.Sprintf("nodebalancer-prefix must be no empty and use only letters, numbers, underscores, and dashes: %s\n", Options.NodeBalancerPrefix) + if !validPrefix.MatchString(options.Options.NodeBalancerPrefix) { + msg := fmt.Sprintf("nodebalancer-prefix must be no empty and use only letters, numbers, underscores, and dashes: %s\n", options.Options.NodeBalancerPrefix) klog.Error(msg) return nil, fmt.Errorf("%s", msg) } @@ -258,7 +226,7 @@ func (c *linodeCloud) Clusters() (cloudprovider.Clusters, bool) { } func (c *linodeCloud) Routes() (cloudprovider.Routes, bool) { - if Options.EnableRouteController { + if options.Options.EnableRouteController { return c.routes, true } return nil, false diff --git a/cloud/linode/cloud_test.go b/cloud/linode/cloud_test.go index d35b7d2d..4aed65e2 100644 --- a/cloud/linode/cloud_test.go +++ b/cloud/linode/cloud_test.go @@ -11,6 +11,8 @@ import ( cloudprovider "k8s.io/cloud-provider" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" ) func TestNewCloudRouteControllerDisabled(t *testing.T) { @@ -20,18 +22,18 @@ func TestNewCloudRouteControllerDisabled(t *testing.T) { t.Setenv("LINODE_API_TOKEN", "dummyapitoken") t.Setenv("LINODE_REGION", "us-east") t.Setenv("LINODE_REQUEST_TIMEOUT_SECONDS", "10") - Options.NodeBalancerPrefix = "ccm" + options.Options.NodeBalancerPrefix = "ccm" t.Run("should not fail if vpc is empty and routecontroller is disabled", func(t *testing.T) { - Options.VPCNames = []string{} - Options.EnableRouteController = false + options.Options.VPCNames = []string{} + options.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.VPCNames = []string{} - Options.EnableRouteController = true + options.Options.VPCNames = []string{} + options.Options.EnableRouteController = true _, err := newCloud() assert.Error(t, err) }) @@ -46,8 +48,8 @@ func TestNewCloud(t *testing.T) { t.Setenv("LINODE_REQUEST_TIMEOUT_SECONDS", "10") t.Setenv("LINODE_ROUTES_CACHE_TTL_SECONDS", "60") t.Setenv("LINODE_URL", "https://api.linode.com/v4") - Options.LinodeGoDebug = true - Options.NodeBalancerPrefix = "ccm" + options.Options.LinodeGoDebug = true + options.Options.NodeBalancerPrefix = "ccm" t.Run("should fail if api token is empty", func(t *testing.T) { t.Setenv("LINODE_API_TOKEN", "") @@ -62,62 +64,62 @@ func TestNewCloud(t *testing.T) { }) t.Run("should fail if both nodeBalancerBackendIPv4SubnetID and nodeBalancerBackendIPv4SubnetName are set", func(t *testing.T) { - Options.VPCNames = []string{"tt"} - Options.NodeBalancerBackendIPv4SubnetID = 12345 - Options.NodeBalancerBackendIPv4SubnetName = "test-subnet" + options.Options.VPCNames = []string{"tt"} + options.Options.NodeBalancerBackendIPv4SubnetID = 12345 + options.Options.NodeBalancerBackendIPv4SubnetName = "test-subnet" defer func() { - Options.VPCNames = []string{} - Options.NodeBalancerBackendIPv4SubnetID = 0 - Options.NodeBalancerBackendIPv4SubnetName = "" + options.Options.VPCNames = []string{} + options.Options.NodeBalancerBackendIPv4SubnetID = 0 + options.Options.NodeBalancerBackendIPv4SubnetName = "" }() _, err := newCloud() assert.Error(t, err, "expected error when both nodeBalancerBackendIPv4SubnetID and nodeBalancerBackendIPv4SubnetName are set") }) t.Run("should fail if incorrect loadbalancertype is set", func(t *testing.T) { - rtEnabled := Options.EnableRouteController - Options.EnableRouteController = false - Options.LoadBalancerType = "test" - Options.VPCNames = []string{"vpc-test1", "vpc-test2"} - Options.NodeBalancerBackendIPv4SubnetName = "t1" - vpcIDs = map[string]int{"vpc-test1": 1, "vpc-test2": 2, "vpc-test3": 3} - subnetIDs = map[string]int{"t1": 1, "t2": 2, "t3": 3} + rtEnabled := options.Options.EnableRouteController + options.Options.EnableRouteController = false + options.Options.LoadBalancerType = "test" + options.Options.VPCNames = []string{"vpc-test1", "vpc-test2"} + options.Options.NodeBalancerBackendIPv4SubnetName = "t1" + services.VpcIDs = map[string]int{"vpc-test1": 1, "vpc-test2": 2, "vpc-test3": 3} + services.SubnetIDs = map[string]int{"t1": 1, "t2": 2, "t3": 3} defer func() { - Options.LoadBalancerType = "" - Options.EnableRouteController = rtEnabled - Options.VPCNames = []string{} - Options.NodeBalancerBackendIPv4SubnetID = 0 - Options.NodeBalancerBackendIPv4SubnetName = "" - vpcIDs = map[string]int{} - subnetIDs = map[string]int{} + options.Options.LoadBalancerType = "" + options.Options.EnableRouteController = rtEnabled + options.Options.VPCNames = []string{} + options.Options.NodeBalancerBackendIPv4SubnetID = 0 + options.Options.NodeBalancerBackendIPv4SubnetName = "" + services.VpcIDs = map[string]int{} + services.SubnetIDs = map[string]int{} }() _, err := newCloud() assert.Error(t, err, "expected error if incorrect loadbalancertype is set") }) t.Run("should fail if ipholdersuffix is longer than 23 chars", func(t *testing.T) { - suffix := Options.IpHolderSuffix - Options.IpHolderSuffix = strings.Repeat("a", 24) - rtEnabled := Options.EnableRouteController - Options.EnableRouteController = false + suffix := options.Options.IpHolderSuffix + options.Options.IpHolderSuffix = strings.Repeat("a", 24) + rtEnabled := options.Options.EnableRouteController + options.Options.EnableRouteController = false defer func() { - Options.IpHolderSuffix = suffix - Options.EnableRouteController = rtEnabled + options.Options.IpHolderSuffix = suffix + options.Options.EnableRouteController = rtEnabled }() _, err := newCloud() assert.Error(t, err, "expected error if ipholdersuffix is longer than 23 chars") }) t.Run("should fail if nodebalancer-prefix is longer than 19 chars", func(t *testing.T) { - prefix := Options.NodeBalancerPrefix - rtEnabled := Options.EnableRouteController - Options.EnableRouteController = false - Options.LoadBalancerType = "nodebalancer" - Options.NodeBalancerPrefix = strings.Repeat("a", 21) + prefix := options.Options.NodeBalancerPrefix + rtEnabled := options.Options.EnableRouteController + options.Options.EnableRouteController = false + options.Options.LoadBalancerType = "nodebalancer" + options.Options.NodeBalancerPrefix = strings.Repeat("a", 21) defer func() { - Options.NodeBalancerPrefix = prefix - Options.LoadBalancerType = "" - Options.EnableRouteController = rtEnabled + options.Options.NodeBalancerPrefix = prefix + options.Options.LoadBalancerType = "" + options.Options.EnableRouteController = rtEnabled }() _, err := newCloud() t.Log(err) @@ -126,15 +128,15 @@ func TestNewCloud(t *testing.T) { }) t.Run("should fail if nodebalancer-prefix is empty", func(t *testing.T) { - prefix := Options.NodeBalancerPrefix - rtEnabled := Options.EnableRouteController - Options.EnableRouteController = false - Options.LoadBalancerType = "nodebalancer" - Options.NodeBalancerPrefix = "" + prefix := options.Options.NodeBalancerPrefix + rtEnabled := options.Options.EnableRouteController + options.Options.EnableRouteController = false + options.Options.LoadBalancerType = "nodebalancer" + options.Options.NodeBalancerPrefix = "" defer func() { - Options.NodeBalancerPrefix = prefix - Options.LoadBalancerType = "" - Options.EnableRouteController = rtEnabled + options.Options.NodeBalancerPrefix = prefix + options.Options.LoadBalancerType = "" + options.Options.EnableRouteController = rtEnabled }() _, err := newCloud() t.Log(err) @@ -143,15 +145,15 @@ func TestNewCloud(t *testing.T) { }) t.Run("should fail if not validated nodebalancer-prefix", func(t *testing.T) { - prefix := Options.NodeBalancerPrefix - rtEnabled := Options.EnableRouteController - Options.EnableRouteController = false - Options.LoadBalancerType = "nodebalancer" - Options.NodeBalancerPrefix = "\\+x" + prefix := options.Options.NodeBalancerPrefix + rtEnabled := options.Options.EnableRouteController + options.Options.EnableRouteController = false + options.Options.LoadBalancerType = "nodebalancer" + options.Options.NodeBalancerPrefix = "\\+x" defer func() { - Options.NodeBalancerPrefix = prefix - Options.LoadBalancerType = "" - Options.EnableRouteController = rtEnabled + options.Options.NodeBalancerPrefix = prefix + options.Options.LoadBalancerType = "" + options.Options.EnableRouteController = rtEnabled }() _, err := newCloud() t.Log(err) @@ -180,7 +182,7 @@ func Test_linodeCloud_LoadBalancer(t *testing.T) { name: "should return loadbalancer interface", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: nil, }, @@ -227,11 +229,11 @@ func Test_linodeCloud_InstancesV2(t *testing.T) { name: "should return instances interface", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: nil, }, - want: newInstances(client), + want: services.NewInstances(client), want1: true, }, } @@ -274,7 +276,7 @@ func Test_linodeCloud_Instances(t *testing.T) { name: "should return nil", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: nil, }, @@ -321,7 +323,7 @@ func Test_linodeCloud_Zones(t *testing.T) { name: "should return nil", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: nil, }, @@ -368,7 +370,7 @@ func Test_linodeCloud_Clusters(t *testing.T) { name: "should return nil", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: nil, }, @@ -417,7 +419,7 @@ func Test_linodeCloud_Routes(t *testing.T) { name: "should return nil", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: r, EnableRouteController: false, @@ -429,7 +431,7 @@ func Test_linodeCloud_Routes(t *testing.T) { name: "should return routes interface", fields: fields{ client: client, - instances: newInstances(client), + instances: services.NewInstances(client), loadbalancers: newLoadbalancers(client, "us-east"), routes: r, EnableRouteController: true, @@ -446,9 +448,9 @@ func Test_linodeCloud_Routes(t *testing.T) { loadbalancers: tt.fields.loadbalancers, routes: tt.fields.routes, } - rt := Options.EnableRouteController - defer func() { Options.EnableRouteController = rt }() - Options.EnableRouteController = tt.fields.EnableRouteController + rt := options.Options.EnableRouteController + defer func() { options.Options.EnableRouteController = rt }() + options.Options.EnableRouteController = tt.fields.EnableRouteController got, got1 := c.Routes() if !reflect.DeepEqual(got, tt.want) { t.Errorf("linodeCloud.Routes() got = %v, want %v", got, tt.want) diff --git a/cloud/linode/common.go b/cloud/linode/common.go deleted file mode 100644 index 9c4aed9f..00000000 --- a/cloud/linode/common.go +++ /dev/null @@ -1,56 +0,0 @@ -package linode - -import ( - "fmt" - "net" - "strconv" - "strings" - - "github.com/linode/linodego" -) - -const ( - providerIDPrefix = "linode://" - DNS1123LabelMaxLength int = 63 -) - -type invalidProviderIDError struct { - value string -} - -func (e invalidProviderIDError) Error() string { - return fmt.Sprintf("invalid provider ID %q", e.value) -} - -func isLinodeProviderID(providerID string) bool { - return strings.HasPrefix(providerID, providerIDPrefix) -} - -func parseProviderID(providerID string) (int, error) { - if !isLinodeProviderID(providerID) { - return 0, invalidProviderIDError{providerID} - } - id, err := strconv.Atoi(strings.TrimPrefix(providerID, providerIDPrefix)) - if err != nil { - return 0, invalidProviderIDError{providerID} - } - return id, nil -} - -// IgnoreLinodeAPIError returns the error except matches to status code -func IgnoreLinodeAPIError(err error, code int) error { - apiErr := linodego.Error{Code: code} - if apiErr.Is(err) { - err = nil - } - - return err -} - -func isPrivate(ip *net.IP) bool { - if Options.LinodeExternalNetwork == nil { - return ip.IsPrivate() - } - - return ip.IsPrivate() && !Options.LinodeExternalNetwork.Contains(*ip) -} diff --git a/cloud/linode/loadbalancers.go b/cloud/linode/loadbalancers.go index 1b8e9276..739572d2 100644 --- a/cloud/linode/loadbalancers.go +++ b/cloud/linode/loadbalancers.go @@ -28,7 +28,8 @@ import ( "github.com/linode/linode-cloud-controller-manager/cloud/annotations" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" - "github.com/linode/linode-cloud-controller-manager/cloud/linode/firewall" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" "github.com/linode/linode-cloud-controller-manager/sentry" ) @@ -129,7 +130,7 @@ type portConfig struct { // newLoadbalancers returns a cloudprovider.LoadBalancer whose concrete type is a *loadbalancer. func newLoadbalancers(client client.Client, zone string) cloudprovider.LoadBalancer { - return &loadbalancers{client: client, zone: zone, loadBalancerType: Options.LoadBalancerType} + return &loadbalancers{client: client, zone: zone, loadBalancerType: options.Options.LoadBalancerType} } func (l *loadbalancers) getNodeBalancerForService(ctx context.Context, service *v1.Service) (*linodego.NodeBalancer, error) { @@ -227,7 +228,7 @@ func (l *loadbalancers) cleanupOldNodeBalancer(ctx context.Context, service *v1. // GetLoadBalancer will not modify service. func (l *loadbalancers) GetLoadBalancerName(_ context.Context, _ string, _ *v1.Service) string { unixNano := strconv.FormatInt(time.Now().UnixNano(), 16) - return fmt.Sprintf("%s-%s", Options.NodeBalancerPrefix, unixNano[len(unixNano)-12:]) + return fmt.Sprintf("%s-%s", options.Options.NodeBalancerPrefix, unixNano[len(unixNano)-12:]) } // GetLoadBalancer returns the *v1.LoadBalancerStatus of service. @@ -296,8 +297,8 @@ func (l *loadbalancers) EnsureLoadBalancer(ctx context.Context, clusterName stri } var ipHolderSuffix string - if Options.IpHolderSuffix != "" { - ipHolderSuffix = Options.IpHolderSuffix + if options.Options.IpHolderSuffix != "" { + ipHolderSuffix = options.Options.IpHolderSuffix klog.Infof("using parameter-based IP Holder suffix %s for Service %s", ipHolderSuffix, serviceNn) } @@ -394,7 +395,7 @@ func (l *loadbalancers) updateNodeBalancer( } } - fwClient := firewall.LinodeClient{Client: l.client} + fwClient := services.LinodeClient{Client: l.client} err = fwClient.UpdateNodeBalancerFirewall(ctx, l.GetLoadBalancerName(ctx, clusterName, service), tags, service, nb) if err != nil { return err @@ -451,8 +452,8 @@ func (l *loadbalancers) updateNodeBalancer( // Add all of the Nodes to the config newNBNodes := make([]linodego.NodeBalancerConfigRebuildNodeOptions, 0, len(nodes)) subnetID := 0 - if Options.NodeBalancerBackendIPv4SubnetID != 0 { - subnetID = Options.NodeBalancerBackendIPv4SubnetID + if options.Options.NodeBalancerBackendIPv4SubnetID != 0 { + subnetID = options.Options.NodeBalancerBackendIPv4SubnetID } backendIPv4Range, ok := service.GetAnnotations()[annotations.NodeBalancerBackendIPv4Range] if ok { @@ -460,7 +461,7 @@ func (l *loadbalancers) updateNodeBalancer( return err } } - if len(Options.VPCNames) > 0 && !Options.DisableNodeBalancerVPCBackends { + if len(options.Options.VPCNames) > 0 && !options.Options.DisableNodeBalancerVPCBackends { var id int id, err = l.getSubnetIDForSVC(ctx, service) if err != nil { @@ -530,8 +531,8 @@ func (l *loadbalancers) UpdateLoadBalancer(ctx context.Context, clusterName stri klog.Infof("handling update for LoadBalancer Service %s/%s as %s", service.Namespace, service.Name, ciliumLBClass) serviceNn := getServiceNn(service) var ipHolderSuffix string - if Options.IpHolderSuffix != "" { - ipHolderSuffix = Options.IpHolderSuffix + if options.Options.IpHolderSuffix != "" { + ipHolderSuffix = options.Options.IpHolderSuffix klog.V(3).Infof("using parameter-based IP Holder suffix %s for Service %s", ipHolderSuffix, serviceNn) } @@ -650,7 +651,7 @@ func (l *loadbalancers) EnsureLoadBalancerDeleted(ctx context.Context, clusterNa return nil } - fwClient := firewall.LinodeClient{Client: l.client} + fwClient := services.LinodeClient{Client: l.client} if err = fwClient.DeleteNodeBalancerFirewall(ctx, service, nb); err != nil { return err } @@ -716,7 +717,7 @@ func (l *loadbalancers) GetLoadBalancerTags(_ context.Context, clusterName strin tags = append(tags, clusterName) } - tags = append(tags, Options.NodeBalancerTags...) + tags = append(tags, options.Options.NodeBalancerTags...) tagStr, ok := service.GetAnnotations()[annotations.AnnLinodeLoadBalancerTags] if ok { @@ -733,7 +734,7 @@ func (l *loadbalancers) GetLinodeNBType(service *v1.Service) linodego.NodeBalanc return linodego.NBTypePremium } - return linodego.NodeBalancerPlanType(Options.DefaultNBType) + return linodego.NodeBalancerPlanType(options.Options.DefaultNBType) } // getVPCCreateOptions returns the VPC options for the NodeBalancer creation. @@ -785,10 +786,10 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser // Precedence 3: If the user has specified a NodeBalancerBackendIPv4SubnetID, use that // and auto-allocate subnets from it for the NodeBalancer - if Options.NodeBalancerBackendIPv4SubnetID != 0 { + if options.Options.NodeBalancerBackendIPv4SubnetID != 0 { vpcCreateOpts := []linodego.NodeBalancerVPCOptions{ { - SubnetID: Options.NodeBalancerBackendIPv4SubnetID, + SubnetID: options.Options.NodeBalancerBackendIPv4SubnetID, }, } return vpcCreateOpts, nil @@ -796,11 +797,11 @@ func (l *loadbalancers) getVPCCreateOptions(ctx context.Context, service *v1.Ser // Precedence 4: If the user has specified a NodeBalancerBackendIPv4Subnet, use that // and auto-allocate subnets from it for the NodeBalancer - if Options.NodeBalancerBackendIPv4Subnet != "" { + if options.Options.NodeBalancerBackendIPv4Subnet != "" { vpcCreateOpts := []linodego.NodeBalancerVPCOptions{ { SubnetID: subnetID, - IPv4Range: Options.NodeBalancerBackendIPv4Subnet, + IPv4Range: options.Options.NodeBalancerBackendIPv4Subnet, IPv4RangeAutoAssign: true, }, } @@ -831,7 +832,7 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri Type: nbType, } - if len(Options.VPCNames) > 0 && !Options.DisableNodeBalancerVPCBackends { + if len(options.Options.VPCNames) > 0 && !options.Options.DisableNodeBalancerVPCBackends { createOpts.VPCs, err = l.getVPCCreateOptions(ctx, service) if err != nil { return nil, err @@ -849,7 +850,7 @@ func (l *loadbalancers) createNodeBalancer(ctx context.Context, clusterName stri // There's no firewallID already set, see if we need to create a new fw, look for the acl annotation. _, ok := service.GetAnnotations()[annotations.AnnLinodeCloudFirewallACL] if ok { - fwcreateOpts, err := firewall.CreateFirewallOptsForSvc(label, tags, service) + fwcreateOpts, err := services.CreateFirewallOptsForSvc(label, tags, service) if err != nil { return nil, err } @@ -971,7 +972,7 @@ func (l *loadbalancers) addTLSCert(ctx context.Context, service *v1.Service, nbC // 3. If CCM is configured with --nodebalancer-backend-ipv4-subnet-id, it will be used as the subnet ID. // 4. Else, use first VPCName and SubnetName to calculate subnet id for the service. func (l *loadbalancers) getSubnetIDForSVC(ctx context.Context, service *v1.Service) (int, error) { - if len(Options.VPCNames) == 0 { + if len(options.Options.VPCNames) == 0 { return 0, fmt.Errorf("CCM not configured with VPC, cannot create NodeBalancer with specified annotation") } // Check if the service has an annotation for NodeBalancerBackendSubnetID @@ -988,26 +989,26 @@ func (l *loadbalancers) getSubnetIDForSVC(ctx context.Context, service *v1.Servi // If no VPCName or SubnetName is specified in annotations, but NodeBalancerBackendIPv4SubnetID is set, // use the NodeBalancerBackendIPv4SubnetID as the subnet ID. - if !vpcOk && !subnetOk && Options.NodeBalancerBackendIPv4SubnetID != 0 { - return Options.NodeBalancerBackendIPv4SubnetID, nil + if !vpcOk && !subnetOk && options.Options.NodeBalancerBackendIPv4SubnetID != 0 { + return options.Options.NodeBalancerBackendIPv4SubnetID, nil } - vpcName := Options.VPCNames[0] + vpcName := options.Options.VPCNames[0] if vpcOk { vpcName = specifiedVPCName } - vpcID, err := GetVPCID(ctx, l.client, vpcName) + vpcID, err := services.GetVPCID(ctx, l.client, vpcName) if err != nil { return 0, err } - subnetName := Options.SubnetNames[0] + subnetName := options.Options.SubnetNames[0] if subnetOk { subnetName = specifiedSubnetName } // Use the VPC ID and Subnet Name to get the subnet ID - return GetSubnetID(ctx, l.client, vpcID, subnetName) + return services.GetSubnetID(ctx, l.client, vpcID, subnetName) } // buildLoadBalancerRequest returns a linodego.NodeBalancer @@ -1020,8 +1021,8 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam configs := make([]*linodego.NodeBalancerConfigCreateOptions, 0, len(ports)) subnetID := 0 - if Options.NodeBalancerBackendIPv4SubnetID != 0 { - subnetID = Options.NodeBalancerBackendIPv4SubnetID + if options.Options.NodeBalancerBackendIPv4SubnetID != 0 { + subnetID = options.Options.NodeBalancerBackendIPv4SubnetID } // Check for the NodeBalancerBackendIPv4Range annotation backendIPv4Range, ok := service.GetAnnotations()[annotations.NodeBalancerBackendIPv4Range] @@ -1030,7 +1031,7 @@ func (l *loadbalancers) buildLoadBalancerRequest(ctx context.Context, clusterNam return nil, err } } - if len(Options.VPCNames) > 0 && !Options.DisableNodeBalancerVPCBackends { + if len(options.Options.VPCNames) > 0 && !options.Options.DisableNodeBalancerVPCBackends { id, err := l.getSubnetIDForSVC(ctx, service) if err != nil { return nil, err @@ -1111,7 +1112,7 @@ func (l *loadbalancers) retrieveKubeClient() error { // Check to see if --kubeconfig was set. If it was, build a kubeconfig from the given file. // Otherwise, use the in-cluster config. - kubeconfigFlag := Options.KubeconfigFlag + kubeconfigFlag := options.Options.KubeconfigFlag if kubeconfigFlag == nil || kubeconfigFlag.Value.String() == "" { kubeConfig, err = rest.InClusterConfig() } else { @@ -1304,7 +1305,7 @@ func makeLoadBalancerStatus(service *v1.Service, nb *linodego.NodeBalancer) *v1. } // Check for per-service IPv6 annotation first, then fall back to global setting - useIPv6 := getServiceBoolAnnotation(service, annotations.AnnLinodeEnableIPv6Ingress) || Options.EnableIPv6ForLoadBalancers + useIPv6 := getServiceBoolAnnotation(service, annotations.AnnLinodeEnableIPv6Ingress) || options.Options.EnableIPv6ForLoadBalancers // When IPv6 is enabled (either per-service or globally), include both IPv4 and IPv6 if useIPv6 && nb.IPv6 != nil && *nb.IPv6 != "" { @@ -1357,15 +1358,15 @@ func getServiceBoolAnnotation(service *v1.Service, name string) bool { // validateNodeBalancerBackendIPv4Range validates the NodeBalancerBackendIPv4Range // annotation to be within the NodeBalancerBackendIPv4Subnet if it is set. func validateNodeBalancerBackendIPv4Range(backendIPv4Range string) error { - if Options.NodeBalancerBackendIPv4Subnet == "" { + if options.Options.NodeBalancerBackendIPv4Subnet == "" { return nil } - withinCIDR, err := isCIDRWithinCIDR(Options.NodeBalancerBackendIPv4Subnet, backendIPv4Range) + withinCIDR, err := isCIDRWithinCIDR(options.Options.NodeBalancerBackendIPv4Subnet, backendIPv4Range) if err != nil { return fmt.Errorf("invalid IPv4 range: %w", err) } if !withinCIDR { - return fmt.Errorf("IPv4 range %s is not within the subnet %s", backendIPv4Range, Options.NodeBalancerBackendIPv4Subnet) + return fmt.Errorf("IPv4 range %s is not within the subnet %s", backendIPv4Range, options.Options.NodeBalancerBackendIPv4Subnet) } return nil } diff --git a/cloud/linode/loadbalancers_test.go b/cloud/linode/loadbalancers_test.go index 848c4d12..1c24e3e5 100644 --- a/cloud/linode/loadbalancers_test.go +++ b/cloud/linode/loadbalancers_test.go @@ -30,7 +30,8 @@ import ( "github.com/linode/linode-cloud-controller-manager/cloud/annotations" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" - "github.com/linode/linode-cloud-controller-manager/cloud/linode/firewall" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" ) const testCert string = `-----BEGIN CERTIFICATE----- @@ -510,8 +511,8 @@ func testCreateNodeBalanceWithNoAllowOrDenyList(t *testing.T, client *linodego.C } err := testCreateNodeBalancer(t, client, f, annotations, nil) - if err == nil || !stderrors.Is(err, firewall.ErrInvalidFWConfig) { - t.Fatalf("expected a %v error, got %v", firewall.ErrInvalidFWConfig, err) + if err == nil || !stderrors.Is(err, services.ErrInvalidFWConfig) { + t.Fatalf("expected a %v error, got %v", services.ErrInvalidFWConfig, err) } } @@ -532,8 +533,8 @@ func testCreateNodeBalanceWithBothAllowOrDenyList(t *testing.T, client *linodego } err := testCreateNodeBalancer(t, client, f, annotations, nil) - if err == nil || !stderrors.Is(err, firewall.ErrInvalidFWConfig) { - t.Fatalf("expected a %v error, got %v", firewall.ErrInvalidFWConfig, err) + if err == nil || !stderrors.Is(err, services.ErrInvalidFWConfig) { + t.Fatalf("expected a %v error, got %v", services.ErrInvalidFWConfig, err) } } @@ -601,11 +602,11 @@ func testCreateNodeBalancerWithInvalidFirewall(t *testing.T, client *linodego.Cl func testCreateNodeBalancerWithGlobalTags(t *testing.T, client *linodego.Client, f *fakeAPI) { t.Helper() - original := Options.NodeBalancerTags + original := options.Options.NodeBalancerTags defer func() { - Options.NodeBalancerTags = original + options.Options.NodeBalancerTags = original }() - Options.NodeBalancerTags = []string{"foobar"} + options.Options.NodeBalancerTags = []string{"foobar"} expectedTags := []string{"linodelb", "foobar", "fake", "test", "yolo"} err := testCreateNodeBalancer(t, client, f, nil, expectedTags) if err != nil { @@ -621,14 +622,14 @@ func testCreateNodeBalancerWithVPCBackend(t *testing.T, client *linodego.Client, annotations.NodeBalancerBackendIPv4Range: "10.100.0.0/30", } - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames }() - Options.VPCNames = []string{"test1"} - Options.SubnetNames = []string{defaultSubnet} + options.Options.VPCNames = []string{"test1"} + options.Options.SubnetNames = []string{defaultSubnet} _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test1", Description: "", @@ -649,11 +650,11 @@ func testCreateNodeBalancerWithVPCBackend(t *testing.T, client *linodego.Client, f.ResetRequests() // test with IPv4Range outside of defined NodeBalancer subnet - nodebalancerBackendIPv4Subnet := Options.NodeBalancerBackendIPv4Subnet + nodebalancerBackendIPv4Subnet := options.Options.NodeBalancerBackendIPv4Subnet defer func() { - Options.NodeBalancerBackendIPv4Subnet = nodebalancerBackendIPv4Subnet + options.Options.NodeBalancerBackendIPv4Subnet = nodebalancerBackendIPv4Subnet }() - Options.NodeBalancerBackendIPv4Subnet = "10.99.0.0/24" + options.Options.NodeBalancerBackendIPv4Subnet = "10.99.0.0/24" if err := testCreateNodeBalancer(t, client, f, ann, nil); err == nil { t.Fatalf("expected nodebalancer creation to fail") } @@ -663,14 +664,14 @@ func testUpdateNodeBalancerWithVPCBackend(t *testing.T, client *linodego.Client, t.Helper() // provision vpc and test - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames }() - Options.VPCNames = []string{"test1"} - Options.SubnetNames = []string{defaultSubnet} + options.Options.VPCNames = []string{"test1"} + options.Options.SubnetNames = []string{defaultSubnet} _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test1", Description: "", @@ -749,17 +750,17 @@ func testCreateNodeBalancerWithVPCOnlySubnetFlag(t *testing.T, client *linodego. t.Helper() // provision vpc and test - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames - nbBackendSubnet := Options.NodeBalancerBackendIPv4Subnet + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames + nbBackendSubnet := options.Options.NodeBalancerBackendIPv4Subnet defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames - Options.NodeBalancerBackendIPv4Subnet = nbBackendSubnet + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames + options.Options.NodeBalancerBackendIPv4Subnet = nbBackendSubnet }() - Options.VPCNames = []string{"test-subflag"} - Options.SubnetNames = []string{defaultSubnet} - Options.NodeBalancerBackendIPv4Subnet = "10.254.0.0/24" + options.Options.VPCNames = []string{"test-subflag"} + options.Options.SubnetNames = []string{defaultSubnet} + options.Options.NodeBalancerBackendIPv4Subnet = "10.254.0.0/24" _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test-subflag", Description: "", @@ -845,14 +846,14 @@ func testCreateNodeBalancerWithVPCNoFlagOrAnnotation(t *testing.T, client *linod t.Helper() // provision vpc and test - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames }() - Options.VPCNames = []string{"test-noflags"} - Options.SubnetNames = []string{defaultSubnet} + options.Options.VPCNames = []string{"test-noflags"} + options.Options.SubnetNames = []string{defaultSubnet} _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test-noflags", Description: "", @@ -928,14 +929,14 @@ func testCreateNodeBalancerWithVPCAnnotationOnly(t *testing.T, client *linodego. t.Helper() // provision vpc and test - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames }() - Options.VPCNames = []string{"test-onlyannotation"} - Options.SubnetNames = []string{defaultSubnet} + options.Options.VPCNames = []string{"test-onlyannotation"} + options.Options.SubnetNames = []string{defaultSubnet} _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test-onlyannotation", Description: "", @@ -1016,17 +1017,17 @@ func testCreateNodeBalancerWithVPCOnlySubnetIDFlag(t *testing.T, client *linodeg t.Helper() // provision vpc and test - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames - nbBackendSubnetID := Options.NodeBalancerBackendIPv4SubnetID + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames + nbBackendSubnetID := options.Options.NodeBalancerBackendIPv4SubnetID defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames - Options.NodeBalancerBackendIPv4SubnetID = nbBackendSubnetID + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames + options.Options.NodeBalancerBackendIPv4SubnetID = nbBackendSubnetID }() - Options.VPCNames = []string{"test1"} - Options.SubnetNames = []string{defaultSubnet} - Options.NodeBalancerBackendIPv4SubnetID = 1111 + options.Options.VPCNames = []string{"test1"} + options.Options.SubnetNames = []string{defaultSubnet} + options.Options.NodeBalancerBackendIPv4SubnetID = 1111 _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test-subid-flag", Description: "", @@ -1104,17 +1105,17 @@ func testCreateNodeBalancerWithVPCAnnotationOverwrite(t *testing.T, client *lino t.Helper() // provision multiple vpcs - vpcNames := Options.VPCNames - subnetNames := Options.SubnetNames - nodebalancerBackendIPv4Subnet := Options.NodeBalancerBackendIPv4Subnet + vpcNames := options.Options.VPCNames + subnetNames := options.Options.SubnetNames + nodebalancerBackendIPv4Subnet := options.Options.NodeBalancerBackendIPv4Subnet defer func() { - Options.VPCNames = vpcNames - Options.SubnetNames = subnetNames - Options.NodeBalancerBackendIPv4Subnet = nodebalancerBackendIPv4Subnet + options.Options.VPCNames = vpcNames + options.Options.SubnetNames = subnetNames + options.Options.NodeBalancerBackendIPv4Subnet = nodebalancerBackendIPv4Subnet }() - Options.VPCNames = []string{"test1"} - Options.SubnetNames = []string{defaultSubnet} - Options.NodeBalancerBackendIPv4Subnet = "10.100.0.0/24" + options.Options.VPCNames = []string{"test1"} + options.Options.SubnetNames = []string{defaultSubnet} + options.Options.NodeBalancerBackendIPv4Subnet = "10.100.0.0/24" _, _ = client.CreateVPC(t.Context(), linodego.VPCCreateOptions{ Label: "test1", @@ -1949,7 +1950,7 @@ func testUpdateLoadBalancerAddNewFirewall(t *testing.T, client *linodego.Client, } svc.Status.LoadBalancer = *lbStatus stubServiceUpdate(fakeClientset, svc) - fwClient := firewall.LinodeClient{Client: client} + fwClient := services.LinodeClient{Client: client} fw, err := fwClient.CreateFirewall(t.Context(), linodego.FirewallCreateOptions{ Label: "test", Rules: linodego.FirewallRuleSet{Inbound: []linodego.FirewallRule{{ @@ -2312,7 +2313,7 @@ func testUpdateLoadBalancerUpdateFirewallRemoveACLaddID(t *testing.T, client *li t.Errorf("expected IP, got %v", fwIPs) } - fwClient := firewall.LinodeClient{Client: client} + fwClient := services.LinodeClient{Client: client} fw, err := fwClient.CreateFirewall(t.Context(), linodego.FirewallCreateOptions{ Label: "test", Rules: linodego.FirewallRuleSet{Inbound: []linodego.FirewallRule{{ @@ -2410,7 +2411,7 @@ func testUpdateLoadBalancerUpdateFirewallRemoveIDaddACL(t *testing.T, client *li fakeClientset := fake.NewSimpleClientset() lb.kubeClient = fakeClientset - fwClient := firewall.LinodeClient{Client: client} + fwClient := services.LinodeClient{Client: client} fw, err := fwClient.CreateFirewall(t.Context(), linodego.FirewallCreateOptions{ Label: "test", Rules: linodego.FirewallRuleSet{Inbound: []linodego.FirewallRule{{ @@ -2797,7 +2798,7 @@ func testUpdateLoadBalancerUpdateFirewall(t *testing.T, client *linodego.Client, _ = lb.EnsureLoadBalancerDeleted(t.Context(), "linodelb", svc) }() - fwClient := firewall.LinodeClient{Client: client} + fwClient := services.LinodeClient{Client: client} fw, err := fwClient.CreateFirewall(t.Context(), firewallCreateOpts) if err != nil { t.Errorf("Error creating firewall %s", err) @@ -2930,7 +2931,7 @@ func testUpdateLoadBalancerDeleteFirewallRemoveID(t *testing.T, client *linodego _ = lb.EnsureLoadBalancerDeleted(t.Context(), "linodelb", svc) }() - fwClient := firewall.LinodeClient{Client: client} + fwClient := services.LinodeClient{Client: client} fw, err := fwClient.CreateFirewall(t.Context(), firewallCreateOpts) if err != nil { t.Errorf("Error in creating firewall %s", err) @@ -4369,7 +4370,7 @@ func testMakeLoadBalancerStatusWithIPv6(t *testing.T, client *linodego.Client, _ } // Test with EnableIPv6ForLoadBalancers = false (default) - Options.EnableIPv6ForLoadBalancers = false + options.Options.EnableIPv6ForLoadBalancers = false expectedStatus := &v1.LoadBalancerStatus{ Ingress: []v1.LoadBalancerIngress{{ Hostname: hostname, @@ -4382,7 +4383,7 @@ func testMakeLoadBalancerStatusWithIPv6(t *testing.T, client *linodego.Client, _ } // Test with EnableIPv6ForLoadBalancers = true - Options.EnableIPv6ForLoadBalancers = true + options.Options.EnableIPv6ForLoadBalancers = true expectedStatus = &v1.LoadBalancerStatus{ Ingress: []v1.LoadBalancerIngress{ { @@ -4402,7 +4403,7 @@ func testMakeLoadBalancerStatusWithIPv6(t *testing.T, client *linodego.Client, _ // Test with per-service annotation // Reset the global flag to false and set the annotation - Options.EnableIPv6ForLoadBalancers = false + options.Options.EnableIPv6ForLoadBalancers = false svc.Annotations[annotations.AnnLinodeEnableIPv6Ingress] = "true" // Expect the same result as when the global flag is enabled @@ -4413,7 +4414,7 @@ func testMakeLoadBalancerStatusWithIPv6(t *testing.T, client *linodego.Client, _ } // Reset the flag to its default value - Options.EnableIPv6ForLoadBalancers = false + options.Options.EnableIPv6ForLoadBalancers = false } func testMakeLoadBalancerStatusEnvVar(t *testing.T, client *linodego.Client, _ *fakeAPI) { @@ -5499,7 +5500,7 @@ func Test_loadbalancers_GetLinodeNBType(t *testing.T) { ciliumClient: tt.fields.ciliumClient, loadBalancerType: tt.fields.loadBalancerType, } - Options.DefaultNBType = string(tt.defaultNB) + options.Options.DefaultNBType = string(tt.defaultNB) if got := l.GetLinodeNBType(tt.args.service); !reflect.DeepEqual(got, tt.want) { t.Errorf("loadbalancers.GetLinodeNBType() = %v, want %v", got, tt.want) } @@ -5528,11 +5529,11 @@ func Test_validateNodeBalancerBackendIPv4Range(t *testing.T) { }, } - nbBackendSubnet := Options.NodeBalancerBackendIPv4Subnet + nbBackendSubnet := options.Options.NodeBalancerBackendIPv4Subnet defer func() { - Options.NodeBalancerBackendIPv4Subnet = nbBackendSubnet + options.Options.NodeBalancerBackendIPv4Subnet = nbBackendSubnet }() - Options.NodeBalancerBackendIPv4Subnet = "10.100.0.0/24" + options.Options.NodeBalancerBackendIPv4Subnet = "10.100.0.0/24" for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cloud/linode/node_controller.go b/cloud/linode/node_controller.go index 8cbb7473..4a5326f7 100644 --- a/cloud/linode/node_controller.go +++ b/cloud/linode/node_controller.go @@ -22,6 +22,9 @@ import ( "github.com/linode/linode-cloud-controller-manager/cloud/annotations" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" ) const ( @@ -42,7 +45,7 @@ type nodeController struct { sync.RWMutex client client.Client - instances *instances + instances *services.Instances kubeclient kubernetes.Interface informer v1informers.NodeInformer @@ -154,7 +157,7 @@ func newK8sNodeCache() *k8sNodeCache { } } -func newNodeController(kubeclient kubernetes.Interface, client client.Client, informer v1informers.NodeInformer, instanceCache *instances) *nodeController { +func newNodeController(kubeclient kubernetes.Interface, client client.Client, informer v1informers.NodeInformer, instanceCache *services.Instances) *nodeController { timeout := defaultMetadataTTL if raw, ok := os.LookupEnv("LINODE_METADATA_TTL"); ok { if t, err := strconv.Atoi(raw); t > 0 && err == nil { @@ -283,7 +286,7 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { return nil } - linode, err := s.instances.lookupLinode(ctx, node) + linode, err := s.instances.LookupLinode(ctx, node) if err != nil { klog.V(1).ErrorS(err, "Instance lookup error") return err @@ -293,7 +296,7 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { // linode API response for linode will contain only one private ip // if any private ip is configured. for _, addr := range linode.IPv4 { - if isPrivate(addr) { + if ccmUtils.IsPrivate(addr, options.Options.LinodeExternalNetwork) { expectedPrivateIP = addr.String() break } @@ -320,7 +323,7 @@ func (s *nodeController) handleNode(ctx context.Context, node *v1.Node) error { // Try to update the node ProviderID if it has not been set if nodeResult.Spec.ProviderID == "" { - nodeResult.Spec.ProviderID = providerIDPrefix + strconv.Itoa(linode.ID) + nodeResult.Spec.ProviderID = ccmUtils.ProviderIDPrefix + strconv.Itoa(linode.ID) } // Try to update the expectedPrivateIP if its not set or doesn't match diff --git a/cloud/linode/node_controller_test.go b/cloud/linode/node_controller_test.go index af1634b7..2e1082b9 100644 --- a/cloud/linode/node_controller_test.go +++ b/cloud/linode/node_controller_test.go @@ -19,6 +19,7 @@ import ( "github.com/linode/linode-cloud-controller-manager/cloud/annotations" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" ) func TestNodeController_Run(t *testing.T) { @@ -30,7 +31,7 @@ func TestNodeController_Run(t *testing.T) { informer := informers.NewSharedInformerFactory(kubeClient, 0).Core().V1().Nodes() mockQueue := workqueue.NewTypedDelayingQueueWithConfig(workqueue.TypedDelayingQueueConfig[nodeRequest]{Name: "test"}) - nodeCtrl := newNodeController(kubeClient, client, informer, newInstances(client)) + nodeCtrl := newNodeController(kubeClient, client, informer, services.NewInstances(client)) nodeCtrl.queue = mockQueue nodeCtrl.ttl = 1 * time.Second @@ -83,7 +84,7 @@ func TestNodeController_processNext(t *testing.T) { controller := &nodeController{ kubeclient: kubeClient, - instances: newInstances(client), + instances: services.NewInstances(client), queue: queue, metadataLastUpdate: make(map[string]time.Time), ttl: defaultMetadataTTL, @@ -137,7 +138,7 @@ func TestNodeController_processNext(t *testing.T) { defer func() { controller.instances = currInstances }() - controller.instances = newInstances(client) + controller.instances = services.NewInstances(client) registeredK8sNodeCache.lastUpdate = time.Now().Add(-15 * time.Minute) controller.addNodeToQueue(node2) publicIP := net.ParseIP("172.234.31.123") @@ -168,7 +169,7 @@ func TestNodeController_processNext(t *testing.T) { controller.queue = queue controller.addNodeToQueue(node) client := mocks.NewMockClient(ctrl) - controller.instances = newInstances(client) + controller.instances = services.NewInstances(client) retryInterval = 1 * time.Nanosecond client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, &linodego.Error{Code: http.StatusTooManyRequests, Message: "Too many requests"}) result := controller.processNext() @@ -184,7 +185,7 @@ func TestNodeController_processNext(t *testing.T) { controller.queue = queue controller.addNodeToQueue(node) client := mocks.NewMockClient(ctrl) - controller.instances = newInstances(client) + controller.instances = services.NewInstances(client) retryInterval = 1 * time.Nanosecond client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, &linodego.Error{Code: http.StatusInternalServerError, Message: "Too many requests"}) result := controller.processNext() @@ -213,7 +214,7 @@ func TestNodeController_handleNode(t *testing.T) { _, err := kubeClient.CoreV1().Nodes().Create(t.Context(), node, metav1.CreateOptions{}) require.NoError(t, err, "expected no error during node creation") - instCache := newInstances(client) + instCache := services.NewInstances(client) t.Setenv("LINODE_METADATA_TTL", "30") nodeCtrl := newNodeController(kubeClient, client, nil, instCache) @@ -248,7 +249,7 @@ func TestNodeController_handleNode(t *testing.T) { // Lookup failure for linode instance client = mocks.NewMockClient(ctrl) - nodeCtrl.instances = newInstances(client) + nodeCtrl.instances = services.NewInstances(client) nodeCtrl.metadataLastUpdate["test-node"] = time.Now().Add(-2 * nodeCtrl.ttl) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, errors.New("lookup failed")) err = nodeCtrl.handleNode(t.Context(), node) @@ -256,7 +257,7 @@ func TestNodeController_handleNode(t *testing.T) { // All fields already set client = mocks.NewMockClient(ctrl) - nodeCtrl.instances = newInstances(client) + nodeCtrl.instances = services.NewInstances(client) nodeCtrl.metadataLastUpdate["test-node"] = time.Now().Add(-2 * nodeCtrl.ttl) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: 123, Label: "test-node", IPv4: []*net.IP{&publicIP, &privateIP}, IPv6: publicIPv6SLAAC, HostUUID: "123"}, diff --git a/cloud/linode/nodeipamcontroller.go b/cloud/linode/nodeipamcontroller.go index 1957e8c2..278e4ce5 100644 --- a/cloud/linode/nodeipamcontroller.go +++ b/cloud/linode/nodeipamcontroller.go @@ -29,6 +29,7 @@ import ( "k8s.io/client-go/kubernetes" netutils "k8s.io/utils/net" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" nodeipamcontroller "github.com/linode/linode-cloud-controller-manager/cloud/nodeipam" "github.com/linode/linode-cloud-controller-manager/cloud/nodeipam/ipam" ) @@ -49,12 +50,12 @@ func startNodeIpamController(stopCh <-chan struct{}, cloud *linodeCloud, nodeInf var secondaryServiceCIDR *net.IPNet // should we start nodeIPAM - if !Options.AllocateNodeCIDRs { + if !options.Options.AllocateNodeCIDRs { return nil } // failure: bad cidrs in config - clusterCIDRs, err := processCIDRs(Options.ClusterCIDRIPv4) + clusterCIDRs, err := processCIDRs(options.Options.ClusterCIDRIPv4) if err != nil { return fmt.Errorf("processCIDRs failed: %w", err) } @@ -86,7 +87,7 @@ func startNodeIpamController(stopCh <-chan struct{}, cloud *linodeCloud, nodeInf secondaryServiceCIDR, nodeCIDRMaskSizes, ipam.CloudAllocatorType, - Options.DisableIPv6NodeCIDRAllocation, + options.Options.DisableIPv6NodeCIDRAllocation, ) if err != nil { return err @@ -110,11 +111,11 @@ func processCIDRs(cidrsList string) ([]*net.IPNet, error) { } func setNodeCIDRMaskSizes() []int { - if Options.NodeCIDRMaskSizeIPv4 != 0 { - defaultNodeMaskCIDRIPv4 = Options.NodeCIDRMaskSizeIPv4 + if options.Options.NodeCIDRMaskSizeIPv4 != 0 { + defaultNodeMaskCIDRIPv4 = options.Options.NodeCIDRMaskSizeIPv4 } - if Options.NodeCIDRMaskSizeIPv6 != 0 { - defaultNodeMaskCIDRIPv6 = Options.NodeCIDRMaskSizeIPv6 + if options.Options.NodeCIDRMaskSizeIPv6 != 0 { + defaultNodeMaskCIDRIPv6 = options.Options.NodeCIDRMaskSizeIPv6 } return []int{defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6} } diff --git a/cloud/linode/nodeipamcontroller_test.go b/cloud/linode/nodeipamcontroller_test.go index d81b7a50..16a65e9a 100644 --- a/cloud/linode/nodeipamcontroller_test.go +++ b/cloud/linode/nodeipamcontroller_test.go @@ -28,6 +28,8 @@ import ( v1 "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" + + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" ) func Test_setNodeCIDRMaskSizes(t *testing.T) { @@ -56,17 +58,17 @@ func Test_setNodeCIDRMaskSizes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - oldCIDRMaskSizeIPv4 := Options.NodeCIDRMaskSizeIPv4 - oldCIDRMaskSizeIPv6 := Options.NodeCIDRMaskSizeIPv6 + oldCIDRMaskSizeIPv4 := options.Options.NodeCIDRMaskSizeIPv4 + oldCIDRMaskSizeIPv6 := options.Options.NodeCIDRMaskSizeIPv6 defer func() { - Options.NodeCIDRMaskSizeIPv4 = oldCIDRMaskSizeIPv4 - Options.NodeCIDRMaskSizeIPv6 = oldCIDRMaskSizeIPv6 + options.Options.NodeCIDRMaskSizeIPv4 = oldCIDRMaskSizeIPv4 + options.Options.NodeCIDRMaskSizeIPv6 = oldCIDRMaskSizeIPv6 }() if tt.args.ipv4NetMask != 0 { - Options.NodeCIDRMaskSizeIPv4 = tt.args.ipv4NetMask + options.Options.NodeCIDRMaskSizeIPv4 = tt.args.ipv4NetMask } if tt.args.ipv6NetMask != 0 { - Options.NodeCIDRMaskSizeIPv6 = tt.args.ipv6NetMask + options.Options.NodeCIDRMaskSizeIPv6 = tt.args.ipv6NetMask } got := setNodeCIDRMaskSizes() if !reflect.DeepEqual(got, tt.want) { @@ -212,14 +214,14 @@ func Test_startNodeIpamController(t *testing.T) { }, } for _, tt := range tests { - currAllocateNodeCIDRs := Options.AllocateNodeCIDRs - currClusterCIDR := Options.ClusterCIDRIPv4 + currAllocateNodeCIDRs := options.Options.AllocateNodeCIDRs + currClusterCIDR := options.Options.ClusterCIDRIPv4 defer func() { - Options.AllocateNodeCIDRs = currAllocateNodeCIDRs - Options.ClusterCIDRIPv4 = currClusterCIDR + options.Options.AllocateNodeCIDRs = currAllocateNodeCIDRs + options.Options.ClusterCIDRIPv4 = currClusterCIDR }() - Options.AllocateNodeCIDRs = tt.args.allocateNodeCIDRs - Options.ClusterCIDRIPv4 = tt.args.clusterCIDR + options.Options.AllocateNodeCIDRs = tt.args.allocateNodeCIDRs + options.Options.ClusterCIDRIPv4 = tt.args.clusterCIDR t.Run(tt.name, func(t *testing.T) { if err := startNodeIpamController(tt.args.stopCh, &tt.args.cloud, tt.args.nodeInformer, tt.args.kubeclient); (err != nil) != tt.wantErr { t.Errorf("startNodeIpamController() error = %v, wantErr %v", err, tt.wantErr) diff --git a/cloud/linode/options/options.go b/cloud/linode/options/options.go new file mode 100644 index 00000000..9a72d5f2 --- /dev/null +++ b/cloud/linode/options/options.go @@ -0,0 +1,39 @@ +package options + +import ( + "net" + + "github.com/spf13/pflag" +) + +// Options is a configuration object for this cloudprovider implementation. +// We expect it to be initialized with flags external to this package, likely in +// main.go +var Options struct { + KubeconfigFlag *pflag.Flag + LinodeGoDebug bool + EnableRouteController bool + EnableTokenHealthChecker bool + VPCNames []string + VPCIDs []int + SubnetNames []string + SubnetIDs []int + LoadBalancerType string + BGPNodeSelector string + IpHolderSuffix string + LinodeExternalNetwork *net.IPNet + NodeBalancerTags []string + DefaultNBType string + NodeBalancerBackendIPv4Subnet string + NodeBalancerBackendIPv4SubnetID int + NodeBalancerBackendIPv4SubnetName string + DisableNodeBalancerVPCBackends bool + GlobalStopChannel chan<- struct{} + EnableIPv6ForLoadBalancers bool + AllocateNodeCIDRs bool + DisableIPv6NodeCIDRAllocation bool + ClusterCIDRIPv4 string + NodeCIDRMaskSizeIPv4 int + NodeCIDRMaskSizeIPv6 int + NodeBalancerPrefix string +} diff --git a/cloud/linode/route_controller.go b/cloud/linode/route_controller.go index b02ac55b..7bf6f0d4 100644 --- a/cloud/linode/route_controller.go +++ b/cloud/linode/route_controller.go @@ -18,6 +18,9 @@ import ( "k8s.io/klog/v2" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" ) var ipv6ConfiguredRoutes []*cloudprovider.Route @@ -39,12 +42,12 @@ func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) { } vpcNodes := map[int][]linodego.VPCIP{} - for _, v := range Options.VPCNames { + for _, v := range options.Options.VPCNames { vpcName := strings.TrimSpace(v) if vpcName == "" { continue } - resp, err := GetVPCIPAddresses(ctx, client, vpcName) + resp, err := services.GetVPCIPAddresses(ctx, client, vpcName) if err != nil { klog.Errorf("failed updating cache for VPC %s. Error: %s", vpcName, err.Error()) continue @@ -60,11 +63,11 @@ func (rc *routeCache) refreshRoutes(ctx context.Context, client client.Client) { type routes struct { client client.Client - instances *instances + instances *services.Instances routeCache *routeCache } -func newRoutes(client client.Client, instanceCache *instances) (cloudprovider.Routes, error) { +func newRoutes(client client.Client, instanceCache *services.Instances) (cloudprovider.Routes, error) { timeout := 60 if raw, ok := os.LookupEnv("LINODE_ROUTES_CACHE_TTL_SECONDS"); ok { if t, err := strconv.Atoi(raw); t > 0 && err == nil { @@ -73,7 +76,7 @@ func newRoutes(client client.Client, instanceCache *instances) (cloudprovider.Ro } klog.V(3).Infof("TTL for routeCache set to %d seconds", timeout) - if Options.EnableRouteController && len(Options.VPCNames) == 0 { + if options.Options.EnableRouteController && len(options.Options.VPCNames) == 0 { return nil, fmt.Errorf("cannot enable route controller as vpc-names is empty") } @@ -120,7 +123,7 @@ func (r *routes) getInstanceFromName(ctx context.Context, name string) (*linodeg } // fetch instance with specified node name - instance, err := r.instances.lookupLinode(ctx, node) + instance, err := r.instances.LookupLinode(ctx, node) if err != nil { klog.Errorf("failed getting linode %s", name) return nil, err @@ -156,7 +159,7 @@ func (r *routes) CreateRoute(ctx context.Context, clusterName string, nameHint s intfRoutes := []string{} intfVPCIP := linodego.VPCIP{} - for _, vpcid := range GetAllVPCIDs() { + for _, vpcid := range services.GetAllVPCIDs() { for _, ir := range instanceRoutes { if ir.VPCID != vpcid { continue @@ -209,7 +212,7 @@ func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *clo intfRoutes := []string{} intfVPCIP := linodego.VPCIP{} - for _, vpcid := range GetAllVPCIDs() { + for _, vpcid := range services.GetAllVPCIDs() { for _, ir := range instanceRoutes { if ir.VPCID != vpcid { continue @@ -246,14 +249,14 @@ func (r *routes) DeleteRoute(ctx context.Context, clusterName string, route *clo // 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) + instances, err := r.instances.ListAllInstances(ctx) if err != nil { return nil, err } var configuredRoutes []*cloudprovider.Route for _, instance := range instances { - providerID := providerIDPrefix + strconv.Itoa(instance.ID) + providerID := ccmUtils.ProviderIDPrefix + strconv.Itoa(instance.ID) label, found := registeredK8sNodeCache.getNodeLabel(providerID, instance.Label) if !found { klog.V(4).Infof("Node %s not found in k8s node cache, skipping listing its routes", instance.Label) @@ -269,7 +272,7 @@ func (r *routes) ListRoutes(ctx context.Context, clusterName string) ([]*cloudpr } // check for configured routes - for _, vpcid := range GetAllVPCIDs() { + for _, vpcid := range services.GetAllVPCIDs() { for _, ir := range instanceRoutes { if ir.Address != nil || ir.VPCID != vpcid { continue diff --git a/cloud/linode/route_controller_test.go b/cloud/linode/route_controller_test.go index 83073ed2..37548b14 100644 --- a/cloud/linode/route_controller_test.go +++ b/cloud/linode/route_controller_test.go @@ -16,15 +16,18 @@ import ( "k8s.io/utils/ptr" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/services" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" ) func TestListRoutes(t *testing.T) { - Options.VPCNames = []string{"test", "abc"} - vpcIDs["test"] = 1 - vpcIDs["abc"] = 2 - Options.SubnetNames = []string{"default"} - subnetIDs["default"] = 1 - Options.EnableRouteController = true + options.Options.VPCNames = []string{"test", "abc"} + services.VpcIDs["test"] = 1 + services.VpcIDs["abc"] = 2 + options.Options.SubnetNames = []string{"default"} + services.SubnetIDs["default"] = 1 + options.Options.EnableRouteController = true nodeID := 123 name := "mock-instance" @@ -34,7 +37,7 @@ func TestListRoutes(t *testing.T) { Labels: map[string]string{}, Annotations: map[string]string{}, }, - Spec: v1.NodeSpec{ProviderID: providerIDPrefix + strconv.Itoa(nodeID)}, + Spec: v1.NodeSpec{ProviderID: ccmUtils.ProviderIDPrefix + strconv.Itoa(nodeID)}, } publicIPv4 := net.ParseIP("45.76.101.25") privateIPv4 := net.ParseIP("192.168.133.65") @@ -46,7 +49,7 @@ func TestListRoutes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -71,7 +74,7 @@ func TestListRoutes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -88,7 +91,7 @@ func TestListRoutes(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["test"], + VPCID: services.VpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, @@ -99,7 +102,7 @@ func TestListRoutes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -117,21 +120,21 @@ func TestListRoutes(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["test"], + VPCID: services.VpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcIDs["test"], + VPCID: services.VpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange2, - VPCID: vpcIDs["test"], + VPCID: services.VpcIDs["test"], NAT1To1: nil, LinodeID: nodeID, }, @@ -142,7 +145,7 @@ func TestListRoutes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) existingK8sCache := registeredK8sNodeCache defer func() { registeredK8sNodeCache = existingK8sCache @@ -191,7 +194,7 @@ func TestListRoutes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) existingK8sCache := registeredK8sNodeCache defer func() { registeredK8sNodeCache = existingK8sCache @@ -214,7 +217,7 @@ func TestListRoutes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) existingK8sCache := registeredK8sNodeCache defer func() { registeredK8sNodeCache = existingK8sCache @@ -252,35 +255,35 @@ func TestListRoutes(t *testing.T) { { Address: &vpcIP2, AddressRange: nil, - VPCID: vpcIDs["abc"], + VPCID: services.VpcIDs["abc"], NAT1To1: nil, LinodeID: instance2ID, }, { Address: nil, AddressRange: &addressRange3, - VPCID: vpcIDs["abc"], + VPCID: services.VpcIDs["abc"], NAT1To1: nil, LinodeID: instance2ID, }, { Address: nil, AddressRange: &addressRange4, - VPCID: vpcIDs["abc"], + VPCID: services.VpcIDs["abc"], NAT1To1: nil, LinodeID: instance2ID, }, { Address: &vpcIP2, AddressRange: nil, - VPCID: vpcIDs["abc"], + VPCID: services.VpcIDs["abc"], NAT1To1: nil, LinodeID: instance3ID, }, { Address: nil, AddressRange: &addressRange5, - VPCID: vpcIDs["abc"], + VPCID: services.VpcIDs["abc"], NAT1To1: nil, LinodeID: instance3ID, }, @@ -292,7 +295,7 @@ func TestListRoutes(t *testing.T) { Labels: map[string]string{}, Annotations: map[string]string{}, }, - Spec: v1.NodeSpec{ProviderID: providerIDPrefix + strconv.Itoa(instance2ID)}, + Spec: v1.NodeSpec{ProviderID: ccmUtils.ProviderIDPrefix + strconv.Itoa(instance2ID)}, } registeredK8sNodeCache.addNodeToCache(node2) @@ -320,9 +323,9 @@ func TestListRoutes(t *testing.T) { func TestCreateRoute(t *testing.T) { ctx := t.Context() - Options.VPCNames = []string{"dummy"} - vpcIDs["dummy"] = 1 - Options.EnableRouteController = true + options.Options.VPCNames = []string{"dummy"} + services.VpcIDs["dummy"] = 1 + options.Options.EnableRouteController = true nodeID := 123 name := "mock-instance" @@ -336,7 +339,7 @@ func TestCreateRoute(t *testing.T) { Labels: map[string]string{}, Annotations: map[string]string{}, }, - Spec: v1.NodeSpec{ProviderID: providerIDPrefix + strconv.Itoa(nodeID)}, + Spec: v1.NodeSpec{ProviderID: ccmUtils.ProviderIDPrefix + strconv.Itoa(nodeID)}, } validInstance := linodego.Instance{ ID: nodeID, @@ -351,14 +354,14 @@ func TestCreateRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["dummy"], + VPCID: services.VpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, } instanceConfigIntfWithVPCAndRoute := linodego.InstanceConfigInterface{ - VPCID: ptr.To(vpcIDs["dummy"]), + VPCID: ptr.To(services.VpcIDs["dummy"]), IPv4: &linodego.VPCIPv4{VPC: vpcIP}, IPRanges: []string{"10.10.10.0/24"}, } @@ -372,7 +375,7 @@ func TestCreateRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) existingK8sCache := registeredK8sNodeCache defer func() { registeredK8sNodeCache = existingK8sCache @@ -399,7 +402,7 @@ func TestCreateRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -416,7 +419,7 @@ func TestCreateRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -429,14 +432,14 @@ func TestCreateRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["dummy"], + VPCID: services.VpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcIDs["dummy"], + VPCID: services.VpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, @@ -446,7 +449,7 @@ func TestCreateRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -461,7 +464,7 @@ func TestCreateRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -474,9 +477,9 @@ func TestCreateRoute(t *testing.T) { } func TestDeleteRoute(t *testing.T) { - Options.VPCNames = []string{"dummy"} - vpcIDs["dummy"] = 1 - Options.EnableRouteController = true + options.Options.VPCNames = []string{"dummy"} + services.VpcIDs["dummy"] = 1 + options.Options.EnableRouteController = true ctx := t.Context() @@ -505,7 +508,7 @@ func TestDeleteRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -521,14 +524,14 @@ func TestDeleteRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["dummy"], + VPCID: services.VpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, } instanceConfigIntfWithVPCAndNoRoute := linodego.InstanceConfigInterface{ - VPCID: ptr.To(vpcIDs["dummy"]), + VPCID: ptr.To(services.VpcIDs["dummy"]), IPv4: &linodego.VPCIPv4{VPC: vpcIP}, IPRanges: []string{}, } @@ -537,7 +540,7 @@ func TestDeleteRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) @@ -553,14 +556,14 @@ func TestDeleteRoute(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["dummy"], + VPCID: services.VpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcIDs["dummy"], + VPCID: services.VpcIDs["dummy"], NAT1To1: nil, LinodeID: nodeID, }, @@ -570,7 +573,7 @@ func TestDeleteRoute(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - instanceCache := newInstances(client) + instanceCache := services.NewInstances(client) routeController, err := newRoutes(client, instanceCache) require.NoError(t, err) diff --git a/cloud/linode/service_controller_test.go b/cloud/linode/service_controller_test.go index bcb8c340..3a99dd9c 100644 --- a/cloud/linode/service_controller_test.go +++ b/cloud/linode/service_controller_test.go @@ -13,6 +13,7 @@ import ( "k8s.io/client-go/util/workqueue" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" ) func Test_serviceController_Run(t *testing.T) { @@ -70,7 +71,7 @@ func Test_serviceController_processNextDeletion(t *testing.T) { loadbalancers: nil, }, Setup: func(f *fields) { - f.loadbalancers = &loadbalancers{client: f.Client, zone: "test", loadBalancerType: Options.LoadBalancerType} + f.loadbalancers = &loadbalancers{client: f.Client, zone: "test", loadBalancerType: options.Options.LoadBalancerType} f.queue = workqueue.NewTypedDelayingQueueWithConfig(workqueue.TypedDelayingQueueConfig[any]{Name: "testQueue"}) f.queue.Add("test") }, @@ -83,7 +84,7 @@ func Test_serviceController_processNextDeletion(t *testing.T) { loadbalancers: nil, }, Setup: func(f *fields) { - f.loadbalancers = &loadbalancers{client: f.Client, zone: "test", loadBalancerType: Options.LoadBalancerType} + f.loadbalancers = &loadbalancers{client: f.Client, zone: "test", loadBalancerType: options.Options.LoadBalancerType} f.queue = workqueue.NewTypedDelayingQueueWithConfig(workqueue.TypedDelayingQueueConfig[any]{Name: "testQueue"}) svc := createTestService() f.queue.Add(svc) diff --git a/cloud/linode/firewall/firewalls.go b/cloud/linode/services/firewalls.go similarity index 99% rename from cloud/linode/firewall/firewalls.go rename to cloud/linode/services/firewalls.go index 100b3390..9cb45b0c 100644 --- a/cloud/linode/firewall/firewalls.go +++ b/cloud/linode/services/firewalls.go @@ -1,4 +1,4 @@ -package firewall +package services import ( "context" diff --git a/cloud/linode/firewall/firewalls_test.go b/cloud/linode/services/firewalls_test.go similarity index 99% rename from cloud/linode/firewall/firewalls_test.go rename to cloud/linode/services/firewalls_test.go index f65cb497..ce3acc90 100644 --- a/cloud/linode/firewall/firewalls_test.go +++ b/cloud/linode/services/firewalls_test.go @@ -1,4 +1,4 @@ -package firewall +package services import ( "reflect" diff --git a/cloud/linode/instances.go b/cloud/linode/services/instances.go similarity index 83% rename from cloud/linode/instances.go rename to cloud/linode/services/instances.go index 1a44a5a6..4faccc06 100644 --- a/cloud/linode/instances.go +++ b/cloud/linode/services/instances.go @@ -1,4 +1,4 @@ -package linode +package services import ( "context" @@ -18,6 +18,8 @@ import ( "k8s.io/klog/v2" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" "github.com/linode/linode-cloud-controller-manager/sentry" ) @@ -60,7 +62,7 @@ func (nc *nodeCache) getInstanceAddresses(instance linodego.Instance, vpcips []s for _, ip := range instance.IPv4 { ipType := v1.NodeExternalIP - if isPrivate(ip) { + if ccmUtils.IsPrivate(ip, options.Options.LinodeExternalNetwork) { ipType = v1.NodeInternalIP } ips = append(ips, nodeIP{ip: ip.String(), ipType: ipType}) @@ -94,7 +96,7 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) // If running within VPC, find instances and store their ips vpcNodes := map[int][]string{} vpcIPv6AddrTypes := map[string]v1.NodeAddressType{} - for _, name := range Options.VPCNames { + for _, name := range options.Options.VPCNames { vpcName := strings.TrimSpace(name) if vpcName == "" { continue @@ -134,7 +136,7 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) newNodes := make(map[int]linodeInstance, len(instances)) for index, instance := range instances { // if running within VPC, only store instances in cache which are part of VPC - if len(Options.VPCNames) > 0 && len(vpcNodes[instance.ID]) == 0 { + if len(options.Options.VPCNames) > 0 && len(vpcNodes[instance.ID]) == 0 { continue } node := linodeInstance{ @@ -149,13 +151,14 @@ func (nc *nodeCache) refreshInstances(ctx context.Context, client client.Client) return nil } -type instances struct { +type Instances struct { client client.Client nodeCache *nodeCache } -func newInstances(client client.Client) *instances { +// NewInstances creates a new Instances cache with a specified TTL for the nodeCache. +func NewInstances(client client.Client) *Instances { timeout := 15 if raw, ok := os.LookupEnv("LINODE_INSTANCE_CACHE_TTL"); ok { if t, err := strconv.Atoi(raw); t > 0 && err == nil { @@ -164,7 +167,7 @@ func newInstances(client client.Client) *instances { } klog.V(3).Infof("TTL for nodeCache set to %d", timeout) - return &instances{client, &nodeCache{ + return &Instances{client, &nodeCache{ nodes: make(map[int]linodeInstance, 0), ttl: time.Duration(timeout) * time.Second, }} @@ -178,7 +181,7 @@ func (e instanceNoIPAddressesError) Error() string { return fmt.Sprintf("instance %d has no IP addresses", e.id) } -func (i *instances) linodeByIP(kNode *v1.Node) (*linodego.Instance, error) { +func (i *Instances) linodeByIP(kNode *v1.Node) (*linodego.Instance, error) { i.nodeCache.RLock() defer i.nodeCache.RUnlock() var kNodeAddresses []string @@ -192,7 +195,7 @@ func (i *instances) linodeByIP(kNode *v1.Node) (*linodego.Instance, error) { } for _, node := range i.nodeCache.nodes { for _, nodeIP := range node.instance.IPv4 { - if !isPrivate(nodeIP) && slices.Contains(kNodeAddresses, nodeIP.String()) { + if !ccmUtils.IsPrivate(nodeIP, options.Options.LinodeExternalNetwork) && slices.Contains(kNodeAddresses, nodeIP.String()) { return node.instance, nil } } @@ -201,7 +204,7 @@ func (i *instances) linodeByIP(kNode *v1.Node) (*linodego.Instance, error) { return nil, cloudprovider.InstanceNotFound } -func (i *instances) linodeByName(nodeName types.NodeName) *linodego.Instance { +func (i *Instances) linodeByName(nodeName types.NodeName) *linodego.Instance { i.nodeCache.RLock() defer i.nodeCache.RUnlock() for _, node := range i.nodeCache.nodes { @@ -213,7 +216,7 @@ func (i *instances) linodeByName(nodeName types.NodeName) *linodego.Instance { return nil } -func (i *instances) linodeByID(id int) (*linodego.Instance, error) { +func (i *Instances) linodeByID(id int) (*linodego.Instance, error) { i.nodeCache.RLock() defer i.nodeCache.RUnlock() linodeInstance, ok := i.nodeCache.nodes[id] @@ -223,8 +226,8 @@ func (i *instances) linodeByID(id int) (*linodego.Instance, error) { return linodeInstance.instance, nil } -// listAllInstances returns all instances in nodeCache -func (i *instances) listAllInstances(ctx context.Context) ([]linodego.Instance, error) { +// 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 } @@ -236,7 +239,8 @@ func (i *instances) listAllInstances(ctx context.Context) ([]linodego.Instance, return instances, nil } -func (i *instances) lookupLinode(ctx context.Context, node *v1.Node) (*linodego.Instance, error) { +// LookupLinode looks up a Linode instance by its ProviderID or NodeName. +func (i *Instances) LookupLinode(ctx context.Context, node *v1.Node) (*linodego.Instance, error) { if err := i.nodeCache.refreshInstances(ctx, i.client); err != nil { return nil, err } @@ -247,8 +251,8 @@ func (i *instances) lookupLinode(ctx context.Context, node *v1.Node) (*linodego. sentry.SetTag(ctx, "provider_id", providerID) sentry.SetTag(ctx, "node_name", node.Name) - if providerID != "" && isLinodeProviderID(providerID) { - id, err := parseProviderID(providerID) + if providerID != "" && ccmUtils.IsLinodeProviderID(providerID) { + id, err := ccmUtils.ParseProviderID(providerID) if err != nil { sentry.CaptureError(ctx, err) return nil, err @@ -265,9 +269,9 @@ func (i *instances) lookupLinode(ctx context.Context, node *v1.Node) (*linodego. return i.linodeByIP(node) } -func (i *instances) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) { +func (i *Instances) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) { ctx = sentry.SetHubOnContext(ctx) - if _, err := i.lookupLinode(ctx, node); err != nil { + if _, err := i.LookupLinode(ctx, node); err != nil { if errors.Is(err, cloudprovider.InstanceNotFound) { return false, nil } @@ -278,9 +282,9 @@ func (i *instances) InstanceExists(ctx context.Context, node *v1.Node) (bool, er return true, nil } -func (i *instances) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) { +func (i *Instances) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) { ctx = sentry.SetHubOnContext(ctx) - instance, err := i.lookupLinode(ctx, node) + instance, err := i.LookupLinode(ctx, node) if err != nil { sentry.CaptureError(ctx, err) return false, err @@ -296,9 +300,9 @@ func (i *instances) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, return false, nil } -func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { +func (i *Instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { ctx = sentry.SetHubOnContext(ctx) - linode, err := i.lookupLinode(ctx, node) + linode, err := i.LookupLinode(ctx, node) if err != nil { sentry.CaptureError(ctx, err) return nil, err @@ -344,7 +348,7 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud klog.Infof("Instance %s, assembled IP addresses: %v", node.Name, addresses) // note that Zone is omitted as it's not a thing in Linode meta := &cloudprovider.InstanceMetadata{ - ProviderID: fmt.Sprintf("%v%v", providerIDPrefix, linode.ID), + ProviderID: fmt.Sprintf("%v%v", ccmUtils.ProviderIDPrefix, linode.ID), NodeAddresses: addresses, InstanceType: linode.Type, Region: linode.Region, @@ -353,9 +357,9 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud return meta, nil } -func (i *instances) getLinodeAddresses(ctx context.Context, node *v1.Node) ([]nodeIP, error) { +func (i *Instances) getLinodeAddresses(ctx context.Context, node *v1.Node) ([]nodeIP, error) { ctx = sentry.SetHubOnContext(ctx) - instance, err := i.lookupLinode(ctx, node) + instance, err := i.LookupLinode(ctx, node) if err != nil { sentry.CaptureError(ctx, err) return nil, err diff --git a/cloud/linode/instances_test.go b/cloud/linode/services/instances_test.go similarity index 85% rename from cloud/linode/instances_test.go rename to cloud/linode/services/instances_test.go index 3c17b0d5..4d6a5c1a 100644 --- a/cloud/linode/instances_test.go +++ b/cloud/linode/services/instances_test.go @@ -1,4 +1,4 @@ -package linode +package services import ( "fmt" @@ -17,6 +17,8 @@ import ( cloudprovider "k8s.io/cloud-provider" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" + ccmUtils "github.com/linode/linode-cloud-controller-manager/cloud/linode/utils" ) const ( @@ -43,8 +45,8 @@ func TestInstanceExists(t *testing.T) { client := mocks.NewMockClient(ctrl) t.Run("should return false if linode does not exist (by providerID)", func(t *testing.T) { - instances := newInstances(client) - node := nodeWithProviderID(providerIDPrefix + "123") + instances := NewInstances(client) + node := nodeWithProviderID(ccmUtils.ProviderIDPrefix + "123") client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) exists, err := instances.InstanceExists(ctx, node) @@ -53,8 +55,8 @@ func TestInstanceExists(t *testing.T) { }) t.Run("should return true if linode exists (by provider)", func(t *testing.T) { - instances := newInstances(client) - node := nodeWithProviderID(providerIDPrefix + "123") + instances := NewInstances(client) + node := nodeWithProviderID(ccmUtils.ProviderIDPrefix + "123") client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ { ID: 123, @@ -70,7 +72,7 @@ func TestInstanceExists(t *testing.T) { }) t.Run("should return true if linode exists (by name)", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) name := "some-name" node := nodeWithName(name) @@ -92,7 +94,7 @@ func TestMetadataRetrieval(t *testing.T) { client := mocks.NewMockClient(ctrl) t.Run("uses name over IP for finding linode", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) publicIP := net.ParseIP("172.234.31.123") privateIP := net.ParseIP("192.168.159.135") expectedInstance := linodego.Instance{Label: "expected-instance", ID: 12345, IPv4: []*net.IP{&publicIP, &privateIP}} @@ -102,13 +104,13 @@ func TestMetadataRetrieval(t *testing.T) { meta, err := instances.InstanceMetadata(ctx, node) require.NoError(t, err) - assert.Equal(t, providerIDPrefix+strconv.Itoa(expectedInstance.ID), meta.ProviderID) + assert.Equal(t, ccmUtils.ProviderIDPrefix+strconv.Itoa(expectedInstance.ID), meta.ProviderID) }) t.Run("fails when linode does not exist (by provider)", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 456302 - providerID := providerIDPrefix + strconv.Itoa(id) + providerID := ccmUtils.ProviderIDPrefix + strconv.Itoa(id) node := nodeWithProviderID(providerID) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) meta, err := instances.InstanceMetadata(ctx, node) @@ -118,7 +120,7 @@ func TestMetadataRetrieval(t *testing.T) { }) t.Run("should return data when linode is found (by name)", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 123 node := nodeWithName(instanceName) publicIPv4 := net.ParseIP("45.76.101.25") @@ -131,7 +133,7 @@ func TestMetadataRetrieval(t *testing.T) { meta, err := instances.InstanceMetadata(ctx, node) require.NoError(t, err) - assert.Equal(t, providerIDPrefix+strconv.Itoa(id), meta.ProviderID) + assert.Equal(t, ccmUtils.ProviderIDPrefix+strconv.Itoa(id), meta.ProviderID) assert.Equal(t, region, meta.Region) assert.Equal(t, linodeType, meta.InstanceType) assert.Equal(t, []v1.NodeAddress{ @@ -151,7 +153,7 @@ func TestMetadataRetrieval(t *testing.T) { }) t.Run("should return data when linode is found (by name) and addresses must be in order", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 123 node := nodeWithName(instanceName) publicIPv4 := net.ParseIP("45.76.101.25") @@ -159,9 +161,9 @@ func TestMetadataRetrieval(t *testing.T) { ipv6Addr := "2001::8a2e:370:7348" linodeType := typeG6 - Options.VPCNames = []string{"test"} - vpcIDs["test"] = 1 - Options.EnableRouteController = true + options.Options.VPCNames = []string{"test"} + VpcIDs["test"] = 1 + options.Options.EnableRouteController = true instance := linodego.Instance{ ID: id, @@ -178,33 +180,33 @@ func TestMetadataRetrieval(t *testing.T) { { Address: &vpcIP, AddressRange: nil, - VPCID: vpcIDs["test"], + VPCID: VpcIDs["test"], NAT1To1: nil, LinodeID: id, }, { Address: nil, AddressRange: &addressRange1, - VPCID: vpcIDs["test"], + VPCID: VpcIDs["test"], NAT1To1: nil, LinodeID: id, }, { Address: nil, AddressRange: &addressRange2, - VPCID: vpcIDs["test"], + VPCID: VpcIDs["test"], NAT1To1: nil, LinodeID: id, }, } client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{instance}, nil) - client.EXPECT().ListVPCIPAddresses(gomock.Any(), vpcIDs["test"], gomock.Any()).Return(routesInVPC, nil) - client.EXPECT().ListVPCIPv6Addresses(gomock.Any(), vpcIDs["test"], gomock.Any()).Return([]linodego.VPCIP{}, nil) + client.EXPECT().ListVPCIPAddresses(gomock.Any(), VpcIDs["test"], gomock.Any()).Return(routesInVPC, nil) + client.EXPECT().ListVPCIPv6Addresses(gomock.Any(), VpcIDs["test"], gomock.Any()).Return([]linodego.VPCIP{}, nil) meta, err := instances.InstanceMetadata(ctx, node) require.NoError(t, err) - assert.Equal(t, providerIDPrefix+strconv.Itoa(id), meta.ProviderID) + assert.Equal(t, ccmUtils.ProviderIDPrefix+strconv.Itoa(id), meta.ProviderID) assert.Equal(t, usEast, meta.Region) assert.Equal(t, linodeType, meta.InstanceType) assert.Equal(t, []v1.NodeAddress{ @@ -230,7 +232,7 @@ func TestMetadataRetrieval(t *testing.T) { }, }, meta.NodeAddresses) - Options.VPCNames = []string{} + options.Options.VPCNames = []string{} }) ipTests := []struct { @@ -327,15 +329,15 @@ func TestMetadataRetrieval(t *testing.T) { for _, test := range ipTests { t.Run(fmt.Sprintf("addresses are retrieved - %s", test.name), func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 192910 name := "my-instance" - providerID := providerIDPrefix + strconv.Itoa(id) + providerID := ccmUtils.ProviderIDPrefix + strconv.Itoa(id) node := nodeWithProviderID(providerID) if test.externalNetwork == "" { - Options.LinodeExternalNetwork = nil + options.Options.LinodeExternalNetwork = nil } else { - _, Options.LinodeExternalNetwork, _ = net.ParseCIDR(test.externalNetwork) + _, options.Options.LinodeExternalNetwork, _ = net.ParseCIDR(test.externalNetwork) } if test.existingAddresses != nil { node.Status.Addresses = append(node.Status.Addresses, test.existingAddresses...) @@ -419,7 +421,7 @@ func TestMetadataRetrieval(t *testing.T) { for _, test := range getByIPTests { t.Run(fmt.Sprintf("gets linode by IP - %s", test.name), func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{{ID: 3456, IPv4: []*net.IP{&wrongIP}}, expectedInstance}, nil) node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "test-node-1"}, Status: v1.NodeStatus{Addresses: test.nodeAddresses}} meta, err := instances.InstanceMetadata(ctx, &node) @@ -428,7 +430,7 @@ func TestMetadataRetrieval(t *testing.T) { assert.Equal(t, test.expectedErr, err) } else { require.NoError(t, err) - assert.Equal(t, providerIDPrefix+strconv.Itoa(expectedInstance.ID), meta.ProviderID) + assert.Equal(t, ccmUtils.ProviderIDPrefix+strconv.Itoa(expectedInstance.ID), meta.ProviderID) } }) } @@ -443,13 +445,13 @@ func TestMalformedProviders(t *testing.T) { client := mocks.NewMockClient(ctrl) t.Run("fails on non-numeric providerID", func(t *testing.T) { - instances := newInstances(client) - providerID := providerIDPrefix + "abc" + instances := NewInstances(client) + providerID := ccmUtils.ProviderIDPrefix + "abc" node := nodeWithProviderID(providerID) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) meta, err := instances.InstanceMetadata(ctx, node) - require.ErrorIs(t, err, invalidProviderIDError{providerID}) + require.ErrorIs(t, err, ccmUtils.InvalidProviderIDError{Value: providerID}) assert.Nil(t, meta) }) } @@ -462,9 +464,9 @@ func TestInstanceShutdown(t *testing.T) { client := mocks.NewMockClient(ctrl) t.Run("fails when instance not found (by provider)", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 12345 - node := nodeWithProviderID(providerIDPrefix + strconv.Itoa(id)) + node := nodeWithProviderID(ccmUtils.ProviderIDPrefix + strconv.Itoa(id)) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) shutdown, err := instances.InstanceShutdown(ctx, node) @@ -473,7 +475,7 @@ func TestInstanceShutdown(t *testing.T) { }) t.Run("fails when instance not found (by name)", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) name := "some-name" node := nodeWithName(name) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{}, nil) @@ -484,9 +486,9 @@ func TestInstanceShutdown(t *testing.T) { }) t.Run("returns true when instance is shut down", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 12345 - node := nodeWithProviderID(providerIDPrefix + strconv.Itoa(id)) + node := nodeWithProviderID(ccmUtils.ProviderIDPrefix + strconv.Itoa(id)) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: "offline-linode", Status: linodego.InstanceOffline}, }, nil) @@ -497,9 +499,9 @@ func TestInstanceShutdown(t *testing.T) { }) t.Run("returns true when instance is shutting down", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 12345 - node := nodeWithProviderID(providerIDPrefix + strconv.Itoa(id)) + node := nodeWithProviderID(ccmUtils.ProviderIDPrefix + strconv.Itoa(id)) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: "shutting-down-linode", Status: linodego.InstanceShuttingDown}, }, nil) @@ -510,9 +512,9 @@ func TestInstanceShutdown(t *testing.T) { }) t.Run("returns false when instance is running", func(t *testing.T) { - instances := newInstances(client) + instances := NewInstances(client) id := 12345 - node := nodeWithProviderID(providerIDPrefix + strconv.Itoa(id)) + node := nodeWithProviderID(ccmUtils.ProviderIDPrefix + strconv.Itoa(id)) client.EXPECT().ListInstances(gomock.Any(), nil).Times(1).Return([]linodego.Instance{ {ID: id, Label: "running-linode", Status: linodego.InstanceRunning}, }, nil) diff --git a/cloud/linode/vpc.go b/cloud/linode/services/vpc.go similarity index 76% rename from cloud/linode/vpc.go rename to cloud/linode/services/vpc.go index ea7a235c..cf74d31a 100644 --- a/cloud/linode/vpc.go +++ b/cloud/linode/services/vpc.go @@ -1,4 +1,4 @@ -package linode +package services import ( "context" @@ -13,14 +13,15 @@ import ( "k8s.io/klog/v2" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" ) var ( Mu sync.RWMutex - // vpcIDs map stores vpc id's for given vpc labels - vpcIDs = make(map[string]int, 0) - // subnetIDs map stores subnet id's for given subnet labels - subnetIDs = make(map[string]int, 0) + // VpcIDs map stores vpc id's for given vpc labels + VpcIDs = make(map[string]int, 0) + // SubnetIDs map stores subnet id's for given subnet labels + SubnetIDs = make(map[string]int, 0) ) type vpcLookupError struct { @@ -47,8 +48,8 @@ func (e subnetLookupError) Error() string { func GetAllVPCIDs() []int { Mu.Lock() defer Mu.Unlock() - values := make([]int, 0, len(vpcIDs)) - for _, v := range vpcIDs { + values := make([]int, 0, len(VpcIDs)) + for _, v := range VpcIDs { values = append(values, v) } return values @@ -60,7 +61,7 @@ func GetVPCID(ctx context.Context, client client.Client, vpcName string) (int, e defer Mu.Unlock() // check if map contains vpc id for given label - if vpcid, ok := vpcIDs[vpcName]; ok { + if vpcid, ok := VpcIDs[vpcName]; ok { return vpcid, nil } vpcs, err := client.ListVPCs(ctx, &linodego.ListOptions{}) @@ -69,7 +70,7 @@ func GetVPCID(ctx context.Context, client client.Client, vpcName string) (int, e } for _, vpc := range vpcs { if vpc.Label == vpcName { - vpcIDs[vpcName] = vpc.ID + VpcIDs[vpcName] = vpc.ID return vpc.ID, nil } } @@ -82,7 +83,7 @@ func GetSubnetID(ctx context.Context, client client.Client, vpcID int, subnetNam defer Mu.Unlock() // Check if map contains the id for the given label - if subnetid, ok := subnetIDs[subnetName]; ok { + if subnetid, ok := SubnetIDs[subnetName]; ok { return subnetid, nil } // Otherwise, get it from linodego.ListVPCSubnets() @@ -92,7 +93,7 @@ func GetSubnetID(ctx context.Context, client client.Client, vpcID int, subnetNam } for _, subnet := range subnets { if subnet.Label == subnetName { - subnetIDs[subnetName] = subnet.ID + SubnetIDs[subnetName] = subnet.ID return subnet.ID, nil } } @@ -110,11 +111,11 @@ func getVPCIDAndFilter(ctx context.Context, client client.Client, vpcName string resultFilter := "" // Get subnet ID(s) from name(s) if subnet-names is specified - if len(Options.SubnetNames) > 0 { + if len(options.Options.SubnetNames) > 0 { // subnetIDList is a slice of strings for ease of use with resultFilter subnetIDList := []string{} - for _, name := range Options.SubnetNames { + for _, name := range options.Options.SubnetNames { // For caching var subnetID int subnetID, err = GetSubnetID(ctx, client, vpcID, name) @@ -147,7 +148,7 @@ func handleNotFoundError(err error, vpcName string) error { Mu.Lock() defer Mu.Unlock() klog.Errorf("vpc %s not found. Deleting entry from cache", vpcName) - delete(vpcIDs, vpcName) + delete(VpcIDs, vpcName) } return err } @@ -179,19 +180,19 @@ func GetVPCIPv6Addresses(ctx context.Context, client client.Client, vpcName stri return resp, nil } -// getNodeBalancerBackendIPv4SubnetID returns the subnet ID for the NodeBalancer backend IPv4 subnet. +// GetNodeBalancerBackendIPv4SubnetID returns the subnet ID for the NodeBalancer backend IPv4 subnet. // It uses the first VPC name from Options.VPCNames to find the VPC ID and then retrieves the subnet ID // for the NodeBalancer backend IPv4 subnet name specified in Options.NodeBalancerBackendIPv4SubnetName. -func getNodeBalancerBackendIPv4SubnetID(client client.Client) (int, error) { +func GetNodeBalancerBackendIPv4SubnetID(client client.Client) (int, error) { // Get the VPC ID from the name - vpcID, err := GetVPCID(context.TODO(), client, Options.VPCNames[0]) + vpcID, err := GetVPCID(context.TODO(), client, options.Options.VPCNames[0]) if err != nil { - return 0, fmt.Errorf("failed to get vpc id for %s: %w", Options.VPCNames[0], err) + return 0, fmt.Errorf("failed to get vpc id for %s: %w", options.Options.VPCNames[0], err) } // Get the subnet ID from the name - subnetID, err := GetSubnetID(context.TODO(), client, vpcID, Options.NodeBalancerBackendIPv4SubnetName) + subnetID, err := GetSubnetID(context.TODO(), client, vpcID, options.Options.NodeBalancerBackendIPv4SubnetName) if err != nil { - return 0, fmt.Errorf("failed to get subnet id for %s: %w", Options.NodeBalancerBackendIPv4SubnetName, err) + return 0, fmt.Errorf("failed to get subnet id for %s: %w", options.Options.NodeBalancerBackendIPv4SubnetName, err) } return subnetID, nil @@ -199,7 +200,7 @@ func getNodeBalancerBackendIPv4SubnetID(client client.Client) (int, error) { // validateBothNameAndIDNotSet checks if both VPC IDs and names are set. func validateBothNameAndIDNotSet() error { - if len(Options.VPCIDs) != 0 && len(Options.VPCNames) != 0 { + if len(options.Options.VPCIDs) != 0 && len(options.Options.VPCNames) != 0 { return fmt.Errorf("cannot have both vpc-ids and vpc-names set") } return nil @@ -207,10 +208,10 @@ func validateBothNameAndIDNotSet() error { // validateVPCAndSubnetPairing checks if both VPC and subnet IDs or names are set correctly. func validateVPCAndSubnetPairing() error { - if len(Options.VPCIDs) != 0 && len(Options.SubnetIDs) == 0 { + if len(options.Options.VPCIDs) != 0 && len(options.Options.SubnetIDs) == 0 { return fmt.Errorf("vpc-ids cannot be set without subnet-ids") } - if len(Options.SubnetIDs) != 0 && len(Options.VPCIDs) == 0 { + if len(options.Options.SubnetIDs) != 0 && len(options.Options.VPCIDs) == 0 { return fmt.Errorf("subnet-ids cannot be set without vpc-ids") } return nil @@ -218,7 +219,7 @@ func validateVPCAndSubnetPairing() error { // validateSubnetNamesWithRouteController checks if subnet-names are set without vpc-names func validateSubnetNamesWithRouteController() error { - if len(Options.SubnetNames) != 0 && len(Options.VPCNames) == 0 && Options.EnableRouteController { + if len(options.Options.SubnetNames) != 0 && len(options.Options.VPCNames) == 0 && options.Options.EnableRouteController { return fmt.Errorf("subnet-names cannot be set without vpc-names") } return nil @@ -244,26 +245,26 @@ func validateVPCSubnetFlags() error { // resolveSubnetNames resolves subnet ids to names for the given VPC ID. func resolveSubnetNames(client client.Client, vpcID int) ([]string, error) { subnetNames := []string{} - for _, subnetID := range Options.SubnetIDs { + for _, subnetID := range options.Options.SubnetIDs { subnet, err := client.GetVPCSubnet(context.TODO(), vpcID, subnetID) if err != nil { return nil, fmt.Errorf("failed to get subnet %d for VPC %d: %w", subnetID, vpcID, err) } - subnetIDs[subnet.Label] = subnet.ID + SubnetIDs[subnet.Label] = subnet.ID subnetNames = append(subnetNames, subnet.Label) } return subnetNames, nil } -// validateAndSetVPCSubnetFlags validates the VPC and subnet flags and sets the vpcNames and subnetNames options. +// ValidateAndSetVPCSubnetFlags validates the VPC and subnet flags and sets the vpcNames and subnetNames options. // It retrieves the VPC names and subnet names from the Linode API based on the provided flags. // If subnet IDs are provided, it resolves the subnet names based on the first VPC ID. -func validateAndSetVPCSubnetFlags(client client.Client) error { +func ValidateAndSetVPCSubnetFlags(client client.Client) error { // ignore default subnet-names if subnet-ids are set - if len(Options.SubnetIDs) > 0 { - Options.SubnetNames = []string{} + if len(options.Options.SubnetIDs) > 0 { + options.Options.SubnetNames = []string{} } if err := validateVPCSubnetFlags(); err != nil { @@ -274,29 +275,29 @@ func validateAndSetVPCSubnetFlags(client client.Client) error { defer Mu.Unlock() vpcNames := []string{} - for idx, vpcID := range Options.VPCIDs { + for idx, vpcID := range options.Options.VPCIDs { vpc, err := client.GetVPC(context.TODO(), vpcID) if err != nil { return fmt.Errorf("failed to get VPC %d: %w", vpcID, err) } - vpcIDs[vpc.Label] = vpcID + VpcIDs[vpc.Label] = vpcID vpcNames = append(vpcNames, vpc.Label) // we resolve subnet names only for the first VPC ID // as there is no vpc to subnet mapping in input flags // and we assume all subnets are in the same VPC - if idx == 0 && len(Options.SubnetIDs) > 0 { + if idx == 0 && len(options.Options.SubnetIDs) > 0 { subnetNames, err := resolveSubnetNames(client, vpcID) if err != nil { return err } - Options.SubnetNames = append(Options.SubnetNames, subnetNames...) + options.Options.SubnetNames = append(options.Options.SubnetNames, subnetNames...) } } - Options.VPCNames = append(Options.VPCNames, vpcNames...) + options.Options.VPCNames = append(options.Options.VPCNames, vpcNames...) klog.V(3).Infof("VPC IDs: %v, VPC Names: %v, Subnet IDs: %v, Subnet Names: %v", - Options.VPCIDs, Options.VPCNames, Options.SubnetIDs, Options.SubnetNames) + options.Options.VPCIDs, options.Options.VPCNames, options.Options.SubnetIDs, options.Options.SubnetNames) return nil } diff --git a/cloud/linode/vpc_test.go b/cloud/linode/services/vpc_test.go similarity index 72% rename from cloud/linode/vpc_test.go rename to cloud/linode/services/vpc_test.go index 736f14da..88f9dc62 100644 --- a/cloud/linode/vpc_test.go +++ b/cloud/linode/services/vpc_test.go @@ -1,4 +1,4 @@ -package linode +package services import ( "errors" @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/linode/linode-cloud-controller-manager/cloud/linode/client/mocks" + "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" ) func TestGetAllVPCIDs(t *testing.T) { @@ -34,7 +35,7 @@ func TestGetAllVPCIDs(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - vpcIDs = tt.vpcIDs + VpcIDs = tt.vpcIDs got := GetAllVPCIDs() sort.Ints(got) if !reflect.DeepEqual(got, tt.want) { @@ -49,14 +50,14 @@ func TestGetVPCID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} got, err := GetVPCID(t.Context(), client, "test3") if err != nil { t.Errorf("GetVPCID() error = %v", err) return } - if got != vpcIDs["test3"] { - t.Errorf("GetVPCID() = %v, want %v", got, vpcIDs["test3"]) + if got != VpcIDs["test3"] { + t.Errorf("GetVPCID() = %v, want %v", got, VpcIDs["test3"]) } }) @@ -64,7 +65,7 @@ func TestGetVPCID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{}, errors.New("error")) got, err := GetVPCID(t.Context(), client, "test4") require.Error(t, err) @@ -77,7 +78,7 @@ func TestGetVPCID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{}, nil) got, err := GetVPCID(t.Context(), client, "test4") require.ErrorIs(t, err, vpcLookupError{"test4"}) @@ -90,7 +91,7 @@ func TestGetVPCID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{{ID: 4, Label: "test4"}}, nil) got, err := GetVPCID(t.Context(), client, "test4") require.NoError(t, err) @@ -105,7 +106,7 @@ func TestGetVPCIPAddresses(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{}, nil) _, err := GetVPCIPAddresses(t.Context(), client, "test4") require.Error(t, err) @@ -115,11 +116,11 @@ func TestGetVPCIPAddresses(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, &linodego.Error{Code: http.StatusNotFound, Message: "[404] [label] VPC not found"}) _, err := GetVPCIPAddresses(t.Context(), client, "test3") require.Error(t, err) - _, exists := vpcIDs["test3"] + _, exists := VpcIDs["test3"] assert.False(t, exists, "test3 key should get deleted from vpcIDs map") }) @@ -127,11 +128,11 @@ func TestGetVPCIPAddresses(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, &linodego.Error{Code: http.StatusInternalServerError, Message: "[500] [label] Internal Server Error"}) _, err := GetVPCIPAddresses(t.Context(), client, "test1") require.Error(t, err) - _, exists := vpcIDs["test1"] + _, exists := VpcIDs["test1"] assert.True(t, exists, "test1 key should not get deleted from vpcIDs map") }) @@ -139,12 +140,12 @@ func TestGetVPCIPAddresses(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - vpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + VpcIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{{ID: 10, Label: "test10"}}, nil) client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil) _, err := GetVPCIPAddresses(t.Context(), client, "test10") require.NoError(t, err) - _, exists := vpcIDs["test10"] + _, exists := VpcIDs["test10"] assert.True(t, exists, "test10 key should be present in vpcIDs map") }) @@ -152,17 +153,17 @@ func TestGetVPCIPAddresses(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - sn := Options.SubnetNames - defer func() { Options.SubnetNames = sn }() - Options.SubnetNames = []string{"subnet4"} - vpcIDs = map[string]int{"test1": 1} - subnetIDs = map[string]int{"subnet1": 1} + sn := options.Options.SubnetNames + defer func() { options.Options.SubnetNames = sn }() + options.Options.SubnetNames = []string{"subnet4"} + VpcIDs = map[string]int{"test1": 1} + SubnetIDs = map[string]int{"subnet1": 1} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{{ID: 10, Label: "test10"}}, nil) client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{{ID: 4, Label: "subnet4"}}, nil) client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil) _, err := GetVPCIPAddresses(t.Context(), client, "test10") require.NoError(t, err) - _, exists := subnetIDs["subnet4"] + _, exists := SubnetIDs["subnet4"] assert.True(t, exists, "subnet4 should be present in subnetIDs map") }) } @@ -172,14 +173,14 @@ func TestGetSubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} got, err := GetSubnetID(t.Context(), client, 0, "test3") if err != nil { t.Errorf("GetSubnetID() error = %v", err) return } - if got != subnetIDs["test3"] { - t.Errorf("GetSubnetID() = %v, want %v", got, subnetIDs["test3"]) + if got != SubnetIDs["test3"] { + t.Errorf("GetSubnetID() = %v, want %v", got, SubnetIDs["test3"]) } }) @@ -187,14 +188,14 @@ func TestGetSubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{}, errors.New("error")) got, err := GetSubnetID(t.Context(), client, 0, "test4") require.Error(t, err) if got != 0 { t.Errorf("GetSubnetID() = %v, want %v", got, 0) } - _, exists := subnetIDs["test4"] + _, exists := SubnetIDs["test4"] assert.False(t, exists, "subnet4 should not be present in subnetIDs") }) @@ -202,7 +203,7 @@ func TestGetSubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{}, nil) got, err := GetSubnetID(t.Context(), client, 0, "test4") require.ErrorIs(t, err, subnetLookupError{"test4"}) @@ -215,7 +216,7 @@ func TestGetSubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{{ID: 4, Label: "test4"}}, nil) got, err := GetSubnetID(t.Context(), client, 0, "test4") require.NoError(t, err) @@ -230,19 +231,19 @@ func TestGetNodeBalancerBackendIPv4SubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - currVPCNames := Options.VPCNames - currVPCIDs := vpcIDs - currSubnetIDs := subnetIDs + currVPCNames := options.Options.VPCNames + currVPCIDs := VpcIDs + currSubnetIDs := SubnetIDs defer func() { - Options.VPCNames = currVPCNames - vpcIDs = currVPCIDs - subnetIDs = currSubnetIDs + options.Options.VPCNames = currVPCNames + VpcIDs = currVPCIDs + SubnetIDs = currSubnetIDs }() - Options.VPCNames = []string{"vpc-test1", "vpc-test2", "vpc-test3"} - vpcIDs = map[string]int{"vpc-test2": 2, "vpc-test3": 3} - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + options.Options.VPCNames = []string{"vpc-test1", "vpc-test2", "vpc-test3"} + VpcIDs = map[string]int{"vpc-test2": 2, "vpc-test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{}, errors.New("error")) - subnetID, err := getNodeBalancerBackendIPv4SubnetID(client) + subnetID, err := GetNodeBalancerBackendIPv4SubnetID(client) require.Error(t, err) if subnetID != 0 { t.Errorf("getNodeBalancerBackendIPv4SubnetID() = %v, want %v", subnetID, 0) @@ -253,13 +254,13 @@ func TestGetNodeBalancerBackendIPv4SubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - currVPCNames := Options.VPCNames - defer func() { Options.VPCNames = currVPCNames }() - Options.VPCNames = []string{"vpc-test1", "vpc-test2", "vpc-test3"} - vpcIDs = map[string]int{"vpc-test1": 1, "vpc-test2": 2, "vpc-test3": 3} - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + currVPCNames := options.Options.VPCNames + defer func() { options.Options.VPCNames = currVPCNames }() + options.Options.VPCNames = []string{"vpc-test1", "vpc-test2", "vpc-test3"} + VpcIDs = map[string]int{"vpc-test1": 1, "vpc-test2": 2, "vpc-test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{}, errors.New("error")) - subnetID, err := getNodeBalancerBackendIPv4SubnetID(client) + subnetID, err := GetNodeBalancerBackendIPv4SubnetID(client) require.Error(t, err) if subnetID != 0 { t.Errorf("getNodeBalancerBackendIPv4SubnetID() = %v, want %v", subnetID, 0) @@ -270,18 +271,18 @@ func TestGetNodeBalancerBackendIPv4SubnetID(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - currVPCNames := Options.VPCNames - currNodeBalancerBackendIPv4SubnetName := Options.NodeBalancerBackendIPv4SubnetName + currVPCNames := options.Options.VPCNames + currNodeBalancerBackendIPv4SubnetName := options.Options.NodeBalancerBackendIPv4SubnetName defer func() { - Options.VPCNames = currVPCNames - Options.NodeBalancerBackendIPv4SubnetName = currNodeBalancerBackendIPv4SubnetName + options.Options.VPCNames = currVPCNames + options.Options.NodeBalancerBackendIPv4SubnetName = currNodeBalancerBackendIPv4SubnetName }() - Options.VPCNames = []string{"vpc-test1", "vpc-test2", "vpc-test3"} - Options.NodeBalancerBackendIPv4SubnetName = "test4" - vpcIDs = map[string]int{"vpc-test1": 1, "vpc-test2": 2, "vpc-test3": 3} - subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} + options.Options.VPCNames = []string{"vpc-test1", "vpc-test2", "vpc-test3"} + options.Options.NodeBalancerBackendIPv4SubnetName = "test4" + VpcIDs = map[string]int{"vpc-test1": 1, "vpc-test2": 2, "vpc-test3": 3} + SubnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3} client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{{ID: 4, Label: "test4"}}, nil) - subnetID, err := getNodeBalancerBackendIPv4SubnetID(client) + subnetID, err := GetNodeBalancerBackendIPv4SubnetID(client) require.NoError(t, err) if subnetID != 4 { t.Errorf("getNodeBalancerBackendIPv4SubnetID() = %v, want %v", subnetID, 4) @@ -349,10 +350,10 @@ func Test_validateVPCSubnetFlags(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - Options.VPCIDs = tt.vpcIDs - Options.VPCNames = tt.vpcNames - Options.SubnetIDs = tt.subnetIDs - Options.SubnetNames = tt.subnetNames + options.Options.VPCIDs = tt.vpcIDs + options.Options.VPCNames = tt.vpcNames + options.Options.SubnetIDs = tt.subnetIDs + options.Options.SubnetNames = tt.subnetNames if err := validateVPCSubnetFlags(); (err != nil) != tt.wantErr { t.Errorf("validateVPCSubnetFlags() error = %v, wantErr %v", err, tt.wantErr) } @@ -365,13 +366,13 @@ func Test_resolveSubnetNames(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - optionsSubnetIDs := Options.SubnetIDs - currSubnetIDs := subnetIDs + optionsSubnetIDs := options.Options.SubnetIDs + currSubnetIDs := SubnetIDs defer func() { - Options.SubnetIDs = optionsSubnetIDs - subnetIDs = currSubnetIDs + options.Options.SubnetIDs = optionsSubnetIDs + SubnetIDs = currSubnetIDs }() - Options.SubnetIDs = []int{} + options.Options.SubnetIDs = []int{} // client.EXPECT().GetVPCSubnet(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&linodego.VPCSubnet{}, errors.New("error")) subnetNames, err := resolveSubnetNames(client, 10) if err != nil { @@ -387,13 +388,13 @@ func Test_resolveSubnetNames(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - optionsSubnetIDs := Options.SubnetIDs - currSubnetIDs := subnetIDs + optionsSubnetIDs := options.Options.SubnetIDs + currSubnetIDs := SubnetIDs defer func() { - Options.SubnetIDs = optionsSubnetIDs - subnetIDs = currSubnetIDs + options.Options.SubnetIDs = optionsSubnetIDs + SubnetIDs = currSubnetIDs }() - Options.SubnetIDs = []int{1} + options.Options.SubnetIDs = []int{1} client.EXPECT().GetVPCSubnet(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&linodego.VPCSubnet{}, errors.New("error")) _, err := resolveSubnetNames(client, 10) require.Error(t, err) @@ -403,13 +404,13 @@ func Test_resolveSubnetNames(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - optionsSubnetIDs := Options.SubnetIDs - currSubnetIDs := subnetIDs + optionsSubnetIDs := options.Options.SubnetIDs + currSubnetIDs := SubnetIDs defer func() { - Options.SubnetIDs = optionsSubnetIDs - subnetIDs = currSubnetIDs + options.Options.SubnetIDs = optionsSubnetIDs + SubnetIDs = currSubnetIDs }() - Options.SubnetIDs = []int{1} + options.Options.SubnetIDs = []int{1} client.EXPECT().GetVPCSubnet(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(&linodego.VPCSubnet{ID: 1, Label: "subnet1"}, nil) subnet, err := resolveSubnetNames(client, 10) require.NoError(t, err) @@ -422,9 +423,9 @@ func Test_validateAndSetVPCSubnetFlags(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - Options.VPCIDs = []int{1, 2} - Options.SubnetIDs = []int{} - err := validateAndSetVPCSubnetFlags(client) + options.Options.VPCIDs = []int{1, 2} + options.Options.SubnetIDs = []int{} + err := ValidateAndSetVPCSubnetFlags(client) require.Error(t, err) }) @@ -432,19 +433,19 @@ func Test_validateAndSetVPCSubnetFlags(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - currVPCIDs := Options.VPCIDs - currSubnetIDs := Options.SubnetIDs + currVPCIDs := options.Options.VPCIDs + currSubnetIDs := options.Options.SubnetIDs defer func() { - Options.VPCIDs = currVPCIDs - Options.SubnetIDs = currSubnetIDs - vpcIDs = map[string]int{} - subnetIDs = map[string]int{} + options.Options.VPCIDs = currVPCIDs + options.Options.SubnetIDs = currSubnetIDs + VpcIDs = map[string]int{} + SubnetIDs = map[string]int{} }() - Options.VPCIDs = []int{1} - Options.SubnetIDs = []int{1, 2} + options.Options.VPCIDs = []int{1} + options.Options.SubnetIDs = []int{1, 2} client.EXPECT().GetVPC(gomock.Any(), gomock.Any()).Times(1).Return(&linodego.VPC{ID: 1, Label: "test"}, nil) client.EXPECT().GetVPCSubnet(gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(&linodego.VPCSubnet{ID: 1, Label: "subnet1"}, nil) - err := validateAndSetVPCSubnetFlags(client) + err := ValidateAndSetVPCSubnetFlags(client) require.NoError(t, err) }) @@ -452,20 +453,20 @@ func Test_validateAndSetVPCSubnetFlags(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - currVPCIDs := Options.VPCIDs - currSubnetIDs := Options.SubnetIDs + currVPCIDs := options.Options.VPCIDs + currSubnetIDs := options.Options.SubnetIDs defer func() { - Options.VPCIDs = currVPCIDs - Options.SubnetIDs = currSubnetIDs - vpcIDs = map[string]int{} - subnetIDs = map[string]int{} + options.Options.VPCIDs = currVPCIDs + options.Options.SubnetIDs = currSubnetIDs + VpcIDs = map[string]int{} + SubnetIDs = map[string]int{} }() - Options.VPCIDs = []int{1} - Options.SubnetIDs = []int{1, 2} - Options.VPCNames = []string{} - Options.SubnetNames = []string{} + options.Options.VPCIDs = []int{1} + options.Options.SubnetIDs = []int{1, 2} + options.Options.VPCNames = []string{} + options.Options.SubnetNames = []string{} client.EXPECT().GetVPC(gomock.Any(), gomock.Any()).Times(1).Return(nil, errors.New("error")) - err := validateAndSetVPCSubnetFlags(client) + err := ValidateAndSetVPCSubnetFlags(client) require.Error(t, err) }) @@ -473,19 +474,19 @@ func Test_validateAndSetVPCSubnetFlags(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mocks.NewMockClient(ctrl) - currVPCNames := Options.VPCNames - currSubnetNames := Options.SubnetNames + currVPCNames := options.Options.VPCNames + currSubnetNames := options.Options.SubnetNames defer func() { - Options.VPCNames = currVPCNames - Options.SubnetNames = currSubnetNames - vpcIDs = map[string]int{} - subnetIDs = map[string]int{} + options.Options.VPCNames = currVPCNames + options.Options.SubnetNames = currSubnetNames + VpcIDs = map[string]int{} + SubnetIDs = map[string]int{} }() - Options.VPCNames = []string{"vpc1"} - Options.SubnetNames = []string{"subnet1", "subnet2"} - Options.VPCIDs = []int{} - Options.SubnetIDs = []int{} - err := validateAndSetVPCSubnetFlags(client) + options.Options.VPCNames = []string{"vpc1"} + options.Options.SubnetNames = []string{"subnet1", "subnet2"} + options.Options.VPCIDs = []int{} + options.Options.SubnetIDs = []int{} + err := ValidateAndSetVPCSubnetFlags(client) require.NoError(t, err) }) } diff --git a/cloud/linode/utils/common.go b/cloud/linode/utils/common.go new file mode 100644 index 00000000..f4db8962 --- /dev/null +++ b/cloud/linode/utils/common.go @@ -0,0 +1,56 @@ +package utils + +import ( + "fmt" + "net" + "strconv" + "strings" + + "github.com/linode/linodego" +) + +const ( + ProviderIDPrefix = "linode://" + DNS1123LabelMaxLength int = 63 +) + +type InvalidProviderIDError struct { + Value string +} + +func (e InvalidProviderIDError) Error() string { + return fmt.Sprintf("invalid provider ID %q", e.Value) +} + +func IsLinodeProviderID(providerID string) bool { + return strings.HasPrefix(providerID, ProviderIDPrefix) +} + +func ParseProviderID(providerID string) (int, error) { + if !IsLinodeProviderID(providerID) { + return 0, InvalidProviderIDError{providerID} + } + id, err := strconv.Atoi(strings.TrimPrefix(providerID, ProviderIDPrefix)) + if err != nil { + return 0, InvalidProviderIDError{providerID} + } + return id, nil +} + +// IgnoreLinodeAPIError returns the error except matches to status code +func IgnoreLinodeAPIError(err error, code int) error { + apiErr := linodego.Error{Code: code} + if apiErr.Is(err) { + err = nil + } + + return err +} + +func IsPrivate(ip *net.IP, linodeExternalNetwork *net.IPNet) bool { + if linodeExternalNetwork == nil { + return ip.IsPrivate() + } + + return ip.IsPrivate() && !linodeExternalNetwork.Contains(*ip) +} diff --git a/cloud/linode/common_test.go b/cloud/linode/utils/common_test.go similarity index 96% rename from cloud/linode/common_test.go rename to cloud/linode/utils/common_test.go index cb517640..16827de8 100644 --- a/cloud/linode/common_test.go +++ b/cloud/linode/utils/common_test.go @@ -1,4 +1,4 @@ -package linode +package utils import ( "errors" @@ -36,7 +36,7 @@ func TestParseProviderID(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - id, err := parseProviderID(tc.providerID) + id, err := ParseProviderID(tc.providerID) if err != nil { if !tc.errExpected { t.Errorf("unexpected error: %v", err) diff --git a/main.go b/main.go index 81e403e8..ebf2fc9a 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ import ( "k8s.io/klog/v2" "github.com/linode/linode-cloud-controller-manager/cloud/linode" + ccmOptions "github.com/linode/linode-cloud-controller-manager/cloud/linode/options" "github.com/linode/linode-cloud-controller-manager/sentry" _ "k8s.io/component-base/metrics/prometheus/clientgo" // for client metric registration @@ -70,37 +71,37 @@ func main() { ctx := sentry.SetHubOnContext(context.Background()) - ccmOptions, err := options.NewCloudControllerManagerOptions() + opts, err := options.NewCloudControllerManagerOptions() if err != nil { klog.Fatalf("unable to initialize command options: %v", err) } fss := utilflag.NamedFlagSets{} controllerAliases := names.CCMControllerAliases() stopCh := make(chan struct{}) - command := app.NewCloudControllerManagerCommand(ccmOptions, cloudInitializer, app.DefaultInitFuncConstructors, controllerAliases, fss, stopCh) + command := app.NewCloudControllerManagerCommand(opts, cloudInitializer, app.DefaultInitFuncConstructors, controllerAliases, fss, stopCh) // 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().BoolVar(&linode.Options.EnableTokenHealthChecker, "enable-token-health-checker", false, "enables Linode API token health checker") - command.Flags().StringSliceVar(&linode.Options.VPCNames, "vpc-names", nil, "comma separated vpc names whose routes will be managed by route-controller") - command.Flags().StringSliceVar(&linode.Options.SubnetNames, "subnet-names", []string{"default"}, "comma separated subnet names whose routes will be managed by route-controller (requires vpc-names flag to also be set)") - command.Flags().IntSliceVar(&linode.Options.VPCIDs, "vpc-ids", nil, "comma separated vpc ids whose routes will be managed by route-controller") - command.Flags().IntSliceVar(&linode.Options.SubnetIDs, "subnet-ids", nil, "comma separated subnet ids whose routes will be managed by route-controller (requires vpc-ids flag to also be set)") - command.Flags().StringVar(&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") - command.Flags().StringVar(&linode.Options.IpHolderSuffix, "ip-holder-suffix", "", "suffix to append to the ip holder name when using shared IP fail-over with BGP (e.g. ip-holder-suffix=my-cluster-name") - command.Flags().StringVar(&linode.Options.DefaultNBType, "default-nodebalancer-type", string(linodego.NBTypeCommon), "default type of NodeBalancer to create (options: common, premium)") - command.Flags().StringVar(&linode.Options.NodeBalancerBackendIPv4Subnet, "nodebalancer-backend-ipv4-subnet", "", "ipv4 subnet to use for NodeBalancer backends") - command.Flags().StringSliceVar(&linode.Options.NodeBalancerTags, "nodebalancer-tags", []string{}, "Linode tags to apply to all NodeBalancers") - command.Flags().BoolVar(&linode.Options.EnableIPv6ForLoadBalancers, "enable-ipv6-for-loadbalancers", false, "set both IPv4 and IPv6 addresses for all LoadBalancer services (when disabled, only IPv4 is used)") - command.Flags().IntVar(&linode.Options.NodeCIDRMaskSizeIPv4, "node-cidr-mask-size-ipv4", 0, "ipv4 cidr mask size for pod cidrs allocated to nodes") - command.Flags().IntVar(&linode.Options.NodeCIDRMaskSizeIPv6, "node-cidr-mask-size-ipv6", 0, "ipv6 cidr mask size for pod cidrs allocated to nodes") - command.Flags().IntVar(&linode.Options.NodeBalancerBackendIPv4SubnetID, "nodebalancer-backend-ipv4-subnet-id", 0, "ipv4 subnet id to use for NodeBalancer backends") - command.Flags().StringVar(&linode.Options.NodeBalancerBackendIPv4SubnetName, "nodebalancer-backend-ipv4-subnet-name", "", "ipv4 subnet name to use for NodeBalancer backends") - command.Flags().BoolVar(&linode.Options.DisableNodeBalancerVPCBackends, "disable-nodebalancer-vpc-backends", false, "disables nodebalancer backends in VPCs (when enabled, nodebalancers will only have private IPs as backends for backward compatibility)") - command.Flags().StringVar(&linode.Options.NodeBalancerPrefix, "nodebalancer-prefix", "ccm", fmt.Sprintf("Name prefix for NoadBalancers. (max. %v char.)", linode.NodeBalancerPrefixCharLimit)) - command.Flags().BoolVar(&linode.Options.DisableIPv6NodeCIDRAllocation, "disable-ipv6-node-cidr-allocation", false, "disables IPv6 node cidr allocation by ipam controller (when enabled, IPv6 cidr ranges will be allocated to nodes)") + command.Flags().BoolVar(&ccmOptions.Options.LinodeGoDebug, "linodego-debug", false, "enables debug output for the LinodeAPI wrapper") + command.Flags().BoolVar(&ccmOptions.Options.EnableRouteController, "enable-route-controller", false, "enables route_controller for ccm") + command.Flags().BoolVar(&ccmOptions.Options.EnableTokenHealthChecker, "enable-token-health-checker", false, "enables Linode API token health checker") + command.Flags().StringSliceVar(&ccmOptions.Options.VPCNames, "vpc-names", nil, "comma separated vpc names whose routes will be managed by route-controller") + command.Flags().StringSliceVar(&ccmOptions.Options.SubnetNames, "subnet-names", []string{"default"}, "comma separated subnet names whose routes will be managed by route-controller (requires vpc-names flag to also be set)") + command.Flags().IntSliceVar(&ccmOptions.Options.VPCIDs, "vpc-ids", nil, "comma separated vpc ids whose routes will be managed by route-controller") + command.Flags().IntSliceVar(&ccmOptions.Options.SubnetIDs, "subnet-ids", nil, "comma separated subnet ids whose routes will be managed by route-controller (requires vpc-ids flag to also be set)") + command.Flags().StringVar(&ccmOptions.Options.LoadBalancerType, "load-balancer-type", "nodebalancer", "configures which type of load-balancing to use for LoadBalancer Services (options: nodebalancer, cilium-bgp)") + command.Flags().StringVar(&ccmOptions.Options.BGPNodeSelector, "bgp-node-selector", "", "node selector to use to perform shared IP fail-over with BGP (e.g. cilium-bgp-peering=true") + command.Flags().StringVar(&ccmOptions.Options.IpHolderSuffix, "ip-holder-suffix", "", "suffix to append to the ip holder name when using shared IP fail-over with BGP (e.g. ip-holder-suffix=my-cluster-name") + command.Flags().StringVar(&ccmOptions.Options.DefaultNBType, "default-nodebalancer-type", string(linodego.NBTypeCommon), "default type of NodeBalancer to create (options: common, premium)") + command.Flags().StringVar(&ccmOptions.Options.NodeBalancerBackendIPv4Subnet, "nodebalancer-backend-ipv4-subnet", "", "ipv4 subnet to use for NodeBalancer backends") + command.Flags().StringSliceVar(&ccmOptions.Options.NodeBalancerTags, "nodebalancer-tags", []string{}, "Linode tags to apply to all NodeBalancers") + command.Flags().BoolVar(&ccmOptions.Options.EnableIPv6ForLoadBalancers, "enable-ipv6-for-loadbalancers", false, "set both IPv4 and IPv6 addresses for all LoadBalancer services (when disabled, only IPv4 is used)") + command.Flags().IntVar(&ccmOptions.Options.NodeCIDRMaskSizeIPv4, "node-cidr-mask-size-ipv4", 0, "ipv4 cidr mask size for pod cidrs allocated to nodes") + command.Flags().IntVar(&ccmOptions.Options.NodeCIDRMaskSizeIPv6, "node-cidr-mask-size-ipv6", 0, "ipv6 cidr mask size for pod cidrs allocated to nodes") + command.Flags().IntVar(&ccmOptions.Options.NodeBalancerBackendIPv4SubnetID, "nodebalancer-backend-ipv4-subnet-id", 0, "ipv4 subnet id to use for NodeBalancer backends") + command.Flags().StringVar(&ccmOptions.Options.NodeBalancerBackendIPv4SubnetName, "nodebalancer-backend-ipv4-subnet-name", "", "ipv4 subnet name to use for NodeBalancer backends") + command.Flags().BoolVar(&ccmOptions.Options.DisableNodeBalancerVPCBackends, "disable-nodebalancer-vpc-backends", false, "disables nodebalancer backends in VPCs (when enabled, nodebalancers will only have private IPs as backends for backward compatibility)") + command.Flags().StringVar(&ccmOptions.Options.NodeBalancerPrefix, "nodebalancer-prefix", "ccm", fmt.Sprintf("Name prefix for NoadBalancers. (max. %v char.)", linode.NodeBalancerPrefixCharLimit)) + command.Flags().BoolVar(&ccmOptions.Options.DisableIPv6NodeCIDRAllocation, "disable-ipv6-node-cidr-allocation", false, "disables IPv6 node cidr allocation by ipam controller (when enabled, IPv6 cidr ranges will be allocated to nodes)") // Set static flags command.Flags().VisitAll(func(fl *pflag.Flag) { @@ -125,8 +126,8 @@ func main() { }) // Make the Linode-specific CCM bits aware of the kubeconfig flag - linode.Options.KubeconfigFlag = command.Flags().Lookup("kubeconfig") - if linode.Options.KubeconfigFlag == nil { + ccmOptions.Options.KubeconfigFlag = command.Flags().Lookup("kubeconfig") + if ccmOptions.Options.KubeconfigFlag == nil { msg := "kubeconfig missing from CCM flag set" sentry.CaptureError(ctx, fmt.Errorf("%s", msg)) fmt.Fprintf(os.Stderr, "kubeconfig missing from CCM flag set"+"\n") @@ -141,11 +142,11 @@ func main() { fmt.Fprintf(os.Stderr, "%v\n", msg) os.Exit(1) } - linode.Options.LinodeExternalNetwork = network + ccmOptions.Options.LinodeExternalNetwork = network } // Provide stop channel for linode authenticated client healthchecker - linode.Options.GlobalStopChannel = stopCh + ccmOptions.Options.GlobalStopChannel = stopCh pflag.CommandLine.SetNormalizeFunc(utilflag.WordSepNormalizeFunc) pflag.CommandLine.AddGoFlagSet(flag.CommandLine) @@ -163,12 +164,12 @@ func main() { func cloudInitializer(config *config.CompletedConfig) cloudprovider.Interface { // initialize cloud provider with the cloud provider name and config file provided if config.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs { - linode.Options.AllocateNodeCIDRs = true + ccmOptions.Options.AllocateNodeCIDRs = true if config.ComponentConfig.KubeCloudShared.ClusterCIDR == "" { fmt.Fprintf(os.Stderr, "--cluster-cidr is not set. This is required if --allocate-node-cidrs is set.\n") os.Exit(1) } - linode.Options.ClusterCIDRIPv4 = config.ComponentConfig.KubeCloudShared.ClusterCIDR + ccmOptions.Options.ClusterCIDRIPv4 = config.ComponentConfig.KubeCloudShared.ClusterCIDR } cloud, err := cloudprovider.InitCloudProvider(linode.ProviderName, "") if err != nil {