Example #1
0
        public async Task TestBasicUploading()
        {
            /* Setup mocks */
            var scraper = new Mock <IMetricsScraper>();
            var storage = new Mock <IMetricsStorage>();

            storage.Setup(s => s.GetAllMetricsAsync()).ReturnsAsync(Enumerable.Empty <Metric>());

            TaskCompletionSource <object> uploadStarted = new TaskCompletionSource <object>();
            TaskCompletionSource <bool>   finishUpload  = new TaskCompletionSource <bool>();
            var uploader = new Mock <IMetricsPublisher>();
            IEnumerable <Metric> uploadedData = Enumerable.Empty <Metric>();

            uploader.Setup(u => u.PublishAsync(It.IsAny <IEnumerable <Metric> >(), It.IsAny <CancellationToken>())).Callback((Action <IEnumerable <Metric>, CancellationToken>)((data, __) =>
            {
                uploadedData = data;
                uploadStarted.SetResult(null);
            })).Returns(finishUpload.Task);

            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            /* test */
            Task workerTask = worker.Upload(CancellationToken.None);
            await uploadStarted.Task;

            uploadedData.ToList();
            Assert.Equal(1, uploader.Invocations.Count);
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "GetAllMetricsAsync"));
            Assert.Empty(storage.Invocations.Where(i => i.Method.Name == "RemoveAllReturnedMetricsAsync"));
            finishUpload.SetResult(true);
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "GetAllMetricsAsync"));
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "RemoveAllReturnedMetricsAsync"));
        }
Example #2
0
        public async Task TestUploadContent()
        {
            /* test data */
            var metrics = Enumerable.Range(1, 10).Select(i => new Metric(DateTime.UtcNow, "test_metric", 3, $"tag_{i}")).ToList();

            /* Setup mocks */
            var scraper = new Mock <IMetricsScraper>();

            var storage = new Mock <IMetricsStorage>();

            storage.Setup(s => s.GetAllMetricsAsync()).ReturnsAsync(metrics);
            var uploader = new Mock <IMetricsPublisher>();
            IEnumerable <Metric> uploadedData = Enumerable.Empty <Metric>();

            uploader.Setup(u => u.PublishAsync(It.IsAny <IEnumerable <Metric> >(), It.IsAny <CancellationToken>())).Callback((Action <IEnumerable <Metric>, CancellationToken>)((d, _) => uploadedData = d)).ReturnsAsync(true);

            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            /* test */
            await worker.Upload(CancellationToken.None);

            Assert.Equal(metrics.OrderBy(x => x.Tags), uploadedData.OrderBy(x => x.Tags));
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "GetAllMetricsAsync"));
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "RemoveAllReturnedMetricsAsync"));
            Assert.Single(uploader.Invocations);
        }
Example #3
0
        public async Task TestScraping()
        {
            /* Setup mocks */
            Metric[] testData = new Metric[0];
            var      scraper  = new Mock <IMetricsScraper>();

            scraper.Setup(s => s.ScrapeEndpointsAsync(CancellationToken.None)).ReturnsAsync(() => testData);

            var storage = new Mock <IMetricsStorage>();
            IEnumerable <Metric> storedValues = Enumerable.Empty <Metric>();

            storage.Setup(s => s.StoreMetricsAsync(It.IsAny <IEnumerable <Metric> >())).Callback((Action <IEnumerable <Metric> >)(data => storedValues = data)).Returns(Task.CompletedTask);

            var uploader = new Mock <IMetricsPublisher>();

            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            // all values are stored
            testData = this.PrometheousMetrics(Enumerable.Range(1, 10).Select(i => ($"module_{i}", this.rand.NextDouble())).ToArray()).ToArray();
            await worker.Scrape(CancellationToken.None);

            Assert.Equal(1, scraper.Invocations.Count);
            Assert.Equal(1, storage.Invocations.Count);
            Assert.Equal(testData, storedValues);

            testData = this.PrometheousMetrics(Enumerable.Range(1, 10).Select(i => ($"module_{i}", this.rand.NextDouble())).ToArray()).ToArray();
            await worker.Scrape(CancellationToken.None);

            Assert.Equal(2, scraper.Invocations.Count);
            Assert.Equal(2, storage.Invocations.Count);
            Assert.Equal(testData, storedValues);
        }
Example #4
0
        public async Task TestNoOverlap()
        {
            /* Setup mocks */
            TaskCompletionSource <bool> scrapeTaskSource = new TaskCompletionSource <bool>();
            TaskCompletionSource <bool> uploadTaskSource = new TaskCompletionSource <bool>();

            var scraper = new Mock <IMetricsScraper>();

            scraper.Setup(s => s.ScrapeEndpointsAsync(CancellationToken.None)).Returns(async() =>
            {
                await scrapeTaskSource.Task;
                return(this.PrometheousMetrics(Enumerable.Range(1, 10).Select(i => ($"module_{i}", 1.0)).ToArray()));
            });

            var storage = new Mock <IMetricsStorage>();

            var uploader = new Mock <IMetricsPublisher>();

            uploader.Setup(u => u.PublishAsync(It.IsAny <IEnumerable <Metric> >(), It.IsAny <CancellationToken>())).Returns(async() => await uploadTaskSource.Task);

            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            /* test scraper first */
            var scrapeTask = worker.Scrape(CancellationToken.None);
            await Task.Delay(1);

            var uploadTask = worker.Upload(CancellationToken.None);
            await Task.Delay(1);

            uploadTaskSource.SetResult(true);
            await Task.Delay(1);

            Assert.False(scrapeTask.IsCompleted);
            Assert.False(uploadTask.IsCompleted);
            scrapeTaskSource.SetResult(true);
            await Task.Delay(1);

            await Task.WhenAll(scrapeTask, uploadTask);

            /* test uploader first */
            scrapeTaskSource = new TaskCompletionSource <bool>();
            uploadTaskSource = new TaskCompletionSource <bool>();

            uploadTask = worker.Upload(CancellationToken.None);
            await Task.Delay(1);

            scrapeTask = worker.Scrape(CancellationToken.None);
            await Task.Delay(1);

            scrapeTaskSource.SetResult(true);
            await Task.Delay(1);

            Assert.False(scrapeTask.IsCompleted);
            Assert.False(uploadTask.IsCompleted);
            uploadTaskSource.SetResult(true);
            await Task.Delay(1);

            await Task.WhenAll(scrapeTask, uploadTask);
        }
Example #5
0
        public async Task TestUploadIsLazy()
        {
            /* test data */
            int metricsCalls = 0;

            IEnumerable <Metric> Metrics()
            {
                metricsCalls++;
                return(Enumerable.Range(1, 10).Select(i => new Metric(DateTime.UtcNow, "1", 3, new Dictionary <string, string> {
                    { "id", $"{i}" }
                })));
            }

            /* Setup mocks */
            var scraper = new Mock <IMetricsScraper>();

            var storage = new Mock <IMetricsStorage>();

            storage.Setup(s => s.GetAllMetricsAsync()).ReturnsAsync(Metrics);

            var uploader = new Mock <IMetricsPublisher>();
            IEnumerable <Metric> uploadedData = Enumerable.Empty <Metric>();

            uploader.Setup(u => u.PublishAsync(It.IsAny <IEnumerable <Metric> >(), It.IsAny <CancellationToken>())).Callback((Action <IEnumerable <Metric>, CancellationToken>)((d, _) => uploadedData = d)).ReturnsAsync(true);

            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            /* test */
            await worker.Upload(CancellationToken.None);

            int numMetrics = 0;

            foreach (Metric metric in uploadedData)
            {
                Assert.Equal(numMetrics++ / 10 + 1, metricsCalls);
            }

            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "GetAllMetricsAsync"));
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "RemoveAllReturnedMetricsAsync"));
            Assert.Single(uploader.Invocations);
        }
Example #6
0
        public async Task TestRetryFail()
        {
            int maxRetries = 5;
            /* Setup mocks */
            var scraper = new Mock <IMetricsScraper>();
            var storage = new Mock <IMetricsStorage>();

            storage.Setup(s => s.GetAllMetricsAsync()).ReturnsAsync(Enumerable.Empty <Metric>());

            var uploader = new Mock <IMetricsPublisher>();

            uploader.Setup(u => u.PublishAsync(It.IsAny <IEnumerable <Metric> >(), It.IsAny <CancellationToken>())).ReturnsAsync(false);

            MetricsWorker.RetryStrategy = new FakeRetryStrategy(maxRetries);
            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            /* test */
            await worker.Upload(CancellationToken.None);

            Assert.Equal(maxRetries + 1, uploader.Invocations.Count); // It tries once, then retries maxRetryTimes.
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "RemoveAllReturnedMetricsAsync"));
        }
Example #7
0
        public async Task TestRetry()
        {
            int targetRetries = 10;
            /* Setup mocks */
            var scraper = new Mock <IMetricsScraper>();
            var storage = new Mock <IMetricsStorage>();

            storage.Setup(s => s.GetAllMetricsAsync()).ReturnsAsync(Enumerable.Empty <Metric>());

            var uploader      = new Mock <IMetricsPublisher>();
            int actualRetries = 0;

            uploader.Setup(u => u.PublishAsync(It.IsAny <IEnumerable <Metric> >(), It.IsAny <CancellationToken>())).ReturnsAsync(() => ++ actualRetries == targetRetries);

            MetricsWorker.RetryStrategy = new FakeRetryStrategy();
            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            /* test */
            await worker.Upload(CancellationToken.None);

            Assert.Equal(targetRetries, actualRetries);
            Assert.Equal(targetRetries, uploader.Invocations.Count);
            Assert.Single(storage.Invocations.Where(i => i.Method.Name == "RemoveAllReturnedMetricsAsync"));
        }
Example #8
0
        public static async Task <int> MainAsync(IConfiguration configuration)
        {
            // Bring up the logger before anything else so we can log errors ASAP
            ILogger logger = SetupLogger(configuration);

            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;
            string                      dockerLoggingDriver;
            Dictionary <string, string> dockerLoggingOptions;
            IEnumerable <global::Docker.DotNet.Models.AuthConfig> dockerAuthConfig;
            int configRefreshFrequencySecs;
            ExperimentalFeatures experimentalFeatures;
            MetricsConfig        metricsConfig;
            DiagnosticConfig     diagnosticConfig;
            bool useServerHeartbeat;

            try
            {
                mode = configuration.GetValue(Constants.ModeKey, "docker");
                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);

                // 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);
                dockerLoggingDriver  = configuration.GetValue <string>("DockerLoggingDriver");
                dockerLoggingOptions = configuration.GetSection("DockerLoggingOptions").Get <Dictionary <string, string> >() ?? new Dictionary <string, string>();
                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(dockerLoggingDriver, dockerLoggingOptions));
                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>();

                switch (mode.ToLowerInvariant())
                {
                case Constants.DockerMode:
                    var    dockerUri = new Uri(configuration.GetValue <string>("DockerUri"));
                    string deviceConnectionString = configuration.GetValue <string>("DeviceConnectionString");
                    IotHubConnectionStringBuilder connectionStringParser = IotHubConnectionStringBuilder.Create(deviceConnectionString);
                    deviceId       = connectionStringParser.DeviceId;
                    iothubHostname = connectionStringParser.HostName;
                    builder.RegisterModule(new AgentModule(maxRestartCount, intensiveCareTime, coolOffTimeUnitInSeconds, usePersistentStorage, storagePath, enableNonPersistentStorageBackup, storageBackupPath, storageTotalMaxWalSize, storageMaxManifestFileSize, storageMaxOpenFiles, storageLogLevel));
                    builder.RegisterModule(new DockerModule(deviceConnectionString, edgeDeviceHostName, dockerUri, dockerAuthConfig, upstreamProtocol, proxy, productInfo, closeOnIdleTimeout, idleTimeout, useServerHeartbeat, backupConfigFilePath));
                    break;

                case Constants.IotedgedMode:
                    string managementUri = configuration.GetValue <string>(Constants.EdgeletManagementUriVariableName);
                    string workloadUri   = configuration.GetValue <string>(Constants.EdgeletWorkloadUriVariableName);
                    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));
                    builder.RegisterModule(new EdgeletModule(iothubHostname, deviceId, new Uri(managementUri), new Uri(workloadUri), apiVersion, dockerAuthConfig, upstreamProtocol, proxy, productInfo, closeOnIdleTimeout, idleTimeout, performanceMetricsUpdateFrequency, useServerHeartbeat, backupConfigFilePath));
                    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:
                    managementUri      = configuration.GetValue <string>(Constants.EdgeletManagementUriVariableName);
                    workloadUri        = configuration.GetValue <string>(Constants.EdgeletWorkloadUriVariableName);
                    moduleId           = configuration.GetValue(Constants.ModuleIdVariableName, Constants.EdgeAgentModuleIdentityName);
                    moduleGenerationId = configuration.GetValue <string>(Constants.EdgeletModuleGenerationIdVariableName);
                    apiVersion         = configuration.GetValue <string>(Constants.EdgeletApiVersionVariableName);
                    iothubHostname     = configuration.GetValue <string>(Constants.IotHubHostnameVariableName);
                    deviceId           = configuration.GetValue <string>(Constants.DeviceIdVariableName);
                    string             proxyImage = configuration.GetValue <string>(K8sConstants.ProxyImageEnvKey);
                    Option <string>    proxyImagePullSecretName      = Option.Maybe(configuration.GetValue <string>(K8sConstants.ProxyImagePullSecretNameEnvKey));
                    string             proxyConfigPath               = configuration.GetValue <string>(K8sConstants.ProxyConfigPathEnvKey);
                    string             proxyConfigVolumeName         = configuration.GetValue <string>(K8sConstants.ProxyConfigVolumeEnvKey);
                    string             proxyConfigMapName            = configuration.GetValue <string>(K8sConstants.ProxyConfigMapNameEnvKey);
                    string             proxyTrustBundlePath          = configuration.GetValue <string>(K8sConstants.ProxyTrustBundlePathEnvKey);
                    string             proxyTrustBundleVolumeName    = configuration.GetValue <string>(K8sConstants.ProxyTrustBundleVolumeEnvKey);
                    string             proxyTrustBundleConfigMapName = configuration.GetValue <string>(K8sConstants.ProxyTrustBundleConfigMapEnvKey);
                    PortMapServiceType mappedServiceDefault          = GetDefaultServiceType(configuration);
                    bool          enableServiceCallTracing           = configuration.GetValue <bool>(K8sConstants.EnableK8sServiceCallTracingName);
                    bool          useMountSourceForVolumeName        = configuration.GetValue <bool>(K8sConstants.UseMountSourceForVolumeNameKey, false);
                    string        storageClassName = configuration.GetValue <string>(K8sConstants.StorageClassNameKey);
                    Option <uint> persistentVolumeClaimDefaultSizeMb = Option.Maybe(configuration.GetValue <uint?>(K8sConstants.PersistentVolumeClaimDefaultSizeInMbKey));
                    string        deviceNamespace = configuration.GetValue <string>(K8sConstants.K8sNamespaceKey);
                    var           kubernetesExperimentalFeatures = KubernetesExperimentalFeatures.Create(configuration.GetSection("experimentalFeatures"), logger);
                    var           moduleOwner = new KubernetesModuleOwner(
                        configuration.GetValue <string>(K8sConstants.EdgeK8sObjectOwnerApiVersionKey),
                        configuration.GetValue <string>(K8sConstants.EdgeK8sObjectOwnerKindKey),
                        configuration.GetValue <string>(K8sConstants.EdgeK8sObjectOwnerNameKey),
                        configuration.GetValue <string>(K8sConstants.EdgeK8sObjectOwnerUidKey));
                    bool runAsNonRoot = configuration.GetValue <bool>(K8sConstants.RunAsNonRootKey);

                    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 KubernetesModule(
                            iothubHostname,
                            deviceId,
                            edgeDeviceHostName,
                            proxyImage,
                            proxyImagePullSecretName,
                            proxyConfigPath,
                            proxyConfigVolumeName,
                            proxyConfigMapName,
                            proxyTrustBundlePath,
                            proxyTrustBundleVolumeName,
                            proxyTrustBundleConfigMapName,
                            apiVersion,
                            deviceNamespace,
                            new Uri(managementUri),
                            new Uri(workloadUri),
                            dockerAuthConfig,
                            upstreamProtocol,
                            Option.Some(productInfo),
                            mappedServiceDefault,
                            enableServiceCallTracing,
                            useMountSourceForVolumeName,
                            storageClassName,
                            persistentVolumeClaimDefaultSizeMb,
                            proxy,
                            closeOnIdleTimeout,
                            idleTimeout,
                            useServerHeartbeat,
                            kubernetesExperimentalFeatures,
                            moduleOwner,
                            runAsNonRoot));

                    IEnumerable <X509Certificate2> k8sTrustBundle = await CertificateHelper.GetTrustBundleFromEdgelet(new Uri(workloadUri), apiVersion, Constants.WorkloadApiVersion, moduleId, moduleGenerationId);

                    CertificateHelper.InstallCertificates(k8sTrustBundle, logger);

                    break;

                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);
            }

            // TODO move this code to Agent
            if (mode.ToLowerInvariant().Equals(Constants.KubernetesMode))
            {
                // Block agent startup routine until proxy sidecar container is ready
                string managementUri      = configuration.GetValue <string>(Constants.EdgeletManagementUriVariableName);
                string apiVersion         = configuration.GetValue <string>(Constants.EdgeletApiVersionVariableName);
                ProxyReadinessProbe probe = new ProxyReadinessProbe(new Uri(managementUri), apiVersion);

                CancellationTokenSource tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));
                await probe.WaitUntilProxyIsReady(tokenSource.Token);

                // Start environment operator
                IKubernetesEnvironmentOperator environmentOperator = container.Resolve <IKubernetesEnvironmentOperator>();
                environmentOperator.Start();

                // Start the edge deployment operator
                IEdgeDeploymentOperator edgeDeploymentOperator = container.Resolve <IEdgeDeploymentOperator>();
                edgeDeploymentOperator.Start();
            }

            // 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);
        }
Example #9
0
        public async Task TestTagHashing()
        {
            /* Setup mocks */
            Metric[] testData = new Metric[0];
            var      scraper  = new Mock <IMetricsScraper>();

            scraper.Setup(s => s.ScrapeEndpointsAsync(CancellationToken.None)).ReturnsAsync(() => testData);

            var storage = new Mock <IMetricsStorage>();
            IEnumerable <Metric> storedValues = Enumerable.Empty <Metric>();

            storage.Setup(s => s.StoreMetricsAsync(It.IsAny <IEnumerable <Metric> >())).Callback((Action <IEnumerable <Metric> >)(data => storedValues = data)).Returns(Task.CompletedTask);

            var uploader = new Mock <IMetricsPublisher>();

            MetricsWorker worker = new MetricsWorker(scraper.Object, storage.Object, uploader.Object);

            // test id hash
            var tags = new Dictionary <string, string>
            {
                { "ms_telemetry", true.ToString() },
                { "not_hashed", "1" },
                { "id", "my_device1/my_module_1" },
            };

            testData = new Metric[] { new Metric(DateTime.UtcNow, "test_metric", 0, tags) };
            await worker.Scrape(CancellationToken.None);

            Assert.Contains(new KeyValuePair <string, string>("not_hashed", "1"), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("id", "device/Ut4Ug5Wg2qMCvwtG08RIi0k10DkoNMqQ7AmTUKy/pMs="), storedValues.Single().Tags);

            // test id not hash edgeAgent
            tags = new Dictionary <string, string>
            {
                { "ms_telemetry", true.ToString() },
                { "not_hashed", "1" },
                { "id", "my_device1/$edgeAgent" },
            };
            testData = new Metric[] { new Metric(DateTime.UtcNow, "test_metric", 0, tags) };
            await worker.Scrape(CancellationToken.None);

            Assert.Contains(new KeyValuePair <string, string>("not_hashed", "1"), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("id", "device/$edgeAgent"), storedValues.Single().Tags);

            // test module_name hash
            tags = new Dictionary <string, string>
            {
                { "ms_telemetry", true.ToString() },
                { "not_hashed", "1" },
                { "module_name", "my_module" },
            };
            testData = new Metric[] { new Metric(DateTime.UtcNow, "test_metric", 0, tags) };
            await worker.Scrape(CancellationToken.None);

            Assert.Contains(new KeyValuePair <string, string>("not_hashed", "1"), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("module_name", "rPbHx4uTZz/x2x8rSxfPxL4egT61y7B1dlsSgWpHh6s="), storedValues.Single().Tags);

            // test module name not hash edgeAgent
            tags = new Dictionary <string, string>
            {
                { "ms_telemetry", true.ToString() },
                { "not_hashed", "1" },
                { "module_name", "$edgeAgent" },
            };
            testData = new Metric[] { new Metric(DateTime.UtcNow, "test_metric", 0, tags) };
            await worker.Scrape(CancellationToken.None);

            Assert.Contains(new KeyValuePair <string, string>("not_hashed", "1"), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("module_name", "$edgeAgent"), storedValues.Single().Tags);

            // test to from hash
            tags = new Dictionary <string, string>
            {
                { "ms_telemetry", true.ToString() },
                { "not_hashed", "1" },
                { "to", "my_module_1" },
                { "from", "my_module_2" },
                { "to_route_input", "my_module_1" },
                { "from_route_output", "my_module_2" },
            };
            testData = new Metric[] { new Metric(DateTime.UtcNow, "test_metric", 0, tags) };
            await worker.Scrape(CancellationToken.None);

            Assert.Contains(new KeyValuePair <string, string>("not_hashed", "1"), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("to", "Ut4Ug5Wg2qMCvwtG08RIi0k10DkoNMqQ7AmTUKy/pMs="), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("from", "t+TD1s4uqQrTHY7Xe/lJqasX1biQ9yK4ev5ZnScMcpk="), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("to_route_input", "Ut4Ug5Wg2qMCvwtG08RIi0k10DkoNMqQ7AmTUKy/pMs="), storedValues.Single().Tags);
            Assert.Contains(new KeyValuePair <string, string>("from_route_output", "t+TD1s4uqQrTHY7Xe/lJqasX1biQ9yK4ev5ZnScMcpk="), storedValues.Single().Tags);
        }