in edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs [85:334]
public static async Task<int> MainAsync(IConfiguration configuration, ILogger logger)
{
logger.LogInformation("Initializing Edge Agent.");
VersionInfo versionInfo = VersionInfo.Get(VersionInfoFileName);
if (versionInfo != VersionInfo.Empty)
{
logger.LogInformation($"Version - {versionInfo.ToString(true)}");
}
LogLogo(logger);
string mode;
string configSourceConfig;
string backupConfigFilePath;
int maxRestartCount;
TimeSpan intensiveCareTime;
int coolOffTimeUnitInSeconds;
bool usePersistentStorage;
string storagePath;
bool enableNonPersistentStorageBackup;
Option<string> storageBackupPath = Option.None<string>();
string edgeDeviceHostName;
IEnumerable<global::Docker.DotNet.Models.AuthConfig> dockerAuthConfig;
int configRefreshFrequencySecs;
ExperimentalFeatures experimentalFeatures;
MetricsConfig metricsConfig;
DiagnosticConfig diagnosticConfig;
bool useServerHeartbeat;
ModuleUpdateMode moduleUpdateMode;
try
{
mode = configuration.GetValue(Constants.ModeKey, "iotedged");
configSourceConfig = configuration.GetValue<string>("ConfigSource");
backupConfigFilePath = configuration.GetValue<string>("BackupConfigFilePath");
maxRestartCount = configuration.GetValue<int>("MaxRestartCount");
intensiveCareTime = TimeSpan.FromMinutes(configuration.GetValue<int>("IntensiveCareTimeInMinutes"));
coolOffTimeUnitInSeconds = configuration.GetValue("CoolOffTimeUnitInSeconds", 10);
usePersistentStorage = configuration.GetValue("UsePersistentStorage", true);
useServerHeartbeat = configuration.GetValue("UseServerHeartbeat", true);
moduleUpdateMode = configuration.GetValue("ModuleUpdateMode", ModuleUpdateMode.NonBlocking);
logger.LogInformation($"ModuleUpdateMode: {moduleUpdateMode.ToString()}");
// Note: Keep in sync with iotedge-check's edge-agent-storage-mounted-from-host check (edgelet/iotedge/src/check/checks/storage_mounted_from_host.rs)
storagePath = GetOrCreateDirectoryPath(configuration.GetValue<string>("StorageFolder"), EdgeAgentStorageFolder);
enableNonPersistentStorageBackup = configuration.GetValue("EnableNonPersistentStorageBackup", false);
if (enableNonPersistentStorageBackup)
{
storageBackupPath = Option.Some(GetOrCreateDirectoryPath(configuration.GetValue<string>("BackupFolder"), EdgeAgentStorageBackupFolder));
}
backupConfigFilePath = GetFullBackupFilePath(storagePath, backupConfigFilePath);
edgeDeviceHostName = configuration.GetValue<string>(Constants.EdgeDeviceHostNameKey);
dockerAuthConfig = configuration.GetSection("DockerRegistryAuth").Get<List<global::Docker.DotNet.Models.AuthConfig>>() ?? new List<global::Docker.DotNet.Models.AuthConfig>();
NestedEdgeParentUriParser parser = new NestedEdgeParentUriParser();
dockerAuthConfig = dockerAuthConfig.Select(c =>
{
c.Password = parser.ParseURI(c.Password).GetOrElse(c.Password);
return c;
})
.ToList();
configRefreshFrequencySecs = configuration.GetValue("ConfigRefreshFrequencySecs", 3600);
}
catch (Exception ex)
{
logger.LogCritical(AgentEventIds.Agent, ex, "Fatal error reading the Agent's configuration.");
return 1;
}
IContainer container;
try
{
var builder = new ContainerBuilder();
builder.RegisterModule(new LoggingModule());
string productInfo =
versionInfo != VersionInfo.Empty ?
$"{Constants.IoTEdgeAgentProductInfoIdentifier}/{versionInfo}" :
Constants.IoTEdgeAgentProductInfoIdentifier;
Option<UpstreamProtocol> upstreamProtocol = configuration.GetValue<string>(Constants.UpstreamProtocolKey).ToUpstreamProtocol();
Option<IWebProxy> proxy = Proxy.Parse(configuration.GetValue<string>("https_proxy"), logger);
bool closeOnIdleTimeout = configuration.GetValue(Constants.CloseOnIdleTimeout, false);
int idleTimeoutSecs = configuration.GetValue(Constants.IdleTimeoutSecs, 300);
TimeSpan idleTimeout = TimeSpan.FromSeconds(idleTimeoutSecs);
experimentalFeatures = ExperimentalFeatures.Create(configuration.GetSection("experimentalFeatures"), logger);
Option<ulong> storageTotalMaxWalSize = GetConfigIfExists<ulong>(Constants.StorageMaxTotalWalSize, configuration, logger);
Option<ulong> storageMaxManifestFileSize = GetConfigIfExists<ulong>(Constants.StorageMaxManifestFileSize, configuration, logger);
Option<int> storageMaxOpenFiles = GetConfigIfExists<int>(Constants.StorageMaxOpenFiles, configuration, logger);
Option<StorageLogLevel> storageLogLevel = GetConfigIfExists<StorageLogLevel>(Constants.StorageLogLevel, configuration, logger);
string iothubHostname;
string deviceId;
string apiVersion = "2018-06-28";
Option<X509Certificate2> manifestTrustBundle = Option.None<X509Certificate2>();
int edgeletTimeoutSecs = configuration.GetValue(Constants.ManagementApiTimeoutSecs, 300);
TimeSpan edgeletTimeout = TimeSpan.FromSeconds(edgeletTimeoutSecs);
var enableOrphanedIdentityCleanup = configuration.GetValue("EnableOrphanedIdentityCleanup", false);
switch (mode.ToLowerInvariant())
{
case Constants.IotedgedMode:
string managementUri = configuration.GetValue<string>(Constants.EdgeletManagementUriVariableName);
string workloadUri = configuration.GetValue<string>(Constants.EdgeletWorkloadUriVariableName);
bool disableDeviceAnalyticsMetadata = configuration.GetValue<bool?>("DisableDeviceAnalyticsMetadata") ?? configuration.GetValue<bool>("DisableDeviceAnalyticsTelemetry", false);
iothubHostname = configuration.GetValue<string>(Constants.IotHubHostnameVariableName);
deviceId = configuration.GetValue<string>(Constants.DeviceIdVariableName);
string moduleId = configuration.GetValue(Constants.ModuleIdVariableName, Constants.EdgeAgentModuleIdentityName);
string moduleGenerationId = configuration.GetValue<string>(Constants.EdgeletModuleGenerationIdVariableName);
apiVersion = configuration.GetValue<string>(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, moduleUpdateMode));
builder.RegisterModule(new EdgeletModule(iothubHostname, deviceId, new Uri(managementUri), new Uri(workloadUri), apiVersion, dockerAuthConfig, upstreamProtocol, proxy, productInfo, closeOnIdleTimeout, idleTimeout, performanceMetricsUpdateFrequency, useServerHeartbeat, backupConfigFilePath, disableDeviceAnalyticsMetadata, moduleUpdateMode, edgeletTimeout, enableOrphanedIdentityCleanup));
IEnumerable<X509Certificate2> trustBundle =
await CertificateHelper.GetTrustBundleFromEdgelet(new Uri(workloadUri), apiVersion, Constants.WorkloadApiVersion, moduleId, moduleGenerationId);
CertificateHelper.InstallCertificates(trustBundle, logger);
manifestTrustBundle = await CertificateHelper.GetManifestTrustBundleFromEdgelet(new Uri(workloadUri), apiVersion, Constants.WorkloadApiVersion, moduleId, moduleGenerationId);
break;
case Constants.KubernetesMode:
default:
throw new InvalidOperationException($"Mode '{mode}' not supported.");
}
switch (configSourceConfig.ToLowerInvariant())
{
case "twin":
bool enableStreams = configuration.GetValue(Constants.EnableStreams, false);
int requestTimeoutSecs = configuration.GetValue(Constants.RequestTimeoutSecs, 600);
builder.RegisterModule(
new TwinConfigSourceModule(
iothubHostname,
deviceId,
configuration,
versionInfo,
TimeSpan.FromSeconds(configRefreshFrequencySecs),
enableStreams,
TimeSpan.FromSeconds(requestTimeoutSecs),
experimentalFeatures,
manifestTrustBundle));
break;
case "local":
string localConfigFilePath = GetLocalConfigFilePath(configuration, logger);
builder.RegisterModule(new FileConfigSourceModule(localConfigFilePath, configuration, versionInfo));
break;
default:
throw new InvalidOperationException($"ConfigSource '{configSourceConfig}' not supported.");
}
metricsConfig = new MetricsConfig(configuration);
builder.RegisterModule(new MetricsModule(metricsConfig, iothubHostname, deviceId));
bool diagnosticsEnabled = configuration.GetValue("SendRuntimeQualityTelemetry", true);
diagnosticConfig = new DiagnosticConfig(diagnosticsEnabled, storagePath, configuration);
builder.RegisterModule(new DiagnosticsModule(diagnosticConfig));
container = builder.Build();
}
catch (Exception ex)
{
logger.LogCritical(AgentEventIds.Agent, ex, "Fatal error building application.");
return 1;
}
// Initialize metrics
if (metricsConfig.Enabled)
{
container.Resolve<IMetricsListener>().Start(logger);
container.Resolve<ISystemResourcesMetrics>().Start(logger);
await container.Resolve<MetadataMetrics>().Start(logger, versionInfo.ToString(true), Newtonsoft.Json.JsonConvert.SerializeObject(experimentalFeatures));
}
// Initialize metric uploading
if (diagnosticConfig.Enabled)
{
MetricsWorker worker = await container.Resolve<Task<MetricsWorker>>();
worker.Start(diagnosticConfig.ScrapeInterval, diagnosticConfig.UploadInterval);
Console.WriteLine($"Scraping frequency: {diagnosticConfig.ScrapeInterval}\nUpload Frequency: {diagnosticConfig.UploadInterval}");
}
(CancellationTokenSource cts, ManualResetEventSlim completed, Option<object> handler)
= ShutdownHandler.Init(ShutdownWaitPeriod, logger);
// Register request handlers
await RegisterRequestHandlers(container);
// Initialize stream request listener
IStreamRequestListener streamRequestListener = await container.Resolve<Task<IStreamRequestListener>>();
streamRequestListener.InitPump();
int returnCode;
using (IConfigSource unused = await container.Resolve<Task<IConfigSource>>())
{
Option<Agent> agentOption = Option.None<Agent>();
try
{
Agent agent = await container.Resolve<Task<Agent>>();
agentOption = Option.Some(agent);
while (!cts.Token.IsCancellationRequested)
{
try
{
await agent.ReconcileAsync(cts.Token).TimeoutAfter(ReconcileTimeout);
}
catch (Exception ex) when (!ex.IsFatal())
{
logger.LogWarning(AgentEventIds.Agent, ex, "Agent reconcile concluded with errors.");
}
await Task.Delay(TimeSpan.FromSeconds(5), cts.Token);
}
logger.LogInformation("Closing module management agent.");
returnCode = 0;
}
catch (OperationCanceledException)
{
logger.LogInformation("Main thread terminated");
returnCode = 0;
}
catch (Exception ex)
{
logger.LogCritical(AgentEventIds.Agent, ex, "Fatal error starting Agent.");
returnCode = 1;
}
// Attempt to report shutdown of Agent
await Cleanup(agentOption, logger);
await CloseDbStoreProviderAsync(container);
if (metricsConfig.Enabled && returnCode == 0)
{
container.Resolve<IDeploymentMetrics>().IndicateCleanShutdown();
}
completed.Set();
}
handler.ForEach(h => GC.KeepAlive(h));
return returnCode;
}