in edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Agent.cs [113:208]
public async Task ReconcileAsync(CancellationToken token)
{
ModuleSet moduleSetToReport = null;
using (await this.reconcileLock.LockAsync(token))
{
try
{
Events.StartingReconcile();
(ModuleSet current, DeploymentConfigInfo deploymentConfigInfo, Exception exception) = await this.GetReconcileData(token);
moduleSetToReport = current;
if (exception != null)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
DeploymentConfig deploymentConfig = deploymentConfigInfo.DeploymentConfig;
if (deploymentConfig.Equals(DeploymentConfig.Empty))
{
this.status = DeploymentStatus.Success;
}
else
{
ModuleSet desiredModuleSet = deploymentConfig.GetModuleSet();
_ = Task.Run(() => this.deploymentMetrics.ComputeAvailability(desiredModuleSet, current))
.ContinueWith(t => Events.UnknownFailure(t.Exception), TaskContinuationOptions.OnlyOnFaulted)
.ConfigureAwait(false);
// TODO - Update this logic to create identities only when needed, in the Command factory, instead of creating all the identities
// up front here. That will allow handling the case when only the state of the system has changed (say one module crashes), and
// no new identities need to be created. This will simplify the logic to allow EdgeAgent to work when offline.
// But that required ModuleSet.Diff to be updated to include modules updated by deployment, and modules updated by state change.
IImmutableDictionary<string, IModuleIdentity> identities = await this.moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desiredModuleSet, current);
Plan plan = await this.planner.PlanAsync(desiredModuleSet, current, deploymentConfig.Runtime, identities);
if (plan.IsEmpty)
{
if (this.currentConfig.Version != deploymentConfigInfo.Version)
{
await this.UpdateCurrentConfig(deploymentConfigInfo);
}
this.status = DeploymentStatus.Success;
}
else
{
try
{
using (this.deploymentMetrics.ReportDeploymentTime())
{
bool result = await this.planRunner.ExecuteAsync(deploymentConfigInfo.Version, plan, token);
await this.UpdateCurrentConfig(deploymentConfigInfo);
if (result)
{
this.status = DeploymentStatus.Success;
}
}
}
catch (Exception ex) when (!ex.IsFatal())
{
Events.PlanExecutionFailed(ex);
await this.UpdateCurrentConfig(deploymentConfigInfo);
throw;
}
}
}
}
catch (Exception ex) when (!ex.IsFatal())
{
switch (ex)
{
case ConfigEmptyException _:
this.status = new DeploymentStatus(DeploymentStatusCode.ConfigEmptyError, ex.Message);
Events.EmptyConfig(ex);
break;
case InvalidSchemaVersionException _:
this.status = new DeploymentStatus(DeploymentStatusCode.InvalidSchemaVersion, ex.Message);
Events.InvalidSchemaVersion(ex);
break;
case ConfigFormatException _:
this.status = new DeploymentStatus(DeploymentStatusCode.ConfigFormatError, ex.Message);
Events.InvalidConfigFormat(ex);
break;
default:
this.status = new DeploymentStatus(DeploymentStatusCode.Failed, ex.Message);
Events.UnknownFailure(ex);
break;
}
}
await this.reporter.ReportAsync(token, moduleSetToReport, await this.environment.GetRuntimeInfoAsync(), this.currentConfig.Version, this.status);
Events.FinishedReconcile();
}
}