Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
fierlion committed Aug 5, 2022
2 parents aaf6d07 + 2b080e8 commit d1c4aa4
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 1 deletion.
2 changes: 1 addition & 1 deletion agent/eni/netwrapper/netwrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ func (utils *utils) FindInterfaceByIndex(index int) (*net.Interface, error) {
}

func (utils *utils) GetAllNetworkInterfaces() ([]net.Interface, error) {
return net.Interfaces()
return utils.getAllNetworkInterfaces()
}
24 changes: 24 additions & 0 deletions agent/eni/netwrapper/netwrapper_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build linux
// +build linux

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package netwrapper

import "net"

// getAllNetworkInterfaces returns all the network adapters.
func (utils *utils) getAllNetworkInterfaces() ([]net.Interface, error) {
return net.Interfaces()
}
90 changes: 90 additions & 0 deletions agent/eni/netwrapper/netwrapper_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//go:build windows
// +build windows

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package netwrapper

import (
"encoding/json"
"net"
"os/exec"

log "github.com/cihub/seelog"
"github.com/pkg/errors"
)

type windowsNetworkAdapter struct {
Name string `json:"ifAlias"`
MacAddress string `json:"MacAddress"`
IfIndex int `json:"ifIndex"`
MTU int `json:"ActiveMaximumTransmissionUnit"`
}

type winAdapters []windowsNetworkAdapter

// Making it visible for unit testing
var execCommand = exec.Command

// getAllNetworkInterfaces returns all the network adapters.
// Unfortunately, as of August 2022, the Win32 IPHelper API seems to be flaky wherein
// the same stops responding in few intermittent cases. Therefore, to avoid failures
// in such cases, we have switched to using Powershell cmdlet for fetching all the interfaces.
func (utils *utils) getAllNetworkInterfaces() ([]net.Interface, error) {
log.Info("Running Powershell command to fetch all the network adapters")

// This command returns a JSON array of all the network interfaces on the instance.
cmd := "Get-NetAdapter | select ifAlias, ifIndex, MacAddress, ActiveMaximumTransmissionUnit | ConvertTo-Json"
out, err := execCommand("Powershell", "-C", cmd).Output()
if err != nil {
return nil, errors.Wrapf(err, "failed to execute Get-NetAdapter for retrieving all interfaces")
}
log.Debugf("Output of Get-NetAdapters Powershell command execution: %s", out)

// Convert the obtained JSON array into an array of structs.
var data winAdapters
err = json.Unmarshal(out, &data)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal the json of network adapters")
}

// This is an error condition since the instance would have at least 1 ENI.
if len(data) == 0 {
return nil, errors.Errorf("invalid number of network adapters found on the instance")
}
log.Debugf("Unmarshalled Powershell output into an array of structs")

// Convert the data into an array of net.Interface
var ifaces []net.Interface
for _, adapter := range data {
mac, err := net.ParseMAC(adapter.MacAddress)
if err != nil {
// Log the error and continue to get as many adapters as possible.
log.Errorf("unable to parse mac address: %v", err)
continue
}

iface := net.Interface{
Index: adapter.IfIndex,
MTU: adapter.MTU,
Name: adapter.Name,
HardwareAddr: mac,
}

ifaces = append(ifaces, iface)
}
log.Debugf("All the interfaces found using Get-NetAdapters are: %v", ifaces)

return ifaces, nil
}
155 changes: 155 additions & 0 deletions agent/eni/netwrapper/netwrapper_windows_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//go:build windows
// +build windows

// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package netwrapper

import (
"fmt"
"net"
"os"
"os/exec"
"testing"

"github.com/stretchr/testify/assert"
)

var (
dummyIfAlias = "Ethernet 2"
dummyIfIndex = 9
dummyMAC = "06-34-BB-C8-64-C5"
networkAdapterValidResult = `[
{
"ifAlias": "%s",
"ifIndex": %d,
"MacAddress": "%s",
"ActiveMaximumTransmissionUnit": 1500
}
]`
networkAdapterExpectedValidResult []net.Interface
networkAdapterInvalidResult = ""
)

func init() {
networkAdapterValidResult = fmt.Sprintf(networkAdapterValidResult, dummyIfAlias, dummyIfIndex, dummyMAC)

parsedMAC, _ := net.ParseMAC(dummyMAC)
networkAdapterExpectedValidResult = []net.Interface{
{
Index: dummyIfIndex,
MTU: 1500,
Name: dummyIfAlias,
HardwareAddr: parsedMAC,
},
}

}

// Supporting methods for testing getAllNetworkInterfaces method.
// mockExecCommandForNetworkAdaptersSuccess returns a valid output from exec cmd.
func mockExecCommandForNetworkAdaptersSuccess(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestNetworkAdaptersProcessSuccess", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}

// TestNetworkAdaptersProcessSuccess is invoked from mockExecCommandForNetworkAdaptersSuccess
// to return the network adapter information.
func TestNetworkAdaptersProcessSuccess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
fmt.Fprintf(os.Stdout, networkAdapterValidResult)
os.Exit(0)
}

// mockExecCommandForNetworkAdaptersInvalidOutput returns an invalid output from exec cmd.
func mockExecCommandForNetworkAdaptersInvalidOutput(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestNetworkAdaptersProcessInvalidOutput", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}

// TestNetworkAdaptersProcessInvalidOutput is invoked from mockExecCommandForNetworkAdaptersInvalidOutput
// to return invalid network adapter information.
func TestNetworkAdaptersProcessInvalidOutput(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
fmt.Fprintf(os.Stdout, networkAdapterInvalidResult)
os.Exit(0)
}

// mockExecCommandForNetworkAdaptersCommandError returns an error during command execution.
func mockExecCommandForNetworkAdaptersCommandError(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestNetworkAdaptersProcessCommandError", "--", command}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd
}

// TestNetworkAdaptersProcessCommandError is invoked from mockExecCommandForNetworkAdaptersCommandError
// to return an error during command execution.
func TestNetworkAdaptersProcessCommandError(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
fmt.Fprintf(os.Stderr, "error during execution")
os.Exit(1)
}

func TestGetAllWindowsNetworkInterfaces(t *testing.T) {
var tests = []struct {
name string
execCmd func(string, ...string) *exec.Cmd
expectedOutput []net.Interface
expectedErrorString string
}{
{
name: "TestGetAllWindowsNetworkInterfacesSuccess",
execCmd: mockExecCommandForNetworkAdaptersSuccess,
expectedOutput: networkAdapterExpectedValidResult,
expectedErrorString: "",
},
{
name: "TestGetAllWindowsNetworkInterfacesInvalidOutput",
execCmd: mockExecCommandForNetworkAdaptersInvalidOutput,
expectedOutput: nil,
expectedErrorString: "failed to unmarshal the json of network adapters: unexpected end of JSON input",
},
{
name: "TestGetAllWindowsNetworkInterfacesCommandError",
execCmd: mockExecCommandForNetworkAdaptersCommandError,
expectedOutput: nil,
expectedErrorString: "failed to execute Get-NetAdapter for retrieving all interfaces: exit status 1",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
execCommand = test.execCmd
out, err := New().GetAllNetworkInterfaces()
assert.Equal(t, test.expectedOutput, out)
if test.expectedErrorString != "" {
assert.EqualError(t, err, test.expectedErrorString)
}
})
}
}

0 comments on commit d1c4aa4

Please sign in to comment.