diff --git a/builds/e2e/templates/e2e-run.yaml b/builds/e2e/templates/e2e-run.yaml index ad8b36830f4..b2e594667dd 100644 --- a/builds/e2e/templates/e2e-run.yaml +++ b/builds/e2e/templates/e2e-run.yaml @@ -68,6 +68,8 @@ steps: $filter += '&Category!=NestedEdgeOnly' } + $filter += '&FullyQualifiedName~ValidateMetrics' + #Dotnet SDK 3.1.415 package on Centos doesn't allow dotnet to be accessed via sudo command due to Path issues. Use the below workaround for centos only. if ('$(artifactName)'.Contains('centos')) { diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/AgentEventIds.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/AgentEventIds.cs index 19d1f57c951..3968af09010 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/AgentEventIds.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/AgentEventIds.cs @@ -14,7 +14,6 @@ public struct AgentEventIds public const int ModuleLifecycleCommandFactory = EventIdStart + 1000; public const int EdgeAgentConnection = EventIdStart + 1100; public const int ModuleClient = EventIdStart + 1200; - public const int RetryingServiceClient = EventIdStart + 1300; public const int OrderedRetryPlanRunner = EventIdStart + 1400; public const int ModuleManagementHttpClient = EventIdStart + 1500; public const int ModuleIdentityLifecycleManager = EventIdStart + 1600; diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Diagnostics/Microsoft.Azure.Devices.Edge.Agent.Diagnostics.csproj b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Diagnostics/Microsoft.Azure.Devices.Edge.Agent.Diagnostics.csproj index bcd142cb1c0..90d5b5a8777 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Diagnostics/Microsoft.Azure.Devices.Edge.Agent.Diagnostics.csproj +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Diagnostics/Microsoft.Azure.Devices.Edge.Agent.Diagnostics.csproj @@ -9,7 +9,7 @@ - + diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/IServiceClient.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/IServiceClient.cs deleted file mode 100644 index 642f87ac747..00000000000 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/IServiceClient.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -namespace Microsoft.Azure.Devices.Edge.Agent.IoTHub -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - - public interface IServiceClient : IDisposable - { - Task> GetModules(); - - Task GetModule(string moduleId); - - Task CreateModules(IEnumerable identities); - - Task UpdateModules(IEnumerable modules); - - Task RemoveModules(IEnumerable identities); - } -} diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/Microsoft.Azure.Devices.Edge.Agent.IoTHub.csproj b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/Microsoft.Azure.Devices.Edge.Agent.IoTHub.csproj index 93dbae98df8..0e7512bb4fe 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/Microsoft.Azure.Devices.Edge.Agent.IoTHub.csproj +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/Microsoft.Azure.Devices.Edge.Agent.IoTHub.csproj @@ -9,8 +9,7 @@ - - + diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/ModuleIdentityLifecycleManager.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/ModuleIdentityLifecycleManager.cs deleted file mode 100644 index e989ffc616d..00000000000 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/ModuleIdentityLifecycleManager.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -namespace Microsoft.Azure.Devices.Edge.Agent.IoTHub -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.Azure.Devices.Edge.Agent.Core; - using Microsoft.Azure.Devices.Edge.Util; - using Microsoft.Extensions.Logging; - - public class ModuleIdentityLifecycleManager : IModuleIdentityLifecycleManager - { - readonly IServiceClient serviceClient; - readonly string iothubHostName; - readonly string deviceId; - readonly string gatewayHostName; - - public ModuleIdentityLifecycleManager( - IServiceClient serviceClient, - string iothubHostName, - string deviceId, - string gatewayHostName) - { - this.serviceClient = Preconditions.CheckNotNull(serviceClient, nameof(serviceClient)); - this.iothubHostName = Preconditions.CheckNonWhiteSpace(iothubHostName, nameof(iothubHostName)); - this.deviceId = Preconditions.CheckNonWhiteSpace(deviceId, nameof(deviceId)); - this.gatewayHostName = Preconditions.CheckNonWhiteSpace(gatewayHostName, nameof(gatewayHostName)); - } - - // Modules in IoTHub can be created in one of two ways - 1. single deployment: - // This can be done using the registry manager's - // ApplyConfigurationContentOnDeviceAsync method. This call will create all - // modules in the provided deployment json, but does not create the module - // credentials. After a single deployment, GetModuleIdentitiesAsync will update - // such modules in the service by calling UpdateModuleAsync, prompting the - // service to create and return credentials for them. The single deployment also - // stamps each module with its twin (provided by the deployment json) at module - // creation time. 2. at-scale deployment: This can be done via the portal on the - // Edge blade. This type of deployment waits for a module identity to be - // created, before stamping it with its twin. In this type of deployment, the - // EdgeAgent needs to create the modules identities. This is also handled in - // GetModuleIdentitiesAsync. When the deployment detects that a module has been - // created, it stamps it with the deployed twin. The service creates the - // $edgeAgent and $edgeHub twin when it creates the Edge Device, so their twins - // are always available for stamping with either a single deployment or at-scale - // deployment. - public async Task> GetModuleIdentitiesAsync(ModuleSet desired, ModuleSet current) - { - Diff diff = desired.Diff(current); - if (diff.IsEmpty) - { - return ImmutableDictionary.Empty; - } - - try - { - IImmutableDictionary moduleIdentities = await this.GetModuleIdentitiesAsync(diff); - return moduleIdentities; - } - catch (Exception ex) - { - Events.ErrorGettingModuleIdentities(ex); - return ImmutableDictionary.Empty; - } - } - - async Task> GetModuleIdentitiesAsync(Diff diff) - { - // System modules have different module names and identity names. We need to convert module names to module identity names - // and vice versa, to make sure the right values are being used. - // TODO - This will fail if the user adds modules with the same module name as a system module - for example a module called - // edgeHub. We might have to catch such cases and flag them as error (or handle them in some other way). - IEnumerable updatedModuleIdentites = diff.AddedOrUpdated.Select(m => ModuleIdentityHelper.GetModuleIdentityName(m.Name)); - IEnumerable removedModuleIdentites = diff.Removed.Select(m => ModuleIdentityHelper.GetModuleIdentityName(m)); - - List modules = (await this.serviceClient.GetModules()).ToList(); - - ImmutableDictionary modulesDict = modules.ToImmutableDictionary(p => p.Id); - - IEnumerable createIdentities = updatedModuleIdentites.Where(m => !modulesDict.ContainsKey(m)); - IEnumerable removeIdentities = removedModuleIdentites.Where( - m => modulesDict.ContainsKey(m) - && string.Equals(modulesDict.GetValueOrDefault(m).ManagedBy, Constants.ModuleIdentityEdgeManagedByValue, StringComparison.OrdinalIgnoreCase)); - - // Update any identities that don't have SAS auth type or where the keys are null (this will happen for single device deployments, - // where the identities of modules are created, but the auth keys are not set). - IEnumerable updateIdentities = modules.Where( - m => m.Authentication == null - || m.Authentication.Type != AuthenticationType.Sas - || m.Authentication.SymmetricKey == null - || (m.Authentication.SymmetricKey.PrimaryKey == null && m.Authentication.SymmetricKey.SecondaryKey == null)) - .Select( - m => - { - m.Authentication = new AuthenticationMechanism - { - Type = AuthenticationType.Sas - }; - return m; - }).ToList(); - - List updatedModulesIndentity = (await this.UpdateServiceModulesIdentityAsync(removeIdentities, createIdentities, updateIdentities)).ToList(); - ImmutableDictionary updatedDict = updatedModulesIndentity.ToImmutableDictionary(p => p.Id); - - IEnumerable moduleIdentities; - - moduleIdentities = updatedModulesIndentity.Concat(modules.Where(p => !updatedDict.ContainsKey(p.Id))).Select( - p => - { - string connectionString = this.GetModuleConnectionString(p); - return new ModuleIdentity( - this.iothubHostName, - this.deviceId, - p.Id, - new ConnectionStringCredentials(connectionString)); - }); - - return moduleIdentities.ToImmutableDictionary(m => ModuleIdentityHelper.GetModuleName(m.ModuleId)); - } - - string GetModuleConnectionString(Module module) - { - if (module.Authentication.Type != AuthenticationType.Sas) - { - throw new ArgumentException($"Authentication type {module.Authentication.Type} is not supported."); - } - - ModuleConnectionStringBuilder.ModuleConnectionString moduleConnectionString = new ModuleConnectionStringBuilder(this.iothubHostName, this.deviceId) - .Create(module.Id) - .WithSharedAccessKey(module.Authentication.SymmetricKey.PrimaryKey); - - return module.Id.Equals(Constants.EdgeHubModuleIdentityName, StringComparison.OrdinalIgnoreCase) - ? moduleConnectionString - : moduleConnectionString.WithGatewayHostName(this.gatewayHostName); - } - - async Task UpdateServiceModulesIdentityAsync(IEnumerable removeIdentities, IEnumerable createIdentities, IEnumerable updateIdentities) - { - await this.serviceClient.RemoveModules(removeIdentities); - - Module[] identities = (await Task.WhenAll( - this.serviceClient.CreateModules(createIdentities), - this.serviceClient.UpdateModules(updateIdentities))) - .Aggregate((l1, l2) => l1.Concat(l2).ToArray()); - - return identities; - } - - static class Events - { - const int IdStart = AgentEventIds.ModuleIdentityLifecycleManager; - static readonly ILogger Log = Logger.Factory.CreateLogger(); - - enum EventIds - { - ErrorGettingModuleIdentities = IdStart, - } - - public static void ErrorGettingModuleIdentities(Exception ex) - { - Log.LogDebug((int)EventIds.ErrorGettingModuleIdentities, ex, "Error getting module identities."); - } - } - } -} diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/RetryingServiceClient.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/RetryingServiceClient.cs deleted file mode 100644 index 1c85ac50462..00000000000 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/RetryingServiceClient.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -namespace Microsoft.Azure.Devices.Edge.Agent.IoTHub -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using Microsoft.Azure.Devices.Common.Exceptions; - using Microsoft.Azure.Devices.Edge.Agent.Core; - using Microsoft.Azure.Devices.Edge.Util; - using Microsoft.Azure.Devices.Edge.Util.TransientFaultHandling; - using Microsoft.Extensions.Logging; - - public class RetryingServiceClient : IServiceClient - { - readonly IServiceClient underlying; - - static readonly ITransientErrorDetectionStrategy TransientDetectionStrategy = new DeviceClientRetryStrategy(); - static readonly RetryStrategy TransientRetryStrategy = new ExponentialBackoff(3, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(4)); - - public RetryingServiceClient(IServiceClient underlying) - { - this.underlying = Preconditions.CheckNotNull(underlying, nameof(underlying)); - } - - public void Dispose() => this.underlying.Dispose(); - - public Task> GetModules() => this.ExecuteWithRetry(() => this.underlying.GetModules(), nameof(this.underlying.GetModules)); - - public Task GetModule(string moduleId) => - this.ExecuteWithRetry(() => this.underlying.GetModule(moduleId), nameof(this.underlying.GetModule)); - - public Task CreateModules(IEnumerable identities) => - this.ExecuteWithRetry(() => this.underlying.CreateModules(identities), nameof(this.underlying.CreateModules)); - - public Task UpdateModules(IEnumerable modules) => - this.ExecuteWithRetry(() => this.underlying.UpdateModules(modules), nameof(this.underlying.UpdateModules)); - - public Task RemoveModules(IEnumerable identities) => - this.ExecuteWithRetry( - async () => - { - await this.underlying.RemoveModules(identities); - return 0; - }, - nameof(this.underlying.RemoveModules)); - - Task ExecuteWithRetry(Func> func, string action) - { - var transientRetryPolicy = new RetryPolicy(TransientDetectionStrategy, TransientRetryStrategy); - transientRetryPolicy.Retrying += (_, args) => Events.ActionFailed(args, action); - return transientRetryPolicy.ExecuteAsync(func); - } - - class DeviceClientRetryStrategy : ITransientErrorDetectionStrategy - { - static readonly ISet NonTransientExceptions = new HashSet - { - typeof(ArgumentException), - typeof(UnauthorizedException) - }; - - public bool IsTransient(Exception ex) => !NonTransientExceptions.Contains(ex.GetType()); - } - - static class Events - { - const int IdStart = AgentEventIds.RetryingServiceClient; - static readonly ILogger Log = Logger.Factory.CreateLogger(); - - enum EventIds - { - Retrying = IdStart - } - - public static void ActionFailed(RetryingEventArgs args, string action) - { - Log.LogDebug((int)EventIds.Retrying, $"Service Client threw exception {args.LastException} on action {action}. Current retry count {args.CurrentRetryCount}."); - } - } - } -} diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/ServiceClient.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/ServiceClient.cs deleted file mode 100644 index 449ab1395c3..00000000000 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.IoTHub/ServiceClient.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -namespace Microsoft.Azure.Devices.Edge.Agent.IoTHub -{ - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.Azure.Devices.Edge.Agent.Core; - using Microsoft.Azure.Devices.Edge.Util; - - public class ServiceClient : IServiceClient - { - readonly RegistryManager rm; - readonly string deviceId; - - public ServiceClient(string deviceConnectionString, string deviceId) - { - Preconditions.CheckNonWhiteSpace(deviceConnectionString, nameof(deviceConnectionString)); - this.deviceId = Preconditions.CheckNonWhiteSpace(deviceId, nameof(deviceId)); - this.rm = RegistryManager.CreateFromConnectionString(deviceConnectionString); - } - - public void Dispose() - { - this.rm.Dispose(); - } - - public Task> GetModules() - { - return this.rm.GetModulesOnDeviceAsync(this.deviceId); - } - - public Task GetModule(string moduleId) - { - Preconditions.CheckNonWhiteSpace(moduleId, nameof(moduleId)); - return this.rm.GetModuleAsync(this.deviceId, moduleId); - } - - public Task CreateModules(IEnumerable identities) - { - return Task.WhenAll( - identities.Select( - moduleId => this.rm.AddModuleAsync( - new Module(this.deviceId, moduleId) - { - ManagedBy = Constants.ModuleIdentityEdgeManagedByValue - }))); - } - - public Task UpdateModules(IEnumerable modules) - { - IList> updateTasks = new List>(); - foreach (Module module in modules) - { - updateTasks.Add(this.rm.UpdateModuleAsync(module)); - } - - return Task.WhenAll(updateTasks); - } - - public Task RemoveModules(IEnumerable identities) - { - return Task.WhenAll(identities.Select(moduleId => this.rm.RemoveModuleAsync(this.deviceId, moduleId))); - } - } -} diff --git a/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test.csproj b/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test.csproj index 64ef231855f..e5ecc703d1f 100644 --- a/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test.csproj +++ b/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test.csproj @@ -9,6 +9,7 @@ + diff --git a/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/ModuleIdentityLifecycleManagerTest.cs b/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/ModuleIdentityLifecycleManagerTest.cs deleted file mode 100644 index 383860adb2a..00000000000 --- a/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/ModuleIdentityLifecycleManagerTest.cs +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -namespace Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using Microsoft.Azure.Devices.Edge.Agent.Core; - using Microsoft.Azure.Devices.Edge.Agent.Core.Test; - using Microsoft.Azure.Devices.Edge.Util.Test.Common; - using Moq; - using Newtonsoft.Json; - using Xunit; - - public class ModuleIdentityLifecycleManagerTest - { - static readonly ConfigurationInfo DefaultConfigurationInfo = new ConfigurationInfo("1"); - static readonly IDictionary EnvVars = new Dictionary(); - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithEmptyDiff_ShouldReturnEmptyIdentities() - { - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string gatewayHostName = "localhost"; - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Empty, ModuleSet.Empty); - - Assert.False(modulesIdentities.Any()); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithUpdatedModules_NoServiceIdentity_ShouldCreateIdentities() - { - const string Name = "test-filters"; - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string moduleSharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primaryModuleAccessKey")); - string gatewayHostName = "localhost"; - - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(ImmutableList.Empty.AsEnumerable())); - - var createdModuleIdentity = new Module("device1", Name); - createdModuleIdentity.Authentication = new AuthenticationMechanism(); - createdModuleIdentity.Authentication.Type = AuthenticationType.Sas; - createdModuleIdentity.Authentication.SymmetricKey.PrimaryKey = moduleSharedAccessKey; - Module[] updatedServiceIdentities = { createdModuleIdentity }; - - // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. - // ReSharper disable PossibleMultipleEnumeration - serviceClient.Setup(sc => sc.CreateModules(It.Is>(m => m.Count() == 1 && m.First() == Name))).Returns(Task.FromResult(updatedServiceIdentities)); - // ReSharper restore PossibleMultipleEnumeration - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); - - // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. - // ReSharper disable PossibleMultipleEnumeration - serviceClient.Verify(sc => sc.CreateModules(It.Is>(m => m.Count() == 1 && m.First() == Name)), Times.Once()); - // ReSharper restore PossibleMultipleEnumeration - Assert.True(modulesIdentities.Count == 1); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithUpdatedModules_NoAccessKey_ShouldUpdateIdentities() - { - const string Name = "test-filters"; - - var serviceModuleIdentity = new Module("device1", Name); - serviceModuleIdentity.Authentication = new AuthenticationMechanism(); - serviceModuleIdentity.Authentication.Type = AuthenticationType.Sas; - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string sharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); - string gatewayHostName = "localhost"; - - Module[] serviceIdentities = { serviceModuleIdentity }; - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.UpdateModules(It.IsAny>())).Callback( - (IEnumerable modules) => - { - foreach (Module m in modules) - { - m.Authentication.SymmetricKey = new SymmetricKey(); - m.Authentication.SymmetricKey.PrimaryKey = sharedAccessKey; - } - }).Returns(Task.FromResult(serviceIdentities)); - - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); - - serviceClient.Verify(sc => sc.UpdateModules(It.IsAny>()), Times.Once()); - Assert.True(modulesIdentities.Count == 1); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithUpdatedModules_AuthTypeNull_ShouldUpdateIdentities() - { - const string Name = "test-filters"; - - var serviceModuleIdentity = new Module("device1", Name); - serviceModuleIdentity.Authentication = null; - - var serviceClient = new Mock(); - string hostname = "hostname.fake.com"; - string deviceId = "deviceId"; - string sharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); - string gatewayHostName = "localhost"; - - Module[] serviceIdentities = { serviceModuleIdentity }; - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.UpdateModules(It.IsAny>())).Callback( - (IEnumerable modules) => - { - foreach (Module m in modules) - { - m.Authentication.SymmetricKey = new SymmetricKey(); - m.Authentication.SymmetricKey.PrimaryKey = sharedAccessKey; - } - }).Returns(Task.FromResult(serviceIdentities)); - - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); - - serviceClient.Verify(sc => sc.UpdateModules(It.IsAny>()), Times.Once()); - Assert.True(modulesIdentities.Count() == 1); - var creds = modulesIdentities.First().Value.Credentials as ConnectionStringCredentials; - Assert.NotNull(creds); - IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(creds.ConnectionString); - Assert.NotNull(connectionString.SharedAccessKey); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithUpdatedModules_AuthTypeNotSas_ShouldUpdateIdentities() - { - const string Name = "test-filters"; - string primaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); - string secondaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("secondarySymmetricKey")); - - var serviceModuleIdentity = new Module("device1", Name); - serviceModuleIdentity.Authentication = new AuthenticationMechanism(); - serviceModuleIdentity.Authentication.Type = AuthenticationType.CertificateAuthority; - var thumbprint = new X509Thumbprint(); - thumbprint.PrimaryThumbprint = primaryKey; - thumbprint.SecondaryThumbprint = secondaryKey; - - serviceModuleIdentity.Authentication.X509Thumbprint = thumbprint; - - var serviceClient = new Mock(); - string hostname = "hostname.fake.com"; - string deviceId = "deviceId"; - string gatewayHostName = "localhost"; - - Module[] serviceIdentities = { serviceModuleIdentity }; - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.UpdateModules(It.IsAny>())).Callback( - (IEnumerable modules) => - { - foreach (Module m in modules) - { - m.Authentication.SymmetricKey = new SymmetricKey(); - m.Authentication.SymmetricKey.PrimaryKey = primaryKey; - } - }).Returns(Task.FromResult(serviceIdentities)); - - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); - - serviceClient.Verify(sc => sc.UpdateModules(It.IsAny>()), Times.Once()); - Assert.True(modulesIdentities.Count() == 1); - var creds = modulesIdentities.First().Value.Credentials as ConnectionStringCredentials; - Assert.NotNull(creds); - IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(creds.ConnectionString); - Assert.NotNull(connectionString.SharedAccessKey); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithUpdatedModules_SymmKeyNull_ShouldUpdateIdentities() - { - const string Name = "test-filters"; - - var serviceModuleIdentity = new Module("device1", Name); - serviceModuleIdentity.Authentication = new AuthenticationMechanism(); - serviceModuleIdentity.Authentication.Type = AuthenticationType.Sas; - serviceModuleIdentity.Authentication.SymmetricKey = null; - - var serviceClient = new Mock(); - string hostname = "hostname.fake.com"; - string deviceId = "deviceId"; - string sharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primaryAccessKey")); - string gatewayHostName = "localhost"; - - Module[] serviceIdentities = { serviceModuleIdentity }; - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.UpdateModules(It.IsAny>())).Callback( - (IEnumerable modules) => - { - foreach (Module m in modules) - { - m.Authentication.SymmetricKey = new SymmetricKey(); - m.Authentication.SymmetricKey.PrimaryKey = sharedAccessKey; - } - }).Returns(Task.FromResult(serviceIdentities)); - - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); - - serviceClient.Verify(sc => sc.UpdateModules(It.IsAny>()), Times.Once()); - Assert.True(modulesIdentities.Count == 1); - var creds = modulesIdentities.First().Value.Credentials as ConnectionStringCredentials; - Assert.NotNull(creds); - IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(creds.ConnectionString); - Assert.NotNull(connectionString.SharedAccessKey); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithUpdatedModules_HasAccessKey_ShouldNotUpdate() - { - const string Name = "test-filters"; - - var serviceModuleIdentity = new Module("device1", Name); - serviceModuleIdentity.Authentication = new AuthenticationMechanism(); - var symmetricKey = new SymmetricKey(); - symmetricKey.PrimaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); - symmetricKey.SecondaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("secondarySymmetricKey")); - - serviceModuleIdentity.Authentication.SymmetricKey = symmetricKey; - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string gatewayHostName = "localhost"; - - Module[] serviceIdentities = { serviceModuleIdentity }; - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.CreateModules(It.Is>(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); - serviceClient.Setup(sc => sc.UpdateModules(It.Is>(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); - - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(module), ModuleSet.Empty); - - serviceClient.Verify(sc => sc.CreateModules(It.Is>(m => !m.Any())), Times.Once); - serviceClient.Verify(sc => sc.UpdateModules(It.Is>(m => !m.Any())), Times.Once); - Assert.True(modulesIdentities.Count == 1); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithRemovedModules_ShouldRemove() - { - const string Name = "test-filters"; - // Use Json to create module because managedBy property can't has private set on Module object - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Synthetic symmetric keys used in tests")] - const string ModuleJson = "{\"moduleId\":\"test-filters\",\"deviceId\":\"device1\",\"authentication\":{\"symmetricKey\":{\"primaryKey\":\"cHJpbWFyeVN5bW1ldHJpY0tleQ == \",\"secondaryKey\":\"c2Vjb25kYXJ5U3ltbWV0cmljS2V5\"},\"x509Thumbprint\":{\"primaryThumbprint\":null,\"secondaryThumbprint\":null},\"type\":\"sas\"},\"managedBy\":\"iotEdge\"}"; - var serviceModuleIdentity = JsonConvert.DeserializeObject(ModuleJson); - var currentModule = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string gatewayHostName = "localhost"; - - var serviceIdentities = new List(); - serviceIdentities.Add(serviceModuleIdentity); - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. - // ReSharper disable PossibleMultipleEnumeration - serviceClient.Setup(sc => sc.RemoveModules(It.Is>(m => m.Count() == 1 && m.First() == Name))).Returns(Task.FromResult(ImmutableList.Empty.AsEnumerable())); - // ReSharper restore PossibleMultipleEnumeration - await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Empty, ModuleSet.Create(new IModule[] { currentModule })); - - // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. - // ReSharper disable PossibleMultipleEnumeration - serviceClient.Verify(sc => sc.RemoveModules(It.Is>(m => m.Count() == 1 && m.First() == Name)), Times.Once); - // ReSharper restore PossibleMultipleEnumeration - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_WithRemovedModules_NotEdgeHubManaged_ShouldNotRemove() - { - const string Name = "test-filters"; - // Use Json to create module because managedBy property can't has private set on Module object - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Synthetic symmetric keys used in tests")] - const string ModuleJson = "{\"moduleId\":\"test-filters\",\"deviceId\":\"device1\",\"authentication\":{\"symmetricKey\":{\"primaryKey\":\"cHJpbWFyeVN5bW1ldHJpY0tleQ == \",\"secondaryKey\":\"c2Vjb25kYXJ5U3ltbWV0cmljS2V5\"},\"x509Thumbprint\":{\"primaryThumbprint\":null,\"secondaryThumbprint\":null},\"type\":\"sas\"},\"managedBy\":null}"; - var serviceModuleIdentity = JsonConvert.DeserializeObject(ModuleJson); - var currentModule = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string gatewayHostName = "localhost"; - - var serviceIdentities = new List(); - serviceIdentities.Add(serviceModuleIdentity); - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.RemoveModules(It.IsAny>())).Returns(Task.FromResult(ImmutableList.Empty.AsEnumerable())); - - await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Empty, ModuleSet.Create(new IModule[] { currentModule })); - - serviceClient.Verify(sc => sc.RemoveModules(It.Is>(m => m.Count() == 0)), Times.Once); - } - - [Fact] - [Unit] - public async Task TestGetModulesIdentity_EdgeHubTest() - { - const string Name = "filter"; - - var symmetricKey = new SymmetricKey(); - symmetricKey.PrimaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); - symmetricKey.SecondaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("secondarySymmetricKey")); - - var serviceModuleFilterIdentity = new Module("device1", Name) - { - Authentication = new AuthenticationMechanism - { - SymmetricKey = symmetricKey - } - }; - - var serviceModuleEdgeHubIdentity = new Module("device1", Constants.EdgeHubModuleIdentityName) - { - Authentication = new AuthenticationMechanism - { - SymmetricKey = symmetricKey - } - }; - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string gatewayHostName = "localhost"; - - Module[] serviceIdentities = { serviceModuleFilterIdentity, serviceModuleEdgeHubIdentity }; - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); - serviceClient.Setup(sc => sc.CreateModules(It.Is>(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); - serviceClient.Setup(sc => sc.UpdateModules(It.Is>(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); - - var testMod = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - var edgeHubMod = new TestModule(Constants.EdgeHubModuleName, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.Always, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(testMod, edgeHubMod), ModuleSet.Empty); - - serviceClient.Verify(sc => sc.CreateModules(It.Is>(m => !m.Any())), Times.Once); - serviceClient.Verify(sc => sc.UpdateModules(It.Is>(m => !m.Any())), Times.Once); - Assert.True(modulesIdentities.Count == 2); - IModuleIdentity edgeHubModuleIdentity = modulesIdentities[Constants.EdgeHubModuleName]; - Assert.NotNull(edgeHubModuleIdentity); - var edgeHubCreds = edgeHubModuleIdentity.Credentials as ConnectionStringCredentials; - Assert.NotNull(edgeHubCreds); - Assert.Equal("HostName=hostname;DeviceId=deviceId;ModuleId=$edgeHub;SharedAccessKey=cHJpbWFyeVN5bW1ldHJpY0tleQ==", edgeHubCreds.ConnectionString); - - IModuleIdentity testModuleIdentity = modulesIdentities[Name]; - Assert.NotNull(testModuleIdentity); - var testModCreds = testModuleIdentity.Credentials as ConnectionStringCredentials; - Assert.NotNull(testModCreds); - Assert.Equal("HostName=hostname;DeviceId=deviceId;ModuleId=filter;SharedAccessKey=cHJpbWFyeVN5bW1ldHJpY0tleQ==;GatewayHostName=localhost", testModCreds.ConnectionString); - } - - [Fact] - [Unit] - public async Task TestGetModuleIdentities_WhenOffline_ReturnsEmptyList() - { - const string Name = "test-filters"; - - var serviceClient = new Mock(); - string hostname = "hostname"; - string deviceId = "deviceId"; - string moduleSharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primaryModuleAccessKey")); - string gatewayHostName = "localhost"; - - serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(ImmutableList.Empty.AsEnumerable())); - - // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. - // ReSharper disable PossibleMultipleEnumeration - serviceClient.Setup(sc => sc.CreateModules(It.Is>(m => m.Count() == 1 && m.First() == Name))).ThrowsAsync(new InvalidOperationException()); - // ReSharper restore PossibleMultipleEnumeration - var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); - - IImmutableDictionary modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) - .GetModuleIdentitiesAsync(ModuleSet.Create(module), ModuleSet.Empty); - - serviceClient.VerifyAll(); - Assert.False(modulesIdentities.Any()); - } - } -} diff --git a/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/RetryingServiceClientTest.cs b/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/RetryingServiceClientTest.cs deleted file mode 100644 index 5b06b539752..00000000000 --- a/edge-agent/test/Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test/RetryingServiceClientTest.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -namespace Microsoft.Azure.Devices.Edge.Agent.IoTHub.Test -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using Microsoft.Azure.Devices.Common.Exceptions; - using Microsoft.Azure.Devices.Edge.Util.Test.Common; - using Moq; - using Xunit; - - [Unit] - public class RetryingServiceClientTest - { - [Fact] - public async Task GetModulesRetryTest() - { - // Arrange - var underlying = new Mock(); - underlying.SetupSequence(c => c.GetModules()) - .Throws(new InvalidOperationException()) - .Returns(Task.FromResult(Enumerable.Empty())); - var serviceClient = new RetryingServiceClient(underlying.Object); - - // Act - IEnumerable modules = await serviceClient.GetModules(); - - // Assert - Assert.NotNull(modules); - } - - [Fact] - public async Task GetModuleRetryTest() - { - // Arrange - var underlying = new Mock(); - underlying.SetupSequence(c => c.GetModule(It.IsAny())) - .Throws(new InvalidOperationException()) - .Returns(Task.FromResult(new Module("d1", "m1"))); - var serviceClient = new RetryingServiceClient(underlying.Object); - - // Act - Module module = await serviceClient.GetModule("m1"); - - // Assert - Assert.NotNull(module); - Assert.Equal("d1", module.DeviceId); - Assert.Equal("m1", module.Id); - } - - [Fact] - public async Task CreateModulesRetryTest() - { - // Arrange - var underlying = new Mock(); - underlying.SetupSequence(c => c.CreateModules(It.IsAny>())) - .Throws(new InvalidOperationException()) - .Returns(Task.FromResult(new[] { new Module("d1", "m1") })); - var serviceClient = new RetryingServiceClient(underlying.Object); - - // Act - Module[] modules = await serviceClient.CreateModules(new List { "m1" }); - - // Assert - Assert.NotNull(modules); - Assert.Single(modules); - Assert.Equal("m1", modules[0].Id); - } - - [Fact] - public async Task GetModuleThrowsTest() - { - // Arrange - var underlying = new Mock(); - underlying.Setup(c => c.GetModules()) - .ThrowsAsync(new InvalidOperationException()); - var serviceClient = new RetryingServiceClient(underlying.Object); - - // Act / assert - await Assert.ThrowsAsync(() => serviceClient.GetModules()); - - // Assert - underlying.Verify(u => u.GetModules(), Times.Exactly(4)); - } - - [Fact] - public async Task GetModulesRetryUnauthorizedThrowsTest() - { - // Arrange - var underlying = new Mock(); - underlying.SetupSequence(c => c.GetModules()) - .Throws(new UnauthorizedException("Unauthorized!")) - .Returns(Task.FromResult(Enumerable.Empty())); - var serviceClient = new RetryingServiceClient(underlying.Object); - - // Act / Assert - await Assert.ThrowsAsync(() => serviceClient.GetModules()); - } - } -} diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/AmqpErrorMapper.cs b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/AmqpErrorMapper.cs index 9c7a1a0eb89..da0fe0386dd 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/AmqpErrorMapper.cs +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/AmqpErrorMapper.cs @@ -8,12 +8,9 @@ namespace Microsoft.Azure.Devices.Edge.Hub.Amqp public static class AmqpErrorMapper { // Error codes - static readonly AmqpSymbol MessageLockLostError = AmqpConstants.Vendor + ":message-lock-lost"; - static readonly AmqpSymbol IotHubNotFoundError = AmqpConstants.Vendor + ":iot-hub-not-found-error"; static readonly AmqpSymbol ArgumentError = AmqpConstants.Vendor + ":argument-error"; static readonly AmqpSymbol DeviceContainerThrottled = AmqpConstants.Vendor + ":device-container-throttled"; static readonly AmqpSymbol PreconditionFailed = AmqpConstants.Vendor + ":precondition-failed"; - static readonly AmqpSymbol IotHubSuspended = AmqpConstants.Vendor + ":iot-hub-suspended"; // Maps the ErrorCode of an IotHubException into an appropriate AMQP error code public static AmqpSymbol GetErrorCondition(ErrorCode errorCode) @@ -28,26 +25,15 @@ public static AmqpSymbol GetErrorCondition(ErrorCode errorCode) return ArgumentError; case ErrorCode.IotHubUnauthorizedAccess: - case ErrorCode.IotHubUnauthorized: return AmqpErrorCode.UnauthorizedAccess; case ErrorCode.DeviceNotFound: return AmqpErrorCode.NotFound; - case ErrorCode.DeviceMessageLockLost: - return MessageLockLostError; - case ErrorCode.IotHubQuotaExceeded: case ErrorCode.DeviceMaximumQueueDepthExceeded: - case ErrorCode.IotHubMaxCbsTokenExceeded: return AmqpErrorCode.ResourceLimitExceeded; - case ErrorCode.IotHubSuspended: - return IotHubSuspended; - - case ErrorCode.IotHubNotFound: - return IotHubNotFoundError; - case ErrorCode.PreconditionFailed: return PreconditionFailed; diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/Microsoft.Azure.Devices.Edge.Hub.Amqp.csproj b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/Microsoft.Azure.Devices.Edge.Hub.Amqp.csproj index 2a77e673390..63a1cf7a5a3 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/Microsoft.Azure.Devices.Edge.Hub.Amqp.csproj +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Amqp/Microsoft.Azure.Devices.Edge.Hub.Amqp.csproj @@ -9,7 +9,7 @@ - + diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.CloudProxy/Microsoft.Azure.Devices.Edge.Hub.CloudProxy.csproj b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.CloudProxy/Microsoft.Azure.Devices.Edge.Hub.CloudProxy.csproj index bbbe9a8a3bf..86c4ab0e8b2 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.CloudProxy/Microsoft.Azure.Devices.Edge.Hub.CloudProxy.csproj +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.CloudProxy/Microsoft.Azure.Devices.Edge.Hub.CloudProxy.csproj @@ -9,7 +9,7 @@ - + diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/Microsoft.Azure.Devices.Edge.Hub.Core.csproj b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/Microsoft.Azure.Devices.Edge.Hub.Core.csproj index 8dde4921229..1920542d72e 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/Microsoft.Azure.Devices.Edge.Hub.Core.csproj +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/Microsoft.Azure.Devices.Edge.Hub.Core.csproj @@ -18,7 +18,7 @@ - + diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Http/Microsoft.Azure.Devices.Edge.Hub.Http.csproj b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Http/Microsoft.Azure.Devices.Edge.Hub.Http.csproj index 876bf784d7e..66dd096706e 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Http/Microsoft.Azure.Devices.Edge.Hub.Http.csproj +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Http/Microsoft.Azure.Devices.Edge.Hub.Http.csproj @@ -13,7 +13,7 @@ - + diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs index 91fda161feb..95e0fd04d8a 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs @@ -20,6 +20,7 @@ namespace Microsoft.Azure.Devices.Edge.Hub.Service using Microsoft.Azure.Devices.Edge.Storage; using Microsoft.Azure.Devices.Edge.Util; using Microsoft.Azure.Devices.Edge.Util.Metrics; + using Microsoft.Azure.Devices.Logging; using Microsoft.Azure.Devices.Routing.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -28,6 +29,7 @@ public class Program { const int DefaultShutdownWaitPeriod = 60; const SslProtocols DefaultSslProtocols = SslProtocols.Tls12; + static ConsoleEventListener consoleEventListener; public static int Main() { @@ -52,6 +54,7 @@ static async Task MainAsync(IConfigurationRoot configuration) } ILogger logger = Logger.Factory.CreateLogger("EdgeHub"); + Program.consoleEventListener = new ConsoleEventListener("Microsoft-Azure-Devices-Device-Client", logger); try { diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Routing.Core/checkpointers/Checkpointer.cs b/edge-hub/core/src/Microsoft.Azure.Devices.Routing.Core/checkpointers/Checkpointer.cs index 895daf5ad40..df1d90758c5 100644 --- a/edge-hub/core/src/Microsoft.Azure.Devices.Routing.Core/checkpointers/Checkpointer.cs +++ b/edge-hub/core/src/Microsoft.Azure.Devices.Routing.Core/checkpointers/Checkpointer.cs @@ -11,6 +11,7 @@ namespace Microsoft.Azure.Devices.Routing.Core.Checkpointers using Microsoft.Azure.Devices.Edge.Util; using Microsoft.Azure.Devices.Edge.Util.Concurrency; using Microsoft.Azure.Devices.Edge.Util.Metrics; + using Microsoft.Azure.Devices.Logging; using Microsoft.Extensions.Logging; using static System.FormattableString; using EdgeMetrics = Microsoft.Azure.Devices.Edge.Util.Metrics.Metrics; diff --git a/edge-modules/SimulatedTemperatureSensor/SimulatedTemperatureSensor.csproj b/edge-modules/SimulatedTemperatureSensor/SimulatedTemperatureSensor.csproj index 6327183f256..683e05c7ae5 100644 --- a/edge-modules/SimulatedTemperatureSensor/SimulatedTemperatureSensor.csproj +++ b/edge-modules/SimulatedTemperatureSensor/SimulatedTemperatureSensor.csproj @@ -14,7 +14,7 @@ - + diff --git a/edge-modules/functions/binding/src/Microsoft.Azure.WebJobs.Extensions.EdgeHub/Microsoft.Azure.WebJobs.Extensions.EdgeHub.csproj b/edge-modules/functions/binding/src/Microsoft.Azure.WebJobs.Extensions.EdgeHub/Microsoft.Azure.WebJobs.Extensions.EdgeHub.csproj index dad6db84025..a82c02a94dc 100644 --- a/edge-modules/functions/binding/src/Microsoft.Azure.WebJobs.Extensions.EdgeHub/Microsoft.Azure.WebJobs.Extensions.EdgeHub.csproj +++ b/edge-modules/functions/binding/src/Microsoft.Azure.WebJobs.Extensions.EdgeHub/Microsoft.Azure.WebJobs.Extensions.EdgeHub.csproj @@ -33,7 +33,7 @@ - + diff --git a/edge-modules/iotedge-diagnostics-dotnet/IotedgeDiagnosticsDotnet.csproj b/edge-modules/iotedge-diagnostics-dotnet/IotedgeDiagnosticsDotnet.csproj index b3d055326c7..2c612637878 100644 --- a/edge-modules/iotedge-diagnostics-dotnet/IotedgeDiagnosticsDotnet.csproj +++ b/edge-modules/iotedge-diagnostics-dotnet/IotedgeDiagnosticsDotnet.csproj @@ -14,7 +14,7 @@ - + diff --git a/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ConsoleEventListener.cs b/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ConsoleEventListener.cs new file mode 100644 index 00000000000..79699305254 --- /dev/null +++ b/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ConsoleEventListener.cs @@ -0,0 +1,87 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.Tracing; +using System.Linq; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.Devices.Logging +{ + /// + /// Prints SDK events to Console output - the log level is set to TRACE + /// + public sealed class ConsoleEventListener : EventListener + { + private readonly string[] _eventFilters; + private readonly object _lock = new object(); + private readonly ILogger _logger; + + public ConsoleEventListener(string filter, ILogger logger) + { + _eventFilters = new string[1]; + _eventFilters[0] = filter ?? throw new ArgumentNullException(nameof(filter)); + _logger = logger; + + InitializeEventSources(); + } + + public ConsoleEventListener(string[] filters) + { + _eventFilters = filters ?? throw new ArgumentNullException(nameof(filters)); + if (_eventFilters.Length == 0) throw new ArgumentException("Filters cannot be empty", nameof(filters)); + + foreach (string filter in _eventFilters) + { + if (string.IsNullOrWhiteSpace(filter)) + { + throw new ArgumentNullException(nameof(filters)); + } + } + + InitializeEventSources(); + } + + private void InitializeEventSources() + { + foreach (EventSource source in EventSource.GetSources()) + { + EnableEvents(source, EventLevel.LogAlways); + } + } + + protected override void OnEventSourceCreated(EventSource eventSource) + { + base.OnEventSourceCreated(eventSource); + EnableEvents( + eventSource, + EventLevel.LogAlways +#if !NET451 + , EventKeywords.All +#endif + ); + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + if (_eventFilters == null) return; + + lock (_lock) + { + if (_eventFilters.Any(ef => eventData.EventSource.Name.StartsWith(ef, StringComparison.Ordinal))) + { + string eventIdent; +#if NET451 + // net451 doesn't have EventName, so we'll settle for EventId + eventIdent = eventData.EventId.ToString(CultureInfo.InvariantCulture); +#else + eventIdent = eventData.EventName; +#endif + string text = $"[{eventData.EventSource.Name}-{eventIdent}]{(eventData.Payload != null ? $" ({string.Join(", ", eventData.Payload)})." : "")}"; + this._logger.LogInformation(text); + } + } + } + } +} diff --git a/samples/dotnet/EdgeDownstreamDevice/EdgeDownstreamDevice.csproj b/samples/dotnet/EdgeDownstreamDevice/EdgeDownstreamDevice.csproj index f320c4a44e5..0f343809457 100644 --- a/samples/dotnet/EdgeDownstreamDevice/EdgeDownstreamDevice.csproj +++ b/samples/dotnet/EdgeDownstreamDevice/EdgeDownstreamDevice.csproj @@ -9,6 +9,6 @@ - + diff --git a/samples/dotnet/EdgeX509AuthDownstreamDevice/EdgeX509AuthDownstreamDevice.csproj b/samples/dotnet/EdgeX509AuthDownstreamDevice/EdgeX509AuthDownstreamDevice.csproj index 34811d0e81d..f3d29043ba8 100644 --- a/samples/dotnet/EdgeX509AuthDownstreamDevice/EdgeX509AuthDownstreamDevice.csproj +++ b/samples/dotnet/EdgeX509AuthDownstreamDevice/EdgeX509AuthDownstreamDevice.csproj @@ -9,7 +9,7 @@ - + diff --git a/smoke/IotEdgeQuickstart/IotEdgeQuickstart.csproj b/smoke/IotEdgeQuickstart/IotEdgeQuickstart.csproj index fb46ff97ea0..2d3a4614076 100644 --- a/smoke/IotEdgeQuickstart/IotEdgeQuickstart.csproj +++ b/smoke/IotEdgeQuickstart/IotEdgeQuickstart.csproj @@ -10,7 +10,7 @@ - + diff --git a/smoke/LeafDevice/LeafDevice.csproj b/smoke/LeafDevice/LeafDevice.csproj index 7f18aeb3d6b..c86edc7cfb1 100644 --- a/smoke/LeafDevice/LeafDevice.csproj +++ b/smoke/LeafDevice/LeafDevice.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/test/Microsoft.Azure.Devices.Edge.Test.Common/EdgeRuntime.cs b/test/Microsoft.Azure.Devices.Edge.Test.Common/EdgeRuntime.cs index 9f9a02a7a52..80932bc7a13 100644 --- a/test/Microsoft.Azure.Devices.Edge.Test.Common/EdgeRuntime.cs +++ b/test/Microsoft.Azure.Devices.Edge.Test.Common/EdgeRuntime.cs @@ -43,7 +43,7 @@ public async Task DeployConfigurationAsync( CancellationToken token, bool nestedEdge) { - (string, string)[] hubEnvVar = new (string, string)[] { ("RuntimeLogLevel", "debug"), ("SslProtocols", "tls1.2") }; + (string, string)[] hubEnvVar = new (string, string)[] { ("RuntimeLogLevel", "debug"), ("SslProtocols", "tls1.2"), ("EnableRoutingLogging", "true") }; if (nestedEdge == true) { diff --git a/test/Microsoft.Azure.Devices.Edge.Test.Common/Microsoft.Azure.Devices.Edge.Test.Common.csproj b/test/Microsoft.Azure.Devices.Edge.Test.Common/Microsoft.Azure.Devices.Edge.Test.Common.csproj index 0d968f806d9..9b6c38659dc 100644 --- a/test/Microsoft.Azure.Devices.Edge.Test.Common/Microsoft.Azure.Devices.Edge.Test.Common.csproj +++ b/test/Microsoft.Azure.Devices.Edge.Test.Common/Microsoft.Azure.Devices.Edge.Test.Common.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/test/modules/CloudToDeviceMessageTester/CloudToDeviceMessageTester.csproj b/test/modules/CloudToDeviceMessageTester/CloudToDeviceMessageTester.csproj index baf5ff4e19e..d33f4b513f5 100644 --- a/test/modules/CloudToDeviceMessageTester/CloudToDeviceMessageTester.csproj +++ b/test/modules/CloudToDeviceMessageTester/CloudToDeviceMessageTester.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/test/modules/DeploymentTester/DeploymentTester.csproj b/test/modules/DeploymentTester/DeploymentTester.csproj index 1f4aecd8f2e..1eb8e6634f4 100644 --- a/test/modules/DeploymentTester/DeploymentTester.csproj +++ b/test/modules/DeploymentTester/DeploymentTester.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/modules/DirectMethodReceiver/DirectMethodReceiver.csproj b/test/modules/DirectMethodReceiver/DirectMethodReceiver.csproj index 8aa4322611b..4c1817c798b 100644 --- a/test/modules/DirectMethodReceiver/DirectMethodReceiver.csproj +++ b/test/modules/DirectMethodReceiver/DirectMethodReceiver.csproj @@ -13,7 +13,7 @@ - + diff --git a/test/modules/DirectMethodSender/DirectMethodSender.csproj b/test/modules/DirectMethodSender/DirectMethodSender.csproj index 32cdbe4c8dc..ae5e3523b9c 100644 --- a/test/modules/DirectMethodSender/DirectMethodSender.csproj +++ b/test/modules/DirectMethodSender/DirectMethodSender.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/test/modules/EdgeHubRestartTester/EdgeHubRestartTester.csproj b/test/modules/EdgeHubRestartTester/EdgeHubRestartTester.csproj index e6a41d92cd9..d7444fd5663 100644 --- a/test/modules/EdgeHubRestartTester/EdgeHubRestartTester.csproj +++ b/test/modules/EdgeHubRestartTester/EdgeHubRestartTester.csproj @@ -27,8 +27,8 @@ - - + + diff --git a/test/modules/MetricsValidator/MetricsValidator.csproj b/test/modules/MetricsValidator/MetricsValidator.csproj index 688eb3b85db..91807e8caab 100644 --- a/test/modules/MetricsValidator/MetricsValidator.csproj +++ b/test/modules/MetricsValidator/MetricsValidator.csproj @@ -4,7 +4,8 @@ Exe - True + False + NU1605 Debug;Release;CheckInBuild true diff --git a/test/modules/MetricsValidator/src/Program.cs b/test/modules/MetricsValidator/src/Program.cs index 8d3c6e7d55d..7a37b04352f 100644 --- a/test/modules/MetricsValidator/src/Program.cs +++ b/test/modules/MetricsValidator/src/Program.cs @@ -14,12 +14,14 @@ namespace MetricsValidator using Microsoft.Azure.Devices.Edge.Agent.Diagnostics; using Microsoft.Azure.Devices.Edge.ModuleUtil; using Microsoft.Azure.Devices.Edge.Util; + using Microsoft.Azure.Devices.Logging; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; class Program { static readonly ILogger Logger = ModuleUtil.CreateLogger("MetricsValidator"); + static ConsoleEventListener consoleEventListener = new ConsoleEventListener("Microsoft-Azure-Devices-Device-Client", Logger); public static int Main() => MainAsync().Result; diff --git a/test/modules/ModuleLib/Microsoft.Azure.Devices.Edge.ModuleUtil.csproj b/test/modules/ModuleLib/Microsoft.Azure.Devices.Edge.ModuleUtil.csproj index ae1c812b459..aa30f418f66 100644 --- a/test/modules/ModuleLib/Microsoft.Azure.Devices.Edge.ModuleUtil.csproj +++ b/test/modules/ModuleLib/Microsoft.Azure.Devices.Edge.ModuleUtil.csproj @@ -3,7 +3,7 @@ - + diff --git a/test/modules/ModuleRestarter/ModuleRestarter.csproj b/test/modules/ModuleRestarter/ModuleRestarter.csproj index e0d99bde476..b08a18620e5 100644 --- a/test/modules/ModuleRestarter/ModuleRestarter.csproj +++ b/test/modules/ModuleRestarter/ModuleRestarter.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/test/modules/Relayer/Relayer.csproj b/test/modules/Relayer/Relayer.csproj index 85ad767ef81..788a33bb45c 100644 --- a/test/modules/Relayer/Relayer.csproj +++ b/test/modules/Relayer/Relayer.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/modules/TemperatureFilter/TemperatureFilter.csproj b/test/modules/TemperatureFilter/TemperatureFilter.csproj index 20d32da2ef1..417402d0838 100644 --- a/test/modules/TemperatureFilter/TemperatureFilter.csproj +++ b/test/modules/TemperatureFilter/TemperatureFilter.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/modules/TestAnalyzer/TestAnalyzer.csproj b/test/modules/TestAnalyzer/TestAnalyzer.csproj index 088eb12e905..f78db650feb 100644 --- a/test/modules/TestAnalyzer/TestAnalyzer.csproj +++ b/test/modules/TestAnalyzer/TestAnalyzer.csproj @@ -19,7 +19,7 @@ - + diff --git a/test/modules/TestMetricsCollector/TestMetricsCollector.csproj b/test/modules/TestMetricsCollector/TestMetricsCollector.csproj index 1ce0cf7e10e..594b3c9abf0 100644 --- a/test/modules/TestMetricsCollector/TestMetricsCollector.csproj +++ b/test/modules/TestMetricsCollector/TestMetricsCollector.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/modules/TestResultCoordinator/TestResultCoordinator.csproj b/test/modules/TestResultCoordinator/TestResultCoordinator.csproj index 1d3df88be0b..a672d2bedaf 100644 --- a/test/modules/TestResultCoordinator/TestResultCoordinator.csproj +++ b/test/modules/TestResultCoordinator/TestResultCoordinator.csproj @@ -20,7 +20,7 @@ - + diff --git a/test/modules/TwinTester/TwinTester.csproj b/test/modules/TwinTester/TwinTester.csproj index 20c62707fd6..846e5bc75b4 100644 --- a/test/modules/TwinTester/TwinTester.csproj +++ b/test/modules/TwinTester/TwinTester.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/test/modules/load-gen/load-gen.csproj b/test/modules/load-gen/load-gen.csproj index 92e441fe24f..b26f154624d 100644 --- a/test/modules/load-gen/load-gen.csproj +++ b/test/modules/load-gen/load-gen.csproj @@ -15,7 +15,7 @@ - +