Skip to content

Commit

Permalink
Update dependent code and unit test on top of previous commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
fenxiong committed Aug 28, 2020
1 parent 18ed18d commit 81d0561
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 95 deletions.
99 changes: 39 additions & 60 deletions agent/api/eni/eni.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ type ENI struct {
IPV4Addresses []*ENIIPV4Address
// IPV6Addresses is the ipv6 address associated with the eni
IPV6Addresses []*ENIIPV6Address
// IPv4SubnetPrefixLength is the VPC IPv4 subnet prefix length of the ENI
IPv4SubnetPrefixLength string
// SubnetGatewayIPV4Address is the IPv4 address of the subnet gateway of the ENI
SubnetGatewayIPV4Address string `json:",omitempty"`
// DomainNameServers specifies the nameserver IP addresses for the eni
Expand All @@ -49,6 +47,14 @@ type ENI struct {
// InterfaceVlanProperties contains information for an interface
// that is supposed to be used as a VLAN device
InterfaceVlanProperties *InterfaceVlanProperties `json:",omitempty"`

// Due to historical reasons, the IPv4 subnet prefix length is sent with IPv4 subnet gateway
// address instead of the ENI's IP addresses. However, CNI plugins and many OS APIs expect it
// the other way around. Instead of doing this conversion all the time for each CNI and TMDS
// request, compute it once on demand and cache it here.
ipv4SubnetPrefixLength string
ipv4SubnetCIDRBlock string
ipv6SubnetCIDRBlock string
}

// InterfaceVlanProperties contains information for an interface that
Expand All @@ -68,7 +74,6 @@ const (
// IPv6SubnetPrefixLength is the IPv6 global unicast address prefix length, consisting of
// global routing prefix and subnet ID lengths as specified in IPv6 addressing architecture
// (RFC 4291 section 2.5.4) and IPv6 Global Unicast Address Format (RFC 3587).
//
// The ACS ENI payload structure does not contain an IPv6 subnet prefix length because "/64" is
// the only allowed length per RFCs above, and the only one that VPC supports.
IPv6SubnetPrefixLength = "64"
Expand All @@ -84,51 +89,23 @@ func (eni *ENI) GetIPV4Addresses() []string {
return addresses
}

// GetPrimaryIPv4Address returns the primary IPv4 address assigned to the ENI.
// TODO: We could delete this function if nobody is calling it.
func (eni *ENI) GetPrimaryIPv4Address() string {
var primaryAddr string
for _, addr := range eni.IPV4Addresses {
if addr.Primary {
primaryAddr = addr.Address
break
}
}

return eni.getIPAddressWithBlockSize(primaryAddr, true)
}

// GetIPV6Addresses returns the list of IPv6 addresses assigned to the ENI.
func (eni *ENI) GetIPV6Addresses() []string {
var addresses []string
for _, addr := range eni.IPV6Addresses {
addresses = append(addresses, eni.getIPAddressWithBlockSize(addr.Address, false))
addresses = append(addresses, addr.Address)
}

return addresses
}

// getIPAddressWithBlockSize appends a block size to the IP address of the ENI.
// For IPv4, the block size is retrieved from the subnet gateway IPv4 address;
// for IPv6, the block size is hardcoded to 64.
func (eni *ENI) getIPAddressWithBlockSize(addr string, ipv4 bool) string {
var blockSize string
if ipv4 {
s := strings.Split(eni.SubnetGatewayIPV4Address, "/")
blockSize = s[1]
} else {
blockSize = "64"
}
return addr + "/" + blockSize
}

// GetPrimaryIPv4AddressWithPrefixLength returns the primary IPv4 address assigned to the ENI with
// its subnet prefix length.
func (eni *ENI) GetPrimaryIPv4AddressWithPrefixLength() string {
var primaryAddr string
for _, addr := range eni.IPV4Addresses {
if addr.Primary {
primaryAddr = addr.Address + "/" + eni.IPv4SubnetPrefixLength
primaryAddr = addr.Address + "/" + eni.GetIPv4SubnetPrefixLength()
break
}
}
Expand All @@ -141,7 +118,7 @@ func (eni *ENI) GetPrimaryIPv4AddressWithPrefixLength() string {
func (eni *ENI) GetIPAddressesWithPrefixLength() []string {
var addresses []string
for _, addr := range eni.IPV4Addresses {
addresses = append(addresses, addr.Address+"/"+eni.IPv4SubnetPrefixLength)
addresses = append(addresses, addr.Address+"/"+eni.GetIPv4SubnetPrefixLength())
}
for _, addr := range eni.IPV6Addresses {
addresses = append(addresses, addr.Address+"/"+IPv6SubnetPrefixLength)
Expand All @@ -150,42 +127,51 @@ func (eni *ENI) GetIPAddressesWithPrefixLength() []string {
return addresses
}

// GetIPv4SubnetPrefixLength returns the IPv4 prefix length of the ENI's subnet.
func (eni *ENI) GetIPv4SubnetPrefixLength() string {
if eni.ipv4SubnetPrefixLength == "" && eni.SubnetGatewayIPV4Address != "" {
eni.ipv4SubnetPrefixLength = strings.Split(eni.SubnetGatewayIPV4Address, "/")[1]
}

return eni.ipv4SubnetPrefixLength
}

// GetIPv4SubnetCIDRBlock returns the IPv4 CIDR block, if any, of the ENI's subnet.
func (eni *ENI) GetIPv4SubnetCIDRBlock() string {
var ipv4SubnetCIDRBlock string

if eni.SubnetGatewayIPV4Address != "" {
if eni.ipv4SubnetCIDRBlock == "" && eni.SubnetGatewayIPV4Address != "" {
_, ipv4Net, err := net.ParseCIDR(eni.SubnetGatewayIPV4Address)
if err == nil {
ipv4SubnetCIDRBlock = ipv4Net.String()
eni.ipv4SubnetCIDRBlock = ipv4Net.String()
}
}

return ipv4SubnetCIDRBlock
return eni.ipv4SubnetCIDRBlock
}

// GetIPv6SubnetCIDRBlock returns the IPv6 CIDR block, if any, of the ENI's subnet.
func (eni *ENI) GetIPv6SubnetCIDRBlock() string {
var ipv6SubnetCIDRBlock string

if len(eni.IPV6Addresses) > 0 {
if eni.ipv6SubnetCIDRBlock == "" && len(eni.IPV6Addresses) > 0 {
ipv6Addr := eni.IPV6Addresses[0].Address + "/" + IPv6SubnetPrefixLength
_, ipv6Net, err := net.ParseCIDR(ipv6Addr)
if err == nil {
ipv6SubnetCIDRBlock = ipv6Net.String()
eni.ipv6SubnetCIDRBlock = ipv6Net.String()
}
}

return ipv6SubnetCIDRBlock
return eni.ipv6SubnetCIDRBlock
}

// GetSubnetGatewayAddresses returns the list of subnet gateway addresses for the ENI.
func (eni *ENI) GetSubnetGatewayAddresses() []string {
s := strings.Split(eni.SubnetGatewayIPV4Address, "/")
return []string{s[0]}
// GetSubnetGatewayIPv4Address returns the subnet gateway IPv4 address for the ENI.
func (eni *ENI) GetSubnetGatewayIPv4Address() string {
var gwAddr string
if eni.SubnetGatewayIPV4Address != "" {
gwAddr = strings.Split(eni.SubnetGatewayIPV4Address, "/")[0]
}

return gwAddr
}

// GetHostname returns the hostname assigned to the ENI.
// GetHostname returns the hostname assigned to the ENI
func (eni *ENI) GetHostname() string {
return eni.PrivateDNSName
}
Expand Down Expand Up @@ -272,12 +258,6 @@ func ENIFromACS(acsENI *ecsacs.ElasticNetworkInterface) (*ENI, error) {
})
}

// Due to historical reasons, the IPv4 subnet prefix length is sent with IPv4 subnet gateway
// address instead of the ENI's IP addresses. However, CNI plugins and many OS APIs expect it
// the other way around. Instead of doing this conversion all the time, compute and store the
// IPv4 subnet prefix length once here. See IPv6SubnetPrefixLength for IPv6 subnet prefix length.
ipv4SubnetPrefixLength := strings.Split(aws.StringValue(acsENI.SubnetGatewayIpv4Address), "/")[1]

// Read ENI association properties.
var interfaceVlanProperties InterfaceVlanProperties

Expand All @@ -291,7 +271,6 @@ func ENIFromACS(acsENI *ecsacs.ElasticNetworkInterface) (*ENI, error) {
MacAddress: aws.StringValue(acsENI.MacAddress),
IPV4Addresses: ipv4Addrs,
IPV6Addresses: ipv6Addrs,
IPv4SubnetPrefixLength: ipv4SubnetPrefixLength,
SubnetGatewayIPV4Address: aws.StringValue(acsENI.SubnetGatewayIpv4Address),
PrivateDNSName: aws.StringValue(acsENI.PrivateDnsName),
InterfaceAssociationProtocol: aws.StringValue(acsENI.InterfaceAssociationProtocol),
Expand All @@ -310,9 +289,9 @@ func ENIFromACS(acsENI *ecsacs.ElasticNetworkInterface) (*ENI, error) {

// ValidateTaskENI validates the ENI information sent from ACS.
func ValidateTaskENI(acsENI *ecsacs.ElasticNetworkInterface) error {
// At least one IP address should be assigned to the ENI.
if len(acsENI.Ipv4Addresses) == 0 && len(acsENI.Ipv6Addresses) == 0 {
return errors.Errorf("eni message validation: no ip addresses in the message")
// At least one IPv4 address should be associated with the ENI.
if len(acsENI.Ipv4Addresses) < 1 {
return errors.Errorf("eni message validation: no ipv4 addresses in the message")
}

if acsENI.SubnetGatewayIpv4Address == nil {
Expand All @@ -322,7 +301,7 @@ func ValidateTaskENI(acsENI *ecsacs.ElasticNetworkInterface) error {
s := strings.Split(gwIPv4Addr, "/")
if len(s) != 2 {
return errors.Errorf(
"eni message validation: invalid subnet gateway ipv4 address %s, expect format <address>/<block size>", gwIPv4Addr)
"eni message validation: invalid subnet gateway ipv4 address %s", gwIPv4Addr)
}

if acsENI.MacAddress == nil {
Expand Down
63 changes: 39 additions & 24 deletions agent/api/eni/eni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ const (
customDNS = "10.0.0.2"
customSearchDomain = "us-west-2.compute.internal"

ipv4 = "1.2.3.4"
ipv4Gw = "1.2.3.1"
ipv4Block = "/20"
ipv4WithBlock = ipv4 + ipv4Block
ipv4GwWithBlock = ipv4Gw + ipv4Block
ipv6 = "abcd:dcba:1234:4321::"
ipv6Block = "/64"
ipv6WithBlock = ipv6 + ipv6Block
ipv4Addr = "1.2.3.4"
ipv4Gw = "1.2.3.1"
ipv4SubnetPrefixLength = "20"
ipv4Subnet = "1.2.0.0"
ipv4AddrWithPrefixLength = ipv4Addr + "/" + ipv4SubnetPrefixLength
ipv4GwWithPrefixLength = ipv4Gw + "/" + ipv4SubnetPrefixLength
ipv4SubnetCIDRBlock = ipv4Subnet + "/" + ipv4SubnetPrefixLength
ipv6Addr = "abcd:dcba:1234:4321::"
ipv6SubnetPrefixLength = "64"
ipv6SubnetCIDRBlock = ipv6Addr + "/" + ipv6SubnetPrefixLength
ipv6AddrWithPrefixLength = ipv6Addr + "/" + ipv6SubnetPrefixLength
)

var (
Expand All @@ -45,15 +48,15 @@ var (
IPV4Addresses: []*ENIIPV4Address{
{
Primary: true,
Address: ipv4,
Address: ipv4Addr,
},
},
IPV6Addresses: []*ENIIPV6Address{
{
Address: ipv6,
Address: ipv6Addr,
},
},
SubnetGatewayIPV4Address: ipv4GwWithBlock,
SubnetGatewayIPV4Address: ipv4GwWithPrefixLength,
}
)

Expand Down Expand Up @@ -90,20 +93,32 @@ func TestIsStandardENI(t *testing.T) {
}
}

func TestGetPrimaryIPv4Address(t *testing.T) {
assert.Equal(t, ipv4WithBlock, testENI.GetPrimaryIPv4Address())
func TestGetIPV4Addresses(t *testing.T) {
assert.Equal(t, []string{ipv4Addr}, testENI.GetIPV4Addresses())
}

func TestGetIPV6Addresses(t *testing.T) {
assert.Equal(t, []string{ipv6WithBlock}, testENI.GetIPV6Addresses())
assert.Equal(t, []string{ipv6Addr}, testENI.GetIPV6Addresses())
}

func TestGetIPAddresses(t *testing.T) {
assert.Equal(t, []string{ipv4WithBlock, ipv6WithBlock}, testENI.GetIPAddresses())
func TestGetIPAddressesWithPrefixLength(t *testing.T) {
assert.Equal(t, []string{ipv4AddrWithPrefixLength, ipv6AddrWithPrefixLength}, testENI.GetIPAddressesWithPrefixLength())
}

func TestGetSubnetGatewayAddresses(t *testing.T) {
assert.Equal(t, []string{ipv4Gw}, testENI.GetSubnetGatewayAddresses())
func TestGetIPv4SubnetPrefixLength(t *testing.T) {
assert.Equal(t, ipv4SubnetPrefixLength, testENI.GetIPv4SubnetPrefixLength())
}

func TestGetIPv4SubnetCIDRBlock(t *testing.T) {
assert.Equal(t, ipv4SubnetCIDRBlock, testENI.GetIPv4SubnetCIDRBlock())
}

func TestGetIPv6SubnetCIDRBlock(t *testing.T) {
assert.Equal(t, ipv6SubnetCIDRBlock, testENI.GetIPv6SubnetCIDRBlock())
}

func TestGetSubnetGatewayIPv4Address(t *testing.T) {
assert.Equal(t, ipv4Gw, testENI.GetSubnetGatewayIPv4Address())
}

func TestENIToString(t *testing.T) {
Expand Down Expand Up @@ -160,7 +175,7 @@ func TestInvalidENIInterfaceVlanPropertyMissing(t *testing.T) {
PrivateAddress: aws.String("ipv4"),
},
},
SubnetGatewayIpv4Address: aws.String(ipv4GwWithBlock),
SubnetGatewayIpv4Address: aws.String(ipv4GwWithPrefixLength),
Ipv6Addresses: []*ecsacs.IPv6AddressAssignment{
{
Address: aws.String("ipv6"),
Expand All @@ -185,7 +200,7 @@ func TestInvalidENIInvalidInterfaceAssociationProtocol(t *testing.T) {
PrivateAddress: aws.String("ipv4"),
},
},
SubnetGatewayIpv4Address: aws.String(ipv4GwWithBlock),
SubnetGatewayIpv4Address: aws.String(ipv4GwWithPrefixLength),
Ipv6Addresses: []*ecsacs.IPv6AddressAssignment{
{
Address: aws.String("ipv6"),
Expand All @@ -199,7 +214,7 @@ func TestInvalidENIInvalidInterfaceAssociationProtocol(t *testing.T) {

func TestInvalidSubnetGatewayAddress(t *testing.T) {
acsENI := getTestACSENI()
acsENI.SubnetGatewayIpv4Address = aws.String(ipv4)
acsENI.SubnetGatewayIpv4Address = aws.String(ipv4Addr)
_, err := ENIFromACS(acsENI)
assert.Error(t, err)
}
Expand All @@ -211,13 +226,13 @@ func getTestACSENI() *ecsacs.ElasticNetworkInterface {
Ipv4Addresses: []*ecsacs.IPv4AddressAssignment{
{
Primary: aws.Bool(true),
PrivateAddress: aws.String(ipv4),
PrivateAddress: aws.String(ipv4Addr),
},
},
SubnetGatewayIpv4Address: aws.String(ipv4GwWithBlock),
SubnetGatewayIpv4Address: aws.String(ipv4GwWithPrefixLength),
Ipv6Addresses: []*ecsacs.IPv6AddressAssignment{
{
Address: aws.String(ipv6)},
Address: aws.String(ipv6Addr)},
},
MacAddress: aws.String("mac"),
DomainNameServers: []*string{aws.String(defaultDNS), aws.String(customDNS)},
Expand Down
10 changes: 5 additions & 5 deletions agent/ecscni/netconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ func NewENINetworkConfig(eni *eni.ENI, cfg *Config) (string, *libcni.NetworkConf
eniConf := ENIConfig{
Type: ECSENIPluginName,
ENIID: eni.ID,
IPAddresses: eni.GetIPAddresses(),
MACAddress: eni.MacAddress,
IPAddresses: eni.GetIPAddressesWithPrefixLength(),
GatewayIPAddresses: []string{eni.GetSubnetGatewayIPv4Address()},
BlockInstanceMetadata: cfg.BlockInstanceMetadata,
GatewayIPAddresses: eni.GetSubnetGatewayAddresses(),
}

networkConfig, err := newNetworkConfig(eniConf, ECSENIPluginName, cfg.MinSupportedCNIVersion)
Expand All @@ -153,10 +153,10 @@ func NewBranchENINetworkConfig(eni *eni.ENI, cfg *Config) (string, *libcni.Netwo
TrunkMACAddress: eni.InterfaceVlanProperties.TrunkInterfaceMacAddress,
BranchVlanID: eni.InterfaceVlanProperties.VlanID,
BranchMACAddress: eni.MacAddress,
IPAddresses: eni.GetIPAddresses(),
GatewayIPAddresses: eni.GetSubnetGatewayAddresses(),
InterfaceType: vpcCNIPluginInterfaceType,
IPAddresses: eni.GetIPAddressesWithPrefixLength(),
GatewayIPAddresses: []string{eni.GetSubnetGatewayIPv4Address()},
BlockInstanceMetadata: cfg.BlockInstanceMetadata,
InterfaceType: vpcCNIPluginInterfaceType,
}

networkConfig, err := newNetworkConfig(eniConf, ECSBranchENIPluginName, cfg.MinSupportedCNIVersion)
Expand Down
12 changes: 6 additions & 6 deletions agent/ecscni/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,14 @@ type ENIConfig struct {
CNIVersion string `json:"cniVersion,omitempty"`
// ENIID is the id of ec2 eni
ENIID string `json:"eni"`
// IPAddresses contains the ip addresses of the ENI.
IPAddresses []string `json:"ip-addresses"`
// MacAddress is the mac address of eni
MACAddress string `json:"mac"`
// BlockInstanceMetadata specifies if InstanceMetadata endpoint should be blocked
BlockInstanceMetadata bool `json:"block-instance-metadata"`
// IPAddresses contains the ip addresses of the ENI.
IPAddresses []string `json:"ip-addresses"`
// GatewayIPAddresses specifies the addresses of the subnet gateway for the ENI.
GatewayIPAddresses []string `json:"gateway-ip-addresses"`
// BlockInstanceMetadata specifies if InstanceMetadata endpoint should be blocked
BlockInstanceMetadata bool `json:"block-instance-metadata"`
}

// AppMeshConfig contains all the information needed to invoke the app mesh plugin
Expand Down Expand Up @@ -170,10 +170,10 @@ type BranchENIConfig struct {
IPAddresses []string `json:"ipAddresses"`
// GatewayIPAddresses contains the IP addresses of the default gateway in the subnet.
GatewayIPAddresses []string `json:"gatewayIPAddresses"`
// InterfaceType is the type of the interface to connect the branch ENI to
InterfaceType string `json:"interfaceType,omitempty"`
// BlockInstanceMetdata specifies if InstanceMetadata endpoint should be blocked.
BlockInstanceMetadata bool `json:"blockInstanceMetadata"`
// InterfaceType is the type of the interface to connect the branch ENI to
InterfaceType string `json:"interfaceType,omitempty"`
}

// Config contains all the information to set up the container namespace using
Expand Down

0 comments on commit 81d0561

Please sign in to comment.