From 26bbf7145b1cd76fd55cb7b7d990a3907616ac5a Mon Sep 17 00:00:00 2001 From: hugues bouvier <33438817+huguesBouvier@users.noreply.github.com> Date: Thu, 9 Dec 2021 17:55:27 -0800 Subject: [PATCH] Fix: Workload socket issue for concurrent module create (#5876) (#5885) (#5907) Cherry pick of: https://github.com/Azure/iotedge/commit/5712dcc28498121d890082d5c884d9855cc40efd ## Azure IoT Edge PR checklist: --- .../Constants.cs | 2 + .../EdgeletCommandFactory.cs | 48 ++++++++++++++----- .../commands/PrepareUpdateCommand.cs | 2 +- .../Program.cs | 3 +- .../modules/EdgeletModule.cs | 8 +++- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Constants.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Constants.cs index a8963e5ced8..ea12a1b48db 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Constants.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Constants.cs @@ -43,6 +43,8 @@ public static class Constants public const string EdgeModuleHubServerCertificateFileKey = "EdgeModuleHubServerCertificateFile"; + public const string CheckImagePullBeforeModuleCreate = "CheckImagePullBeforeModuleCreate"; + public const string Unknown = "Unknown"; public const string UpstreamProtocolKey = "UpstreamProtocol"; diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/EdgeletCommandFactory.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/EdgeletCommandFactory.cs index f4c50300048..0843010427a 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/EdgeletCommandFactory.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/EdgeletCommandFactory.cs @@ -16,11 +16,13 @@ public class EdgeletCommandFactory : ICommandFactory readonly ICombinedConfigProvider combinedConfigProvider; readonly string edgeDeviceHostname; readonly Option parentEdgeHostname; + readonly bool checkImagePullBeforeModuleCreate; public EdgeletCommandFactory( IModuleManager moduleManager, IConfigSource configSource, - ICombinedConfigProvider combinedConfigProvider) + ICombinedConfigProvider combinedConfigProvider, + bool checkImagePullBeforeModuleCreate) { this.moduleManager = Preconditions.CheckNotNull(moduleManager, nameof(moduleManager)); this.configSource = Preconditions.CheckNotNull(configSource, nameof(configSource)); @@ -32,19 +34,41 @@ public EdgeletCommandFactory( } this.parentEdgeHostname = Option.Maybe(this.configSource.Configuration.GetValue(Constants.GatewayHostnameVariableName)); + this.checkImagePullBeforeModuleCreate = checkImagePullBeforeModuleCreate; } - public Task CreateAsync(IModuleWithIdentity module, IRuntimeInfo runtimeInfo) => - Task.FromResult( - CreateOrUpdateCommand.BuildCreate( - this.moduleManager, - module.Module, - module.ModuleIdentity, - this.configSource, - this.combinedConfigProvider.GetCombinedConfig(module.Module, runtimeInfo), - this.edgeDeviceHostname, - this.parentEdgeHostname) - as ICommand); + public Task CreateAsync(IModuleWithIdentity module, IRuntimeInfo runtimeInfo) + { + if (this.checkImagePullBeforeModuleCreate) + { + T config = this.combinedConfigProvider.GetCombinedConfig(module.Module, runtimeInfo); + return Task.FromResult( + new GroupCommand( + new PrepareUpdateCommand(this.moduleManager, module.Module, config), + CreateOrUpdateCommand.BuildCreate( + this.moduleManager, + module.Module, + module.ModuleIdentity, + this.configSource, + config, + this.edgeDeviceHostname, + this.parentEdgeHostname) + as ICommand) as ICommand); + } + else + { + return Task.FromResult( + CreateOrUpdateCommand.BuildCreate( + this.moduleManager, + module.Module, + module.ModuleIdentity, + this.configSource, + this.combinedConfigProvider.GetCombinedConfig(module.Module, runtimeInfo), + this.edgeDeviceHostname, + this.parentEdgeHostname) + as ICommand); + } + } public Task UpdateAsync(IModule current, IModuleWithIdentity next, IRuntimeInfo runtimeInfo) => this.UpdateAsync(Option.Some(current), next, runtimeInfo, false); diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/commands/PrepareUpdateCommand.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/commands/PrepareUpdateCommand.cs index 087f5150c56..66e1ac4cbd9 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/commands/PrepareUpdateCommand.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/commands/PrepareUpdateCommand.cs @@ -20,7 +20,7 @@ public PrepareUpdateCommand(IModuleManager moduleManager, IModule module, object public string Id => $"PrepareUpdateCommand({this.module.Name})"; - public string Show() => $"Prepare update module {this.module.Name}"; + public string Show() => $"Prepare module {this.module.Name}"; public Task ExecuteAsync(CancellationToken token) { diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs index 99e3b33dbc7..b0fdb853c70 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs @@ -178,6 +178,7 @@ public static async Task MainAsync(IConfiguration configuration) case Constants.IotedgedMode: string managementUri = configuration.GetValue(Constants.EdgeletManagementUriVariableName); string workloadUri = configuration.GetValue(Constants.EdgeletWorkloadUriVariableName); + bool checkImagePullBeforeModuleCreate = configuration.GetValue(Constants.CheckImagePullBeforeModuleCreate, true); iothubHostname = configuration.GetValue(Constants.IotHubHostnameVariableName); deviceId = configuration.GetValue(Constants.DeviceIdVariableName); string moduleId = configuration.GetValue(Constants.ModuleIdVariableName, Constants.EdgeAgentModuleIdentityName); @@ -185,7 +186,7 @@ public static async Task MainAsync(IConfiguration configuration) apiVersion = configuration.GetValue(Constants.EdgeletApiVersionVariableName); TimeSpan performanceMetricsUpdateFrequency = configuration.GetTimeSpan("PerformanceMetricsUpdateFrequency", TimeSpan.FromMinutes(5)); builder.RegisterModule(new AgentModule(maxRestartCount, intensiveCareTime, coolOffTimeUnitInSeconds, usePersistentStorage, storagePath, Option.Some(new Uri(workloadUri)), Option.Some(apiVersion), moduleId, Option.Some(moduleGenerationId), enableNonPersistentStorageBackup, storageBackupPath, storageTotalMaxWalSize, storageMaxManifestFileSize, storageMaxOpenFiles, storageLogLevel)); - builder.RegisterModule(new EdgeletModule(iothubHostname, deviceId, new Uri(managementUri), new Uri(workloadUri), apiVersion, dockerAuthConfig, upstreamProtocol, proxy, productInfo, closeOnIdleTimeout, idleTimeout, performanceMetricsUpdateFrequency, useServerHeartbeat, backupConfigFilePath)); + builder.RegisterModule(new EdgeletModule(iothubHostname, deviceId, new Uri(managementUri), new Uri(workloadUri), apiVersion, dockerAuthConfig, upstreamProtocol, proxy, productInfo, closeOnIdleTimeout, idleTimeout, performanceMetricsUpdateFrequency, useServerHeartbeat, backupConfigFilePath, checkImagePullBeforeModuleCreate)); IEnumerable trustBundle = await CertificateHelper.GetTrustBundleFromEdgelet(new Uri(workloadUri), apiVersion, Constants.WorkloadApiVersion, moduleId, moduleGenerationId); CertificateHelper.InstallCertificates(trustBundle, logger); diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/modules/EdgeletModule.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/modules/EdgeletModule.cs index 6fbb83cbebe..6f7b730f826 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/modules/EdgeletModule.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/modules/EdgeletModule.cs @@ -44,6 +44,7 @@ public class EdgeletModule : Module readonly TimeSpan performanceMetricsUpdateFrequency; readonly bool useServerHeartbeat; readonly string backupConfigFilePath; + readonly bool checkImagePullBeforeModuleCreate; public EdgeletModule( string iotHubHostname, @@ -59,7 +60,8 @@ public EdgeletModule( TimeSpan idleTimeout, TimeSpan performanceMetricsUpdateFrequency, bool useServerHeartbeat, - string backupConfigFilePath) + string backupConfigFilePath, + bool checkImagePullBeforeModuleCreate) { this.iotHubHostName = Preconditions.CheckNonWhiteSpace(iotHubHostname, nameof(iotHubHostname)); this.deviceId = Preconditions.CheckNonWhiteSpace(deviceId, nameof(deviceId)); @@ -75,6 +77,7 @@ public EdgeletModule( this.performanceMetricsUpdateFrequency = performanceMetricsUpdateFrequency; this.useServerHeartbeat = useServerHeartbeat; this.backupConfigFilePath = Preconditions.CheckNonWhiteSpace(backupConfigFilePath, nameof(backupConfigFilePath)); + this.checkImagePullBeforeModuleCreate = checkImagePullBeforeModuleCreate; } protected override void Load(ContainerBuilder builder) @@ -129,7 +132,8 @@ protected override void Load(ContainerBuilder builder) ICommandFactory factory = new EdgeletCommandFactory( moduleManager, configSource, - combinedDockerConfigProvider); + combinedDockerConfigProvider, + this.checkImagePullBeforeModuleCreate); factory = new MetricsCommandFactory(factory, metricsProvider); return new LoggingCommandFactory(factory, loggerFactory) as ICommandFactory; })