diff --git a/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/CertificateRenewal.cs b/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/CertificateRenewal.cs
new file mode 100644
index 00000000000..5c0d11aeb15
--- /dev/null
+++ b/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/CertificateRenewal.cs
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+namespace Microsoft.Azure.Devices.Edge.Hub.Service
+{
+ using System;
+ using System.Threading;
+ using Microsoft.Azure.Devices.Edge.Util;
+ using Microsoft.Extensions.Logging;
+
+ public class CertificateRenewal : IDisposable
+ {
+ readonly static TimeSpan TimeBuffer = TimeSpan.FromMinutes(5);
+ readonly CancellationTokenSource cts;
+
+ ///
+ /// This cancellation token will expire when certificate renewal is required.
+ ///
+ public CancellationToken Token => this.cts.Token;
+
+ public CertificateRenewal(EdgeHubCertificates certificates, ILogger logger)
+ {
+ Preconditions.CheckNotNull(certificates, nameof(certificates));
+ Preconditions.CheckNotNull(logger, nameof(logger));
+
+ TimeSpan timeToExpire = certificates.ServerCertificate.NotAfter - DateTime.UtcNow;
+ if (timeToExpire > TimeBuffer)
+ {
+ var renewAfter = timeToExpire - TimeBuffer;
+ logger.LogInformation("Scheduling server certificate renewal for {0}.", DateTime.UtcNow.Add(renewAfter).ToString("o"));
+ this.cts = new CancellationTokenSource(renewAfter);
+ this.cts.Token.Register(l => ((ILogger)l).LogInformation("Restarting process to perform server certificate renewal."), logger);
+ }
+ else
+ {
+ this.cts = new CancellationTokenSource();
+ logger.LogWarning("Server certificate is expired ({0}). Not scheduling renewal.", timeToExpire.ToString("c"));
+ }
+ }
+
+ public void Dispose()
+ {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ try
+ {
+ this.cts.Dispose();
+ }
+ catch (OperationCanceledException)
+ {
+ // ignore by design
+ }
+ }
+ }
+ }
+}
diff --git a/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/EdgeHubCertificates.cs b/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/EdgeHubCertificates.cs
index 84f7659a07b..d57d0f98701 100644
--- a/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/EdgeHubCertificates.cs
+++ b/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/EdgeHubCertificates.cs
@@ -20,8 +20,8 @@ public class EdgeHubCertificates
EdgeHubCertificates(X509Certificate2 serverCertificate, IList certificateChain)
{
- this.ServerCertificate = serverCertificate;
- this.CertificateChain = certificateChain;
+ this.ServerCertificate = Preconditions.CheckNotNull(serverCertificate, nameof(serverCertificate));
+ this.CertificateChain = Preconditions.CheckNotNull(certificateChain, nameof(certificateChain));
}
public static async Task LoadAsync(IConfigurationRoot configuration)
diff --git a/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs b/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs
index 0cf90feea84..75898280cf0 100644
--- a/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs
+++ b/edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs
@@ -56,6 +56,8 @@ static async Task MainAsync(IConfigurationRoot configuration)
LogLogo(logger);
LogVersionInfo(logger);
+ logger.LogInformation("Loaded server certificate with expiration date of {0}", certificates.ServerCertificate.NotAfter.ToString("o"));
+
// EdgeHub and CloudConnectionProvider have a circular dependency. So need to Bind the EdgeHub to the CloudConnectionProvider.
IEdgeHub edgeHub = await container.Resolve>();
ICloudConnectionProvider cloudConnectionProvider = await container.Resolve>();
@@ -90,14 +92,18 @@ static async Task MainAsync(IConfigurationRoot configuration)
Metrics.BuildMetricsCollector(configuration);
using (IProtocolHead protocolHead = await GetEdgeHubProtocolHeadAsync(logger, configuration, container, hosting).ConfigureAwait(false))
+ using (var renewal = new CertificateRenewal(certificates, logger))
{
await protocolHead.StartAsync();
- await cts.Token.WhenCanceled();
+ await Task.WhenAny(cts.Token.WhenCanceled(), renewal.Token.WhenCanceled());
+ logger.LogInformation("Stopping the protocol heads...");
await Task.WhenAny(protocolHead.CloseAsync(CancellationToken.None), Task.Delay(TimeSpan.FromSeconds(10), CancellationToken.None));
+ logger.LogInformation("Protocol heads stopped.");
}
completed.Set();
handler.ForEach(h => GC.KeepAlive(h));
+ logger.LogInformation("Shutdown complete.");
return 0;
}
diff --git a/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ShutdownHandler.cs b/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ShutdownHandler.cs
index 6a2126bcde4..f4efe07e26a 100644
--- a/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ShutdownHandler.cs
+++ b/edge-util/src/Microsoft.Azure.Devices.Edge.Util/ShutdownHandler.cs
@@ -11,8 +11,8 @@ public static class ShutdownHandler
{
///
/// Here are some references which were used for this code -
- /// https://stackoverflow.com/questions/40742192/how-to-do-gracefully-shutdown-on-dotnet-with-docker/43813871
- /// https://msdn.microsoft.com/en-us/library/system.gc.keepalive(v=vs.110).aspx
+ /// https://stackoverflow.com/questions/40742192/how-to-do-gracefully-shutdown-on-dotnet-with-docker/43813871
+ /// https://msdn.microsoft.com/en-us/library/system.gc.keepalive(v=vs.110).aspx
///
public static (CancellationTokenSource cts, ManualResetEventSlim doneSignal, Option