Skip to content

Commit 6110a5e

Browse files
M00nF1shTimothy-Dougherty
authored andcommitted
support IPv6 IP Pods during AZ detection (kubernetes-sigs#2375)
1 parent 114bbc1 commit 6110a5e

File tree

9 files changed

+780
-211
lines changed

9 files changed

+780
-211
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require (
1818
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6
1919
gomodules.xyz/jsonpatch/v2 v2.2.0
2020
helm.sh/helm/v3 v3.6.1
21+
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 // indirect
2122
k8s.io/api v0.21.2
2223
k8s.io/apimachinery v0.21.2
2324
k8s.io/cli-runtime v0.21.2

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNE
200200
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
201201
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
202202
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
203+
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
203204
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
204205
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
205206
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
@@ -850,6 +851,10 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
850851
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
851852
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
852853
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
854+
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
855+
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
856+
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=
857+
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
853858
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
854859
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
855860
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1162,6 +1167,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
11621167
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
11631168
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
11641169
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
1170+
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw=
1171+
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8=
11651172
k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU=
11661173
k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y=
11671174
k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU=

pkg/networking/utils.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package networking
2+
3+
import "inet.af/netaddr"
4+
5+
// TODO: replace netaddr package with built-in netip package once golang 1.18 released: https://pkg.go.dev/net/netip@master#Prefix
6+
7+
// ParseCIDRs will parse CIDRs in string format into parsed IPPrefix
8+
func ParseCIDRs(cidrs []string) ([]netaddr.IPPrefix, error) {
9+
var ipPrefixes []netaddr.IPPrefix
10+
for _, cidr := range cidrs {
11+
ipPrefix, err := netaddr.ParseIPPrefix(cidr)
12+
if err != nil {
13+
return nil, err
14+
}
15+
ipPrefixes = append(ipPrefixes, ipPrefix)
16+
}
17+
return ipPrefixes, nil
18+
}
19+
20+
// IsIPWithinCIDRs checks whether specific IP is in IPv4 CIDR or IPv6 CIDRs.
21+
func IsIPWithinCIDRs(ip netaddr.IP, cidrs []netaddr.IPPrefix) bool {
22+
for _, cidr := range cidrs {
23+
if cidr.Contains(ip) {
24+
return true
25+
}
26+
}
27+
return false
28+
}

pkg/networking/utils_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package networking
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"github.com/stretchr/testify/assert"
6+
"inet.af/netaddr"
7+
"testing"
8+
)
9+
10+
func TestParseCIDRs(t *testing.T) {
11+
type args struct {
12+
cidrs []string
13+
}
14+
tests := []struct {
15+
name string
16+
args args
17+
want []netaddr.IPPrefix
18+
wantErr error
19+
}{
20+
{
21+
name: "has one valid CIDR",
22+
args: args{
23+
cidrs: []string{"192.168.5.100/16"},
24+
},
25+
want: []netaddr.IPPrefix{
26+
netaddr.MustParseIPPrefix("192.168.5.100/16"),
27+
},
28+
},
29+
{
30+
name: "has multiple valid CIDRs",
31+
args: args{
32+
cidrs: []string{"192.168.5.100/16", "10.100.0.0/16"},
33+
},
34+
want: []netaddr.IPPrefix{
35+
netaddr.MustParseIPPrefix("192.168.5.100/16"),
36+
netaddr.MustParseIPPrefix("10.100.0.0/16"),
37+
},
38+
},
39+
{
40+
name: "has one invalid CIDR",
41+
args: args{
42+
cidrs: []string{"192.168.5.100/16", "10.100.0.0"},
43+
},
44+
wantErr: errors.New("netaddr.ParseIPPrefix(\"10.100.0.0\"): no '/'"),
45+
},
46+
{
47+
name: "empty CIDRs",
48+
args: args{
49+
cidrs: []string{},
50+
},
51+
want: nil,
52+
},
53+
{
54+
name: "nil CIDRs",
55+
args: args{
56+
cidrs: nil,
57+
},
58+
want: nil,
59+
},
60+
}
61+
for _, tt := range tests {
62+
t.Run(tt.name, func(t *testing.T) {
63+
got, err := ParseCIDRs(tt.args.cidrs)
64+
if tt.wantErr != nil {
65+
assert.EqualError(t, err, tt.wantErr.Error())
66+
} else {
67+
assert.NoError(t, err)
68+
assert.Equal(t, tt.want, got)
69+
}
70+
})
71+
}
72+
}
73+
74+
func TestIsIPWithinCIDRs(t *testing.T) {
75+
type args struct {
76+
ip netaddr.IP
77+
cidrs []netaddr.IPPrefix
78+
}
79+
tests := []struct {
80+
name string
81+
args args
82+
want bool
83+
}{
84+
{
85+
name: "ipv4 address within CIDRs",
86+
args: args{
87+
ip: netaddr.MustParseIP("192.168.1.42"),
88+
cidrs: []netaddr.IPPrefix{
89+
netaddr.MustParseIPPrefix("10.100.0.0/16"),
90+
netaddr.MustParseIPPrefix("192.168.0.0/16"),
91+
netaddr.MustParseIPPrefix("2600:1f14:f8c:2700::/56"),
92+
},
93+
},
94+
want: true,
95+
},
96+
{
97+
name: "ipv4 address not within CIDRs",
98+
args: args{
99+
ip: netaddr.MustParseIP("172.16.1.42"),
100+
cidrs: []netaddr.IPPrefix{
101+
netaddr.MustParseIPPrefix("10.100.0.0/16"),
102+
netaddr.MustParseIPPrefix("192.168.0.0/16"),
103+
netaddr.MustParseIPPrefix("2600:1f14:f8c:2700::/56"),
104+
},
105+
},
106+
want: false,
107+
},
108+
{
109+
name: "ipv6 address within CIDRs",
110+
args: args{
111+
ip: netaddr.MustParseIP("2600:1f14:f8c:2701:a740::"),
112+
cidrs: []netaddr.IPPrefix{
113+
netaddr.MustParseIPPrefix("10.100.0.0/16"),
114+
netaddr.MustParseIPPrefix("2700:1f14:f8c:2700::/56"),
115+
netaddr.MustParseIPPrefix("2600:1f14:f8c:2700::/56"),
116+
},
117+
},
118+
want: true,
119+
},
120+
{
121+
name: "ipv6 address not within CIDRs",
122+
args: args{
123+
ip: netaddr.MustParseIP("2800:1f14:f8c:2701:a740::"),
124+
cidrs: []netaddr.IPPrefix{
125+
netaddr.MustParseIPPrefix("10.100.0.0/16"),
126+
netaddr.MustParseIPPrefix("2700:1f14:f8c:2700::/56"),
127+
netaddr.MustParseIPPrefix("2600:1f14:f8c:2700::/56"),
128+
},
129+
},
130+
want: false,
131+
},
132+
}
133+
for _, tt := range tests {
134+
t.Run(tt.name, func(t *testing.T) {
135+
got := IsIPWithinCIDRs(tt.args.ip, tt.args.cidrs)
136+
assert.Equal(t, tt.want, got)
137+
})
138+
}
139+
}

pkg/networking/vpc_info_provider.go

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,56 @@ import (
1414

1515
const defaultVPCInfoCacheTTL = 10 * time.Minute
1616

17+
type VPCInfo ec2sdk.Vpc
18+
19+
// AssociatedIPv4CIDRs computes associated IPv4CIDRs for VPC.
20+
func (vpc *VPCInfo) AssociatedIPv4CIDRs() []string {
21+
var ipv4CIDRs []string
22+
for _, cidr := range vpc.CidrBlockAssociationSet {
23+
if awssdk.StringValue(cidr.CidrBlockState.State) != ec2sdk.VpcCidrBlockStateCodeAssociated {
24+
continue
25+
}
26+
ipv4CIDRs = append(ipv4CIDRs, awssdk.StringValue(cidr.CidrBlock))
27+
}
28+
return ipv4CIDRs
29+
}
30+
31+
// AssociatedIPv6CIDRs computes associated IPv6CIDRs for VPC.
32+
func (vpc *VPCInfo) AssociatedIPv6CIDRs() []string {
33+
var ipv6CIDRs []string
34+
for _, cidr := range vpc.Ipv6CidrBlockAssociationSet {
35+
if awssdk.StringValue(cidr.Ipv6CidrBlockState.State) != ec2sdk.VpcCidrBlockStateCodeAssociated {
36+
continue
37+
}
38+
ipv6CIDRs = append(ipv6CIDRs, awssdk.StringValue(cidr.Ipv6CidrBlock))
39+
}
40+
return ipv6CIDRs
41+
}
42+
43+
type FetchVPCInfoOptions struct {
44+
// whether to ignore cache and reload VPC Info from AWS directly.
45+
ReloadIgnoringCache bool
46+
}
47+
48+
// ApplyOptions applies FetchVPCInfoOption options
49+
func (opts *FetchVPCInfoOptions) ApplyOptions(options ...FetchVPCInfoOption) {
50+
for _, option := range options {
51+
option(opts)
52+
}
53+
}
54+
55+
type FetchVPCInfoOption func(opts *FetchVPCInfoOptions)
56+
57+
// FetchVPCInfoWithoutCache is an option that sets the ReloadIgnoringCache to true.
58+
func FetchVPCInfoWithoutCache() FetchVPCInfoOption {
59+
return func(opts *FetchVPCInfoOptions) {
60+
opts.ReloadIgnoringCache = true
61+
}
62+
}
63+
1764
// VPCInfoProvider is responsible for providing VPC info.
1865
type VPCInfoProvider interface {
19-
FetchVPCInfo(ctx context.Context, vpcID string) (*ec2sdk.Vpc, error)
66+
FetchVPCInfo(ctx context.Context, vpcID string, opts ...FetchVPCInfoOption) (VPCInfo, error)
2067
}
2168

2269
// NewDefaultVPCInfoProvider constructs new defaultVPCInfoProvider.
@@ -42,48 +89,55 @@ type defaultVPCInfoProvider struct {
4289
logger logr.Logger
4390
}
4491

45-
func (p *defaultVPCInfoProvider) FetchVPCInfo(ctx context.Context, vpcID string) (*ec2sdk.Vpc, error) {
46-
if vpcInfo := p.fetchVPCInfoFromCache(); vpcInfo != nil {
47-
return vpcInfo, nil
92+
// FetchVPCInfo fetches VPC info for vpcID.
93+
func (p *defaultVPCInfoProvider) FetchVPCInfo(ctx context.Context, vpcID string, opts ...FetchVPCInfoOption) (VPCInfo, error) {
94+
fetchOpts := FetchVPCInfoOptions{
95+
ReloadIgnoringCache: false,
96+
}
97+
fetchOpts.ApplyOptions(opts...)
98+
99+
if !fetchOpts.ReloadIgnoringCache {
100+
if vpcInfo, exists := p.fetchVPCInfoFromCache(vpcID); exists {
101+
return vpcInfo, nil
102+
}
48103
}
49104

50-
// Fetch VPC info from the AWS API and cache response before returning.
51105
vpcInfo, err := p.fetchVPCInfoFromAWS(ctx, vpcID)
52106
if err != nil {
53-
return nil, err
107+
return VPCInfo{}, err
54108
}
55-
p.saveVPCInfoToCache(vpcInfo)
56-
109+
p.saveVPCInfoToCache(vpcID, vpcInfo)
57110
return vpcInfo, nil
58111
}
59112

60-
func (p *defaultVPCInfoProvider) fetchVPCInfoFromCache() *ec2sdk.Vpc {
113+
// fetchVPCInfoFromCache fetches VPC info for vpcID from cache.
114+
func (p *defaultVPCInfoProvider) fetchVPCInfoFromCache(vpcID string) (VPCInfo, bool) {
61115
p.vpcInfoCacheMutex.RLock()
62116
defer p.vpcInfoCacheMutex.RUnlock()
63117

64-
if rawCacheItem, exists := p.vpcInfoCache.Get("vpcInfo"); exists {
65-
return rawCacheItem.(*ec2sdk.Vpc)
118+
if rawCacheItem, exists := p.vpcInfoCache.Get(vpcID); exists {
119+
return rawCacheItem.(VPCInfo), true
66120
}
67-
68-
return nil
121+
return VPCInfo{}, false
69122
}
70123

71-
func (p *defaultVPCInfoProvider) saveVPCInfoToCache(vpcInfo *ec2sdk.Vpc) {
124+
// saveVPCInfoToCache saves VPC info for vpcID into cache.
125+
func (p *defaultVPCInfoProvider) saveVPCInfoToCache(vpcID string, vpcInfo VPCInfo) {
72126
p.vpcInfoCacheMutex.Lock()
73127
defer p.vpcInfoCacheMutex.Unlock()
74128

75-
p.vpcInfoCache.Set("vpcInfo", vpcInfo, p.vpcInfoCacheTTL)
129+
p.vpcInfoCache.Set(vpcID, vpcInfo, p.vpcInfoCacheTTL)
76130
}
77131

78132
// fetchVPCInfoFromAWS will fetch VPC info from the AWS API.
79-
func (p *defaultVPCInfoProvider) fetchVPCInfoFromAWS(ctx context.Context, vpcID string) (*ec2sdk.Vpc, error) {
133+
func (p *defaultVPCInfoProvider) fetchVPCInfoFromAWS(ctx context.Context, vpcID string) (VPCInfo, error) {
80134
req := &ec2sdk.DescribeVpcsInput{
81135
VpcIds: []*string{awssdk.String(vpcID)},
82136
}
83137
resp, err := p.ec2Client.DescribeVpcsWithContext(ctx, req)
84138
if err != nil {
85-
return nil, err
139+
return VPCInfo{}, err
86140
}
87141

88-
return resp.Vpcs[0], nil
142+
return VPCInfo(*resp.Vpcs[0]), nil
89143
}

pkg/networking/vpc_info_provider_mocks.go

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

0 commit comments

Comments
 (0)