Skip to content

Commit 22d5dd0

Browse files
authored
Merge branch 'linode:main' into main
2 parents dd0f0f1 + 4024ed3 commit 22d5dd0

File tree

40 files changed

+286
-13
lines changed

40 files changed

+286
-13
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ e2e-test:
193193
KUBECONFIG=$(KUBECONFIG_PATH) \
194194
REGION=$(LINODE_REGION) \
195195
LINODE_TOKEN=$(LINODE_TOKEN) \
196-
chainsaw test e2e/test --parallel 2
196+
chainsaw test e2e/test --parallel 2 $(E2E_FLAGS)
197197

198198
.PHONY: e2e-test-bgp
199199
e2e-test-bgp:
@@ -204,7 +204,7 @@ e2e-test-bgp:
204204
KUBECONFIG=$(KUBECONFIG_PATH) \
205205
REGION=$(LINODE_REGION) \
206206
LINODE_TOKEN=$(LINODE_TOKEN) \
207-
chainsaw test e2e/bgp-test/lb-cilium-bgp
207+
chainsaw test e2e/bgp-test/lb-cilium-bgp $(E2E_FLAGS)
208208

209209
#####################################################################
210210
# OS / ARCH

cloud/linode/client/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Client interface {
3535

3636
ListVPCs(context.Context, *linodego.ListOptions) ([]linodego.VPC, error)
3737
ListVPCIPAddresses(context.Context, int, *linodego.ListOptions) ([]linodego.VPCIP, error)
38+
ListVPCSubnets(context.Context, int, *linodego.ListOptions) ([]linodego.VPCSubnet, error)
3839

3940
CreateNodeBalancer(context.Context, linodego.NodeBalancerCreateOptions) (*linodego.NodeBalancer, error)
4041
GetNodeBalancer(context.Context, int) (*linodego.NodeBalancer, error)

cloud/linode/client/client_with_metrics.go

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/linode/client/mocks/mock_client.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloud/linode/cloud.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var Options struct {
4141
// Deprecated: use VPCNames instead
4242
VPCName string
4343
VPCNames string
44+
SubnetNames string
4445
LoadBalancerType string
4546
BGPNodeSelector string
4647
IpHolderSuffix string
@@ -132,6 +133,12 @@ func newCloud() (cloudprovider.Interface, error) {
132133
Options.VPCNames = Options.VPCName
133134
}
134135

136+
// SubnetNames can't be used without VPCNames also being set
137+
if Options.SubnetNames != "" && Options.VPCNames == "" {
138+
klog.Warningf("failed to set flag subnet-names: vpc-names must be set to a non-empty value")
139+
Options.SubnetNames = ""
140+
}
141+
135142
instanceCache = newInstances(linodeClient)
136143
routes, err := newRoutes(linodeClient, instanceCache)
137144
if err != nil {

cloud/linode/vpc.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package linode
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"net/http"
8+
"strconv"
79
"strings"
810
"sync"
911

@@ -16,16 +18,30 @@ var (
1618
Mu sync.RWMutex
1719
// vpcIDs map stores vpc id's for given vpc labels
1820
vpcIDs = make(map[string]int, 0)
21+
// subnetIDs map stores subnet id's for given subnet labels
22+
subnetIDs = make(map[string]int, 0)
1923
)
2024

2125
type vpcLookupError struct {
2226
value string
2327
}
2428

29+
type subnetLookupError struct {
30+
value string
31+
}
32+
33+
type subnetFilter struct {
34+
SubnetID string `json:"subnet_id"`
35+
}
36+
2537
func (e vpcLookupError) Error() string {
2638
return fmt.Sprintf("failed to find VPC: %q", e.value)
2739
}
2840

41+
func (e subnetLookupError) Error() string {
42+
return fmt.Sprintf("failed to find subnet: %q", e.value)
43+
}
44+
2945
// GetAllVPCIDs returns vpc ids stored in map
3046
func GetAllVPCIDs() []int {
3147
Mu.Lock()
@@ -59,13 +75,68 @@ func GetVPCID(ctx context.Context, client client.Client, vpcName string) (int, e
5975
return 0, vpcLookupError{vpcName}
6076
}
6177

78+
// GetSubnetID returns the subnet ID of given subnet label
79+
func GetSubnetID(ctx context.Context, client client.Client, vpcID int, subnetName string) (int, error) {
80+
Mu.Lock()
81+
defer Mu.Unlock()
82+
83+
// Check if map contains the id for the given label
84+
if subnetid, ok := subnetIDs[subnetName]; ok {
85+
return subnetid, nil
86+
}
87+
// Otherwise, get it from linodego.ListVPCSubnets()
88+
subnets, err := client.ListVPCSubnets(ctx, vpcID, &linodego.ListOptions{})
89+
if err != nil {
90+
return 0, err
91+
}
92+
for _, subnet := range subnets {
93+
if subnet.Label == subnetName {
94+
subnetIDs[subnetName] = subnet.ID
95+
return subnet.ID, nil
96+
}
97+
}
98+
99+
return 0, subnetLookupError{subnetName}
100+
}
101+
62102
// GetVPCIPAddresses returns vpc ip's for given VPC label
63103
func GetVPCIPAddresses(ctx context.Context, client client.Client, vpcName string) ([]linodego.VPCIP, error) {
64104
vpcID, err := GetVPCID(ctx, client, strings.TrimSpace(vpcName))
65105
if err != nil {
66106
return nil, err
67107
}
68-
resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, ""))
108+
109+
resultFilter := ""
110+
111+
// Get subnet ID(s) from name(s) if subnet-names is specified
112+
if Options.SubnetNames != "" {
113+
// Get the IDs and store them
114+
// subnetIDList is a slice of strings for ease of use with resultFilter
115+
subnetNames := strings.Split(Options.SubnetNames, ",")
116+
subnetIDList := []string{}
117+
118+
for _, name := range subnetNames {
119+
// For caching
120+
subnetID, err := GetSubnetID(ctx, client, vpcID, name)
121+
// Don't filter subnets we can't find
122+
if err != nil {
123+
klog.Errorf("subnet %s not found due to error: %v. Skipping.", name, err)
124+
continue
125+
}
126+
127+
// For use with the JSON filter
128+
subnetIDList = append(subnetIDList, strconv.Itoa(subnetID))
129+
}
130+
131+
// Assign the list of IDs to a stringified JSON filter
132+
filter, err := json.Marshal(subnetFilter{SubnetID: strings.Join(subnetIDList, ",")})
133+
if err != nil {
134+
klog.Error("could not create JSON filter for subnet_id")
135+
}
136+
resultFilter = string(filter)
137+
}
138+
139+
resp, err := client.ListVPCIPAddresses(ctx, vpcID, linodego.NewListOptions(0, resultFilter))
69140
if err != nil {
70141
if linodego.ErrHasStatus(err, http.StatusNotFound) {
71142
Mu.Lock()

cloud/linode/vpc_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,80 @@ func TestGetVPCIPAddresses(t *testing.T) {
146146
_, exists := vpcIDs["test10"]
147147
assert.True(t, exists, "test10 key should be present in vpcIDs map")
148148
})
149+
150+
t.Run("vpc id found and ip addresses found with subnet filtering", func(t *testing.T) {
151+
ctrl := gomock.NewController(t)
152+
defer ctrl.Finish()
153+
client := mocks.NewMockClient(ctrl)
154+
sn := Options.SubnetNames
155+
defer func() { Options.SubnetNames = sn }()
156+
Options.SubnetNames = "subnet4"
157+
vpcIDs = map[string]int{"test1": 1}
158+
subnetIDs = map[string]int{"subnet1": 1}
159+
client.EXPECT().ListVPCs(gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPC{{ID: 10, Label: "test10"}}, nil)
160+
client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{{ID: 4, Label: "subnet4"}}, nil)
161+
client.EXPECT().ListVPCIPAddresses(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCIP{}, nil)
162+
_, err := GetVPCIPAddresses(context.TODO(), client, "test10")
163+
assert.NoError(t, err)
164+
_, exists := subnetIDs["subnet4"]
165+
assert.True(t, exists, "subnet4 should be present in subnetIDs map")
166+
})
167+
}
168+
169+
func TestGetSubnetID(t *testing.T) {
170+
t.Run("subnet in cache", func(t *testing.T) {
171+
ctrl := gomock.NewController(t)
172+
defer ctrl.Finish()
173+
client := mocks.NewMockClient(ctrl)
174+
subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3}
175+
got, err := GetSubnetID(context.TODO(), client, 0, "test3")
176+
if err != nil {
177+
t.Errorf("GetSubnetID() error = %v", err)
178+
return
179+
}
180+
if got != subnetIDs["test3"] {
181+
t.Errorf("GetSubnetID() = %v, want %v", got, subnetIDs["test3"])
182+
}
183+
})
184+
185+
t.Run("subnetID not in cache and listVPCSubnets return error", func(t *testing.T) {
186+
ctrl := gomock.NewController(t)
187+
defer ctrl.Finish()
188+
client := mocks.NewMockClient(ctrl)
189+
subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3}
190+
client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{}, errors.New("error"))
191+
got, err := GetSubnetID(context.TODO(), client, 0, "test4")
192+
assert.Error(t, err)
193+
if got != 0 {
194+
t.Errorf("GetSubnetID() = %v, want %v", got, 0)
195+
}
196+
_, exists := subnetIDs["test4"]
197+
assert.False(t, exists, "subnet4 should not be present in subnetIDs")
198+
})
199+
200+
t.Run("subnetID not in cache and listVPCSubnets return nothing", func(t *testing.T) {
201+
ctrl := gomock.NewController(t)
202+
defer ctrl.Finish()
203+
client := mocks.NewMockClient(ctrl)
204+
subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3}
205+
client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{}, nil)
206+
got, err := GetSubnetID(context.TODO(), client, 0, "test4")
207+
assert.ErrorIs(t, err, subnetLookupError{"test4"})
208+
if got != 0 {
209+
t.Errorf("GetSubnetID() = %v, want %v", got, 0)
210+
}
211+
})
212+
213+
t.Run("subnetID not in cache and listVPCSubnets return subnet info", func(t *testing.T) {
214+
ctrl := gomock.NewController(t)
215+
defer ctrl.Finish()
216+
client := mocks.NewMockClient(ctrl)
217+
subnetIDs = map[string]int{"test1": 1, "test2": 2, "test3": 3}
218+
client.EXPECT().ListVPCSubnets(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return([]linodego.VPCSubnet{{ID: 4, Label: "test4"}}, nil)
219+
got, err := GetSubnetID(context.TODO(), client, 0, "test4")
220+
assert.NoError(t, err)
221+
if got != 4 {
222+
t.Errorf("GetSubnetID() = %v, want %v", got, 4)
223+
}
224+
})
149225
}

deploy/chart/templates/daemonset.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ spec:
5555
{{- if and $vpcName $vpcNames }}
5656
{{- fail "Both vpcName and vpcNames are set. Please use only vpcNames." }}
5757
{{- end }}
58+
{{- $subnetNames := .Values.subnetNames }}
59+
{{- if and .Values.routeController .Values.routeController.subnetNames }}
60+
{{- $subnetNames = .Values.routeController.subnetNames }}
61+
{{- end }}
5862
{{- if .Values.routeController }}
5963
- --enable-route-controller=true
6064
{{- if not (or $vpcName $vpcNames) }}
@@ -72,6 +76,9 @@ spec:
7276
{{- with $vpcName }}
7377
- --vpc-name={{ . }}
7478
{{- end }}
79+
{{- with $subnetNames }}
80+
- --subnet-names={{ . }}
81+
{{ end }}
7582
{{- if .Values.sharedIPLoadBalancing }}
7683
{{- with .Values.sharedIPLoadBalancing.bgpNodeSelector }}
7784
- --bgp-node-selector={{ . }}

deploy/chart/values.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,14 @@ tolerations:
7171
# routeController:
7272
# vpcName: <name of VPC> [Deprecated: use vpcNames instead]
7373
# vpcNames: <comma separated list of vpc names>
74+
# subnetNames: <comma separated list of subnet names>
7475
# clusterCIDR: 10.0.0.0/8
7576
# configureCloudRoutes: true
7677

77-
# vpcs that node internal IPs will be assigned from (not required if already specified in routeController)
78+
# vpcs and subnets that node internal IPs will be assigned from (not required if already specified in routeController)
7879
# vpcName: <name of VPC> [Deprecated: use vpcNames instead]
7980
# vpcNames: <comma separated list of vpc names>
81+
# subnetNames: <comma separated list of subnet names>
8082

8183
# Enable Linode token health checker
8284
# tokenHealthChecker: true

e2e/bgp-test/lb-cilium-bgp/chainsaw-test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ apiVersion: chainsaw.kyverno.io/v1alpha1
33
kind: Test
44
metadata:
55
name: cilium-bgp-test
6+
labels:
7+
all:
68
spec:
79
namespace: "cilium-bgp-test"
810
steps:

0 commit comments

Comments
 (0)