Example #1
0
        void RegisterRoutingModule(
            ContainerBuilder builder,
            StoreAndForward storeAndForward,
            ExperimentalFeatures experimentalFeatures,
            bool nestedEdgeEnabled,
            bool scopeAuthenticationOnly,
            bool trackDeviceState)
        {
            var    routes             = this.configuration.GetSection("routes").Get <Dictionary <string, string> >();
            int    connectionPoolSize = this.configuration.GetValue <int>("IotHubConnectionPoolSize");
            string configSource       = this.configuration.GetValue <string>("configSource");
            bool   useTwinConfig      = !string.IsNullOrWhiteSpace(configSource) && configSource.Equals("twin", StringComparison.OrdinalIgnoreCase);
            Option <UpstreamProtocol> upstreamProtocolOption = GetUpstreamProtocol(this.configuration);
            int      connectivityCheckFrequencySecs          = this.configuration.GetValue("ConnectivityCheckFrequencySecs", 300);
            TimeSpan connectivityCheckFrequency = connectivityCheckFrequencySecs < 0 ? TimeSpan.MaxValue : TimeSpan.FromSeconds(connectivityCheckFrequencySecs);
            // n Clients + 1 Edgehub
            int               maxConnectedClients               = this.configuration.GetValue("MaxConnectedClients", 100) + 1;
            int               messageAckTimeoutSecs             = this.configuration.GetValue("MessageAckTimeoutSecs", 30);
            TimeSpan          messageAckTimeout                 = TimeSpan.FromSeconds(messageAckTimeoutSecs);
            int               cloudConnectionIdleTimeoutSecs    = this.configuration.GetValue("CloudConnectionIdleTimeoutSecs", 3600);
            TimeSpan          cloudConnectionIdleTimeout        = TimeSpan.FromSeconds(cloudConnectionIdleTimeoutSecs);
            bool              closeCloudConnectionOnIdleTimeout = this.configuration.GetValue("CloseCloudConnectionOnIdleTimeout", true);
            int               cloudOperationTimeoutSecs         = this.configuration.GetValue("CloudOperationTimeoutSecs", 20);
            bool              useServerHeartbeat                = this.configuration.GetValue("UseServerHeartbeat", true);
            TimeSpan          cloudOperationTimeout             = TimeSpan.FromSeconds(cloudOperationTimeoutSecs);
            Option <TimeSpan> minTwinSyncPeriod                 = this.GetConfigurationValueIfExists("MinTwinSyncPeriodSecs")
                                                                  .Map(s => TimeSpan.FromSeconds(s));
            Option <TimeSpan> reportedPropertiesSyncFrequency = this.GetConfigurationValueIfExists("ReportedPropertiesSyncFrequencySecs")
                                                                .Map(s => TimeSpan.FromSeconds(s));
            bool useV1TwinManager = this.GetConfigurationValueIfExists <string>("TwinManagerVersion")
                                    .Map(v => v.Equals("v1", StringComparison.OrdinalIgnoreCase))
                                    .GetOrElse(false);
            int      maxUpstreamBatchSize                   = this.configuration.GetValue("MaxUpstreamBatchSize", 10);
            int      upstreamFanOutFactor                   = this.configuration.GetValue("UpstreamFanOutFactor", 10);
            bool     encryptTwinStore                       = this.configuration.GetValue("EncryptTwinStore", true);
            int      configUpdateFrequencySecs              = this.configuration.GetValue("ConfigRefreshFrequencySecs", 3600);
            TimeSpan configUpdateFrequency                  = TimeSpan.FromSeconds(configUpdateFrequencySecs);
            bool     checkEntireQueueOnCleanup              = this.configuration.GetValue("CheckEntireQueueOnCleanup", false);
            int      messageCleanupIntervalSecs             = this.configuration.GetValue("MessageCleanupIntervalSecs", 1800);
            bool     closeCloudConnectionOnDeviceDisconnect = this.configuration.GetValue("CloseCloudConnectionOnDeviceDisconnect", true);
            bool     isLegacyUpstream                       = ExperimentalFeatures.IsViaBrokerUpstream(
                experimentalFeatures,
                nestedEdgeEnabled,
                this.GetConfigurationValueIfExists <string>(Constants.ConfigKey.GatewayHostname).HasValue);

            builder.RegisterModule(
                new RoutingModule(
                    this.iotHubHostname,
                    this.gatewayHostname,
                    this.edgeDeviceId,
                    this.edgeModuleId,
                    this.connectionString,
                    routes,
                    storeAndForward.IsEnabled,
                    storeAndForward.Config,
                    connectionPoolSize,
                    useTwinConfig,
                    this.versionInfo,
                    upstreamProtocolOption,
                    connectivityCheckFrequency,
                    maxConnectedClients,
                    messageAckTimeout,
                    cloudConnectionIdleTimeout,
                    closeCloudConnectionOnIdleTimeout,
                    cloudOperationTimeout,
                    useServerHeartbeat,
                    minTwinSyncPeriod,
                    reportedPropertiesSyncFrequency,
                    useV1TwinManager,
                    maxUpstreamBatchSize,
                    upstreamFanOutFactor,
                    encryptTwinStore,
                    configUpdateFrequency,
                    checkEntireQueueOnCleanup,
                    messageCleanupIntervalSecs,
                    experimentalFeatures,
                    closeCloudConnectionOnDeviceDisconnect,
                    nestedEdgeEnabled,
                    isLegacyUpstream,
                    scopeAuthenticationOnly: scopeAuthenticationOnly,
                    trackDeviceState: trackDeviceState));
        }
Example #2
0
        static async Task <int> MainAsync(IConfigurationRoot configuration)
        {
            string logLevel = configuration.GetValue($"{Logger.RuntimeLogLevelEnvKey}", "info");

            Logger.SetLogLevel(logLevel);

            // Set the LoggerFactory used by the Routing code.
            if (configuration.GetValue("EnableRoutingLogging", false))
            {
                Routing.LoggerFactory = Logger.Factory;
            }

            ILogger logger = Logger.Factory.CreateLogger("EdgeHub");

            EdgeHubCertificates certificates = await EdgeHubCertificates.LoadAsync(configuration, logger);

            bool clientCertAuthEnabled = configuration.GetValue(Constants.ConfigKey.EdgeHubClientCertAuthEnabled, false);

            string       sslProtocolsConfig = configuration.GetValue(Constants.ConfigKey.SslProtocols, string.Empty);
            SslProtocols sslProtocols       = SslProtocolsHelper.Parse(sslProtocolsConfig, DefaultSslProtocols, logger);

            logger.LogInformation($"Enabling SSL protocols: {sslProtocols.Print()}");

            IDependencyManager dependencyManager = new DependencyManager(configuration, certificates.ServerCertificate, certificates.TrustBundle, sslProtocols);
            Hosting            hosting           = Hosting.Initialize(configuration, certificates.ServerCertificate, dependencyManager, clientCertAuthEnabled, sslProtocols);
            IContainer         container         = hosting.Container;

            logger.LogInformation("Initializing Edge Hub");
            LogLogo(logger);
            LogVersionInfo(logger);
            logger.LogInformation($"OptimizeForPerformance={configuration.GetValue("OptimizeForPerformance", true)}");
            logger.LogInformation($"MessageAckTimeoutSecs={configuration.GetValue("MessageAckTimeoutSecs", 30)}");
            logger.LogInformation("Loaded server certificate with expiration date of {0}", certificates.ServerCertificate.NotAfter.ToString("o"));

            var metricsProvider = container.Resolve <IMetricsProvider>();

            Metrics.InitWithAspNet(metricsProvider, logger); // Note this requires App.UseMetricServer() to be called in Startup.cs

            // EdgeHub and CloudConnectionProvider have a circular dependency. So need to Bind the EdgeHub to the CloudConnectionProvider.
            IEdgeHub edgeHub = await container.Resolve <Task <IEdgeHub> >();

            ICloudConnectionProvider cloudConnectionProvider = await container.Resolve <Task <ICloudConnectionProvider> >();

            cloudConnectionProvider.BindEdgeHub(edgeHub);

            // EdgeHub cloud proxy and DeviceConnectivityManager have a circular dependency,
            // so the cloud proxy has to be set on the DeviceConnectivityManager after both have been initialized.
            var deviceConnectivityManager        = container.Resolve <IDeviceConnectivityManager>();
            IConnectionManager connectionManager = await container.Resolve <Task <IConnectionManager> >();

            (deviceConnectivityManager as DeviceConnectivityManager)?.SetConnectionManager(connectionManager);

            // Register EdgeHub credentials
            var edgeHubCredentials             = container.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
            ICredentialsCache credentialsCache = await container.Resolve <Task <ICredentialsCache> >();

            await credentialsCache.Add(edgeHubCredentials);

            // Initializing configuration
            logger.LogInformation("Initializing configuration");
            IConfigSource configSource = await container.Resolve <Task <IConfigSource> >();

            ConfigUpdater configUpdater = await container.Resolve <Task <ConfigUpdater> >();

            ExperimentalFeatures experimentalFeatures = CreateExperimentalFeatures(configuration);
            var configUpdaterStartupFailed            = new TaskCompletionSource <bool>();
            var configDownloadTask = configUpdater.Init(configSource);

            _ = configDownloadTask.ContinueWith(
                _ => configUpdaterStartupFailed.SetResult(false),
                TaskContinuationOptions.OnlyOnFaulted);

            if (!ExperimentalFeatures.IsViaBrokerUpstream(
                    experimentalFeatures,
                    string.IsNullOrEmpty(configuration.GetValue <string>(Constants.ConfigKey.GatewayHostname))))
            {
                await configDownloadTask;
            }

            if (!Enum.TryParse(configuration.GetValue("AuthenticationMode", string.Empty), true, out AuthenticationMode authenticationMode) ||
                authenticationMode != AuthenticationMode.Cloud)
            {
                ConnectionReauthenticator connectionReauthenticator = await container.Resolve <Task <ConnectionReauthenticator> >();

                connectionReauthenticator.Init();
            }

            TimeSpan shutdownWaitPeriod = TimeSpan.FromSeconds(configuration.GetValue("ShutdownWaitPeriod", DefaultShutdownWaitPeriod));

            (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(shutdownWaitPeriod, logger);

            using (IProtocolHead protocolHead = await GetEdgeHubProtocolHeadAsync(logger, configuration, experimentalFeatures, container, hosting))
                using (var renewal = new CertificateRenewal(certificates, logger))
                {
                    try
                    {
                        await protocolHead.StartAsync();

                        await Task.WhenAny(cts.Token.WhenCanceled(), renewal.Token.WhenCanceled(), configUpdaterStartupFailed.Task);
                    }
                    catch (Exception ex)
                    {
                        logger.LogError($"Error starting protocol heads: {ex.Message}");
                    }

                    logger.LogInformation("Stopping the protocol heads...");
                    await protocolHead.CloseAsync(CancellationToken.None);

                    logger.LogInformation("Protocol heads stopped.");

                    await CloseDbStoreProviderAsync(container);
                }

            completed.Set();
            handler.ForEach(h => GC.KeepAlive(h));
            logger.LogInformation("Shutdown complete.");
            return(0);
        }