-
Notifications
You must be signed in to change notification settings - Fork 613
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
270 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |