Skip to content

Commit d557101

Browse files
committed
Abstrating away network interface, making the code robust over APIs that may throw in restricted environments
1 parent 48e3a8c commit d557101

File tree

5 files changed

+171
-16
lines changed

5 files changed

+171
-16
lines changed

src/Common/Commands.Common/Commands.Common.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
<Compile Include="Serialization\LegacyAzureSubscription.cs" />
129129
<Compile Include="Serialization\ModelConversionExtensions.cs" />
130130
<Compile Include="Utilities\FileUtilities.cs" />
131+
<Compile Include="Utilities\INetworkHelper.cs" />
131132
<Compile Include="Utilities\JsonUtilities.cs" />
132133
<Compile Include="AzureDataCmdlet.cs" />
133134
<Compile Include="AzurePSCmdlet.cs" />
@@ -148,6 +149,7 @@
148149
<Compile Include="Utilities\ConversionUtilities.cs" />
149150
<Compile Include="DebugStreamTraceListener.cs" />
150151
<Compile Include="Utilities\GeneralUtilities.cs" />
152+
<Compile Include="Utilities\NetworkHelper.cs" />
151153
<Compile Include="Utilities\PowerShellUtilities.cs" />
152154
<Compile Include="RecordingTracingInterceptor.cs" />
153155
<Compile Include="ClientCreatedArgs.cs" />

src/Common/Commands.Common/MetricHelper.cs

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.ApplicationInsights;
1616
using Microsoft.ApplicationInsights.DataContracts;
1717
using Microsoft.ApplicationInsights.Extensibility;
18+
using Microsoft.WindowsAzure.Commands.Common.Utilities;
1819
using Microsoft.WindowsAzure.Commands.Utilities.Common;
1920
using Newtonsoft.Json;
2021
using Newtonsoft.Json.Serialization;
@@ -30,6 +31,7 @@ namespace Microsoft.WindowsAzure.Commands.Common
3031
{
3132
public class MetricHelper
3233
{
34+
protected INetworkHelper _networkHelper;
3335
private const int FlushTimeoutInMilli = 5000;
3436

3537
/// <summary>
@@ -52,31 +54,47 @@ public class MetricHelper
5254
/// <summary>
5355
/// Lock used to synchronize mutation of the tracing interceptors.
5456
/// </summary>
55-
private readonly object _lock = new object();
57+
private static readonly object _lock = new object();
5658

5759
private static string _hashMacAddress = string.Empty;
5860

59-
private static string HashMacAddress
61+
public string HashMacAddress
6062
{
6163
get
6264
{
63-
if (_hashMacAddress == string.Empty)
65+
lock(_hashMacAddress)
6466
{
65-
var macAddress = NetworkInterface.GetAllNetworkInterfaces()?
66-
.FirstOrDefault(nic => nic != null &&
67-
nic.OperationalStatus == OperationalStatus.Up &&
68-
nic.GetPhysicalAddress() != null &&
69-
!string.IsNullOrWhiteSpace(nic.GetPhysicalAddress().ToString()))?
70-
.GetPhysicalAddress()?.ToString();
71-
_hashMacAddress = string.IsNullOrWhiteSpace(macAddress) ? null : GenerateSha256HashString(macAddress)?.Replace("-", string.Empty)?.ToLowerInvariant();
67+
if (_hashMacAddress == string.Empty)
68+
{
69+
_hashMacAddress = null;
70+
71+
try
72+
{
73+
var macAddress = _networkHelper.GetMACAddress();
74+
_hashMacAddress = string.IsNullOrWhiteSpace(macAddress)
75+
? null : GenerateSha256HashString(macAddress)?.Replace("-", string.Empty)?.ToLowerInvariant();
76+
}
77+
catch
78+
{
79+
// ignore exceptions in getting the network address
80+
}
81+
}
82+
83+
return _hashMacAddress;
7284
}
73-
74-
return _hashMacAddress;
7585
}
86+
87+
// Add test hook to reset
88+
set { lock(_lock) { _hashMacAddress = value; } }
7689
}
7790

78-
public MetricHelper()
91+
public MetricHelper() : this(new NetworkHelper())
7992
{
93+
}
94+
95+
public MetricHelper(INetworkHelper network)
96+
{
97+
_networkHelper = network;
8098
#if DEBUG
8199
if (TestMockSupport.RunningMocked)
82100
{
@@ -266,11 +284,21 @@ public static string GenerateSha256HashString(string originInput)
266284
return string.Empty;
267285
}
268286

269-
using (var sha256 = new SHA256CryptoServiceProvider())
287+
string result = null;
288+
try
270289
{
271-
var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(originInput));
272-
return BitConverter.ToString(bytes);
290+
using (var sha256 = new SHA256CryptoServiceProvider())
291+
{
292+
var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(originInput));
293+
result = BitConverter.ToString(bytes);
294+
}
295+
}
296+
catch
297+
{
298+
// do not throw if CryptoProvider is not provided
273299
}
300+
301+
return result;
274302
}
275303

276304
/// <summary>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.WindowsAzure.Commands.Common.Utilities
16+
{
17+
/// <summary>
18+
/// Environment helper to allow mocking the network interface
19+
/// </summary>
20+
public interface INetworkHelper
21+
{
22+
/// <summary>
23+
/// Get the mac address of the default nic
24+
/// </summary>
25+
/// <returns>A string respresnting the MAC address of the defualt nic,
26+
/// or null if none is found</returns>
27+
string GetMACAddress();
28+
}
29+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System.Linq;
16+
using System.Net.NetworkInformation;
17+
18+
namespace Microsoft.WindowsAzure.Commands.Common.Utilities
19+
{
20+
/// <summary>
21+
/// Network helper using the .Net NetworkInterface library
22+
/// </summary>
23+
public class NetworkHelper : INetworkHelper
24+
{
25+
/// <summary>
26+
/// Get the MAC address of the default NIC, or null if none can be found
27+
/// </summary>
28+
/// <returns>The MAC address of the defautl nic, or null if noen is found</returns>
29+
public string GetMACAddress()
30+
{
31+
return NetworkInterface.GetAllNetworkInterfaces()?
32+
.FirstOrDefault(nic => nic != null &&
33+
nic.OperationalStatus == OperationalStatus.Up &&
34+
!string.IsNullOrWhiteSpace(nic.GetPhysicalAddress()?.ToString()))?
35+
.GetPhysicalAddress()?.ToString();
36+
}
37+
}
38+
}

src/ResourceManager/Profile/Commands.Profile.Test/TelemetryTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Microsoft.Azure.Commands.ScenarioTest;
1818
using Microsoft.WindowsAzure.Commands.Common;
1919
using Microsoft.WindowsAzure.Commands.Common.Test.Mocks;
20+
using Microsoft.WindowsAzure.Commands.Common.Utilities;
2021
using Microsoft.WindowsAzure.Commands.ScenarioTest;
2122
using Microsoft.WindowsAzure.Commands.Utilities.Common;
2223
using Moq;
@@ -37,6 +38,63 @@ public void HashOfNullOrWhitespaceValueReturnsEmptyString()
3738
Assert.Equal(string.Empty, MetricHelper.GenerateSha256HashString(" "));
3839
}
3940

41+
[Fact]
42+
[Trait(Category.AcceptanceType, Category.CheckIn)]
43+
public void HashofNullNetworkMACAddressIsNull()
44+
{
45+
Mock<INetworkHelper> helper = new Mock<INetworkHelper>();
46+
helper.Setup((f) => f.GetMACAddress()).Returns(() => null);
47+
var metricHelper = new MetricHelper(helper.Object);
48+
metricHelper.HashMacAddress = string.Empty;
49+
try
50+
{
51+
Assert.Null(metricHelper.HashMacAddress);
52+
helper.Verify((f) => f.GetMACAddress(), Times.Once);
53+
}
54+
finally
55+
{
56+
metricHelper.HashMacAddress = string.Empty;
57+
}
58+
}
59+
60+
[Fact]
61+
[Trait(Category.AcceptanceType, Category.CheckIn)]
62+
public void HashofEmptyNetworkMACAddressIsNull()
63+
{
64+
Mock<INetworkHelper> helper = new Mock<INetworkHelper>();
65+
helper.Setup((f) => f.GetMACAddress()).Returns(string.Empty);
66+
var metricHelper = new MetricHelper(helper.Object);
67+
metricHelper.HashMacAddress = string.Empty;
68+
try
69+
{
70+
Assert.Null(metricHelper.HashMacAddress);
71+
helper.Verify((f) => f.GetMACAddress(), Times.Once);
72+
}
73+
finally
74+
{
75+
metricHelper.HashMacAddress = string.Empty;
76+
}
77+
}
78+
79+
[Fact]
80+
[Trait(Category.AcceptanceType, Category.CheckIn)]
81+
public void HashMacAddressOnThrowIsNull()
82+
{
83+
Mock<INetworkHelper> helper = new Mock<INetworkHelper>();
84+
helper.Setup((f) => f.GetMACAddress()).Throws(new InvalidOperationException("This exception should be handled"));
85+
var metricHelper = new MetricHelper(helper.Object);
86+
metricHelper.HashMacAddress = string.Empty;
87+
try
88+
{
89+
Assert.Null(metricHelper.HashMacAddress);
90+
helper.Verify((f) => f.GetMACAddress(), Times.Once);
91+
}
92+
finally
93+
{
94+
metricHelper.HashMacAddress = string.Empty;
95+
}
96+
}
97+
4098
[Fact]
4199
[Trait(Category.AcceptanceType, Category.CheckIn)]
42100
public void HashOfValidValueSucceeds()

0 commit comments

Comments
 (0)