public async void RestartPlannerAddRemoveUpdate() { var factory = new TestCommandFactory(); var token = new CancellationToken(); DateTime lastStartTime = DateTime.Parse("2017-08-04T17:52:13.0419502Z", null, DateTimeStyles.RoundtripKind); DateTime lastExitTime = lastStartTime.AddDays(1); var currentModules = new List <IModule> { new TestRuntimeModule("UpdateMod1", "version1", RestartPolicy.OnUnhealthy, "test", ModuleStatus.Running, Config1, 0, "Running", lastStartTime, lastExitTime, 0, DateTime.MinValue, ModuleStatus.Running), new TestRuntimeModule("UpdateMod2", "version1", RestartPolicy.OnUnhealthy, "test", ModuleStatus.Running, Config1, 0, "Running", lastStartTime, lastExitTime, 0, DateTime.MinValue, ModuleStatus.Stopped), new TestRuntimeModule("RemoveMod1", "version1", RestartPolicy.OnUnhealthy, "test", ModuleStatus.Running, Config1, 0, "Running", lastStartTime, lastExitTime, 0, DateTime.MinValue, ModuleStatus.Running), new TestRuntimeModule("RemoveMod2", "version1", RestartPolicy.OnUnhealthy, "test", ModuleStatus.Running, Config1, 0, "Running", lastStartTime, lastExitTime, 0, DateTime.MinValue, ModuleStatus.Stopped) }; var desiredModules = new List <IModule> { new TestModule("NewMod1", "version1", "test", ModuleStatus.Running, Config1, RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars), new TestModule("NewMod2", "version1", "test", ModuleStatus.Stopped, Config1, RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars), new TestModule("UpdateMod1", "version1", "test", ModuleStatus.Running, Config1, RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars), new TestModule("UpdateMod2", "version1", "test", ModuleStatus.Stopped, Config1, RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars) }; IImmutableDictionary <string, IModuleIdentity> moduleIdentities = GetModuleIdentities(desiredModules); var planner = new RestartPlanner(factory); ModuleSet currentSet = ModuleSet.Create(currentModules.ToArray()); ModuleSet desiredSet = ModuleSet.Create(desiredModules.ToArray()); var updateExecutionList = new List <TestRecordType> { new TestRecordType(TestCommandType.TestStop, currentModules[0]), new TestRecordType(TestCommandType.TestStop, currentModules[1]), new TestRecordType(TestCommandType.TestStop, currentModules[2]), new TestRecordType(TestCommandType.TestStop, currentModules[3]), new TestRecordType(TestCommandType.TestRemove, currentModules[2]), new TestRecordType(TestCommandType.TestRemove, currentModules[3]), new TestRecordType(TestCommandType.TestCreate, desiredModules[0]), new TestRecordType(TestCommandType.TestCreate, desiredModules[1]), new TestRecordType(TestCommandType.TestStart, desiredModules[0]), new TestRecordType(TestCommandType.TestStart, desiredModules[2]), }; Plan addPlan = await planner.PlanAsync(desiredSet, currentSet, RuntimeInfo, moduleIdentities); var planRunner = new OrderedPlanRunner(); await planRunner.ExecuteAsync(1, addPlan, token); //Weak confirmation: no assumed order. factory.Recorder.ForEach(recorder => Assert.All(updateExecutionList, r => Assert.True(recorder.ExecutionList.Contains(r)))); factory.Recorder.ForEach( recorder => { // One way to validate order // UpdateMod1 Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[0])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[8]))); Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[6])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[9]))); // UpdateMod2 Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[1])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[9]))); // RemoveMod1 Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[3])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[5]))); // RemoveMod2 Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[4])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[6]))); // AddMod1 Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[6])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[8]))); // AddModTrue2 Assert.True(recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[0])) < recorder.ExecutionList.FindIndex(r => r.Equals(updateExecutionList[6]))); }); }
public async Task <ModuleSet> GetModulesAsync(CancellationToken token) { IEnumerable <ModuleRuntimeInfo> moduleStatuses = await this.moduleStatusProvider.GetModules(token); var modules = new List <IModule>(); ModuleSet moduleSet = this.deploymentConfig.GetModuleSet(); foreach (ModuleRuntimeInfo moduleRuntimeInfo in moduleStatuses) { if (moduleRuntimeInfo.Type != "docker" || !(moduleRuntimeInfo is ModuleRuntimeInfo <DockerReportedConfig> dockerRuntimeInfo)) { Events.InvalidModuleType(moduleRuntimeInfo); continue; } if (!moduleSet.Modules.TryGetValue(dockerRuntimeInfo.Name, out IModule configModule) || !(configModule is DockerModule dockerModule)) { dockerModule = new DockerModule(dockerRuntimeInfo.Name, string.Empty, ModuleStatus.Unknown, Core.RestartPolicy.Unknown, new DockerConfig(Constants.UnknownImage, new CreateContainerParameters(), Option.None <string>()), ImagePullPolicy.OnCreate, Core.Constants.HighestPriority, new ConfigurationInfo(), null); } Option <ModuleState> moduleStateOption = await this.moduleStateStore.Get(moduleRuntimeInfo.Name); ModuleState moduleState = moduleStateOption.GetOrElse(new ModuleState(0, moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue))); string image = !string.IsNullOrWhiteSpace(dockerRuntimeInfo.Config.Image) ? dockerRuntimeInfo.Config.Image : dockerModule.Config.Image; var dockerReportedConfig = new DockerReportedConfig(image, dockerModule.Config.CreateOptions, dockerRuntimeInfo.Config.ImageHash, dockerModule.Config.Digest); IModule module; switch (moduleRuntimeInfo.Name) { case Core.Constants.EdgeHubModuleName: module = new EdgeHubDockerRuntimeModule( dockerModule.DesiredStatus, dockerModule.RestartPolicy, dockerReportedConfig, (int)dockerRuntimeInfo.ExitCode, moduleRuntimeInfo.Description, moduleRuntimeInfo.StartTime.GetOrElse(DateTime.MinValue), moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue), moduleState.RestartCount, moduleState.LastRestartTimeUtc, moduleRuntimeInfo.ModuleStatus, dockerModule.ImagePullPolicy, dockerModule.Priority, dockerModule.ConfigurationInfo, dockerModule.Env); break; case Core.Constants.EdgeAgentModuleName: module = new EdgeAgentDockerRuntimeModule( dockerReportedConfig, moduleRuntimeInfo.ModuleStatus, (int)dockerRuntimeInfo.ExitCode, moduleRuntimeInfo.Description, moduleRuntimeInfo.StartTime.GetOrElse(DateTime.MinValue), moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue), dockerModule.ImagePullPolicy, dockerModule.ConfigurationInfo, dockerModule.Env); break; default: module = new DockerRuntimeModule( moduleRuntimeInfo.Name, dockerModule.Version, dockerModule.DesiredStatus, dockerModule.RestartPolicy, dockerReportedConfig, (int)dockerRuntimeInfo.ExitCode, moduleRuntimeInfo.Description, moduleRuntimeInfo.StartTime.GetOrElse(DateTime.MinValue), moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue), moduleState.RestartCount, moduleState.LastRestartTimeUtc, moduleRuntimeInfo.ModuleStatus, dockerModule.ImagePullPolicy, dockerModule.Priority, dockerModule.ConfigurationInfo, dockerModule.Env); break; } modules.Add(module); } return(ModuleSet.Create(modules.ToArray())); }
public async void CrdCommandExecuteWithAuthCreateNewObjects() { IModule dockerModule = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); ModuleSet currentModules = ModuleSet.Create(dockerModule); var dockerConfigProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >(); dockerConfigProvider.Setup(cp => cp.GetCombinedConfig(dockerModule, Runtime)) .Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.Maybe(DockerAuth))); var configProvider = new Mock <ICombinedConfigProvider <CombinedKubernetesConfig> >(); configProvider.Setup(cp => cp.GetCombinedConfig(dockerModule, Runtime)) .Returns(() => new CombinedKubernetesConfig("test-image:1", CreatePodParameters.Create(image: "test-image:1"), Option.Maybe(ImagePullSecret))); bool getSecretCalled = false; bool postSecretCalled = false; bool getCrdCalled = false; bool postCrdCalled = false; using (var server = new KubernetesApiServer( resp: string.Empty, shouldNext: httpContext => { string pathStr = httpContext.Request.Path.Value; string method = httpContext.Request.Method; if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.StatusCode = 404; if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets")) { getSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}")) { getCrdCalled = true; } } else if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.StatusCode = 201; httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets")) { postSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}")) { postCrdCalled = true; } } return(Task.FromResult(false)); })) { var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var cmd = new EdgeDeploymentCommand(Namespace, ResourceName, client, new[] { dockerModule }, currentModules, Runtime, configProvider.Object); await cmd.ExecuteAsync(CancellationToken.None); Assert.True(getSecretCalled, nameof(getSecretCalled)); Assert.True(postSecretCalled, nameof(postSecretCalled)); Assert.True(getCrdCalled, nameof(getCrdCalled)); Assert.True(postCrdCalled, nameof(postCrdCalled)); } }
public async void ReconcileAsyncOnSetPlan() { var desiredModule = new TestModule("desired", "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, new ConfigurationInfo("1"), null); var currentModule = new TestModule("current", "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, new ConfigurationInfo("1"), null); Option <TestPlanRecorder> recordKeeper = Option.Some(new TestPlanRecorder()); var moduleExecutionList = new List <TestRecordType> { new TestRecordType(TestCommandType.TestCreate, desiredModule), new TestRecordType(TestCommandType.TestRemove, currentModule) }; var commandList = new List <ICommand> { new TestCommand(TestCommandType.TestCreate, desiredModule, recordKeeper), new TestCommand(TestCommandType.TestRemove, currentModule, recordKeeper) }; var testPlan = new Plan(commandList); var token = new CancellationToken(); var runtimeInfo = Mock.Of <IRuntimeInfo>(); var deploymentConfig = new DeploymentConfig("1.0", runtimeInfo, new SystemModules(null, null), new Dictionary <string, IModule>() { ["desired"] = desiredModule }); var deploymentConfigInfo = new DeploymentConfigInfo(0, deploymentConfig); ModuleSet desiredSet = deploymentConfig.GetModuleSet(); ModuleSet currentSet = ModuleSet.Create(currentModule); var mockConfigSource = new Mock <IConfigSource>(); var mockEnvironment = new Mock <IEnvironment>(); var mockPlanner = new Mock <IPlanner>(); var planRunner = new OrderedPlanRunner(); var mockReporter = new Mock <IReporter>(); var mockModuleIdentityLifecycleManager = new Mock <IModuleIdentityLifecycleManager>(); var configStore = Mock.Of <IEntityStore <string, string> >(); var mockEnvironmentProvider = Mock.Of <IEnvironmentProvider>(m => m.Create(It.IsAny <DeploymentConfig>()) == mockEnvironment.Object); var serde = Mock.Of <ISerde <DeploymentConfigInfo> >(); var encryptionDecryptionProvider = Mock.Of <IEncryptionProvider>(); mockConfigSource.Setup(cs => cs.GetDeploymentConfigInfoAsync()) .ReturnsAsync(deploymentConfigInfo); mockEnvironment.Setup(env => env.GetModulesAsync(token)) .ReturnsAsync(currentSet); mockModuleIdentityLifecycleManager.Setup(m => m.GetModuleIdentitiesAsync(desiredSet, currentSet)) .ReturnsAsync(ImmutableDictionary <string, IModuleIdentity> .Empty); mockPlanner.Setup(pl => pl.PlanAsync(It.IsAny <ModuleSet>(), currentSet, runtimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty)) .Returns(Task.FromResult(testPlan)); mockModuleIdentityLifecycleManager.Setup(m => m.GetModuleIdentitiesAsync(It.IsAny <ModuleSet>(), currentSet)) .Returns(Task.FromResult((IImmutableDictionary <string, IModuleIdentity>)ImmutableDictionary <string, IModuleIdentity> .Empty)); mockReporter.Setup(r => r.ReportAsync(token, It.IsAny <ModuleSet>(), It.IsAny <IRuntimeInfo>(), It.IsAny <long>(), Option.Some(DeploymentStatus.Success))) .Returns(Task.CompletedTask); var agent = new Agent(mockConfigSource.Object, mockEnvironmentProvider, mockPlanner.Object, planRunner, mockReporter.Object, mockModuleIdentityLifecycleManager.Object, configStore, DeploymentConfigInfo.Empty, serde, encryptionDecryptionProvider); await agent.ReconcileAsync(token); mockEnvironment.Verify(env => env.GetModulesAsync(token), Times.Exactly(1)); mockPlanner.Verify(pl => pl.PlanAsync(It.IsAny <ModuleSet>(), currentSet, runtimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty), Times.Once); mockReporter.VerifyAll(); recordKeeper.ForEach(r => Assert.Equal(moduleExecutionList, r.ExecutionList)); }
public async void CrdCommandExecuteWithAuthReplaceObjects() { string secretName = "username-docker.io"; var secretData = new Dictionary <string, byte[]> { [Constants.K8sPullSecretData] = Encoding.UTF8.GetBytes("Invalid Secret Data") }; var secretMeta = new V1ObjectMeta(name: secretName, namespaceProperty: Namespace); IModule dockerModule = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); ModuleSet currentModules = ModuleSet.Create(dockerModule); var dockerConfigProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >(); dockerConfigProvider.Setup(cp => cp.GetCombinedConfig(dockerModule, Runtime)) .Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.Maybe(DockerAuth))); var configProvider = new Mock <ICombinedConfigProvider <CombinedKubernetesConfig> >(); configProvider.Setup(cp => cp.GetCombinedConfig(dockerModule, Runtime)) .Returns(() => new CombinedKubernetesConfig("test-image:1", CreatePodParameters.Create(image: "test-image:1"), Option.Maybe(ImagePullSecret))); var existingSecret = new V1Secret("v1", secretData, type: Constants.K8sPullSecretType, kind: "Secret", metadata: secretMeta); var existingDeployment = new EdgeDeploymentDefinition(Constants.EdgeDeployment.ApiVersion, Constants.EdgeDeployment.Kind, new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>()); bool getSecretCalled = false; bool putSecretCalled = false; bool getCrdCalled = false; bool putCrdCalled = false; using (var server = new KubernetesApiServer( resp: string.Empty, shouldNext: async httpContext => { string pathStr = httpContext.Request.Path.Value; string method = httpContext.Request.Method; if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase)) { if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets/{secretName}")) { getSecretCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(existingSecret).ToBody()); } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}")) { getCrdCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(existingDeployment).ToBody()); } } else if (string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets/{secretName}")) { putSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}")) { putCrdCalled = true; } } return(false); })) { var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var cmd = new EdgeDeploymentCommand(Namespace, ResourceName, client, new[] { dockerModule }, currentModules, Runtime, configProvider.Object); await cmd.ExecuteAsync(CancellationToken.None); Assert.True(getSecretCalled, nameof(getSecretCalled)); Assert.True(putSecretCalled, nameof(putSecretCalled)); Assert.True(getCrdCalled, nameof(getCrdCalled)); Assert.True(putCrdCalled, nameof(putCrdCalled)); } }
public async void CrdCommandExecuteTwoModulesWithSamePullSecret() { string secretName = "username-docker.io"; IModule dockerModule1 = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); IModule dockerModule2 = new DockerModule("module2", "v1", ModuleStatus.Running, RestartPolicy.Always, Config2, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); ModuleSet currentModules = ModuleSet.Create(dockerModule1); var dockerConfigProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >(); dockerConfigProvider.Setup(cp => cp.GetCombinedConfig(It.IsAny <DockerModule>(), Runtime)) .Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.Maybe(DockerAuth))); var configProvider = new Mock <ICombinedConfigProvider <CombinedKubernetesConfig> >(); configProvider.Setup(cp => cp.GetCombinedConfig(It.IsAny <DockerModule>(), Runtime)) .Returns(() => new CombinedKubernetesConfig("test-image:1", CreatePodParameters.Create(image: "test-image:1"), Option.Maybe(ImagePullSecret))); bool getSecretCalled = false; bool putSecretCalled = false; int postSecretCalled = 0; bool getCrdCalled = false; bool putCrdCalled = false; int postCrdCalled = 0; Stream secretBody = Stream.Null; using (var server = new KubernetesApiServer( resp: string.Empty, shouldNext: httpContext => { string pathStr = httpContext.Request.Path.Value; string method = httpContext.Request.Method; if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase)) { if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets/{secretName}")) { if (secretBody == Stream.Null) { // 1st pass, secret should not exist getSecretCalled = true; httpContext.Response.StatusCode = 404; } else { // 2nd pass, use secret from creation. httpContext.Response.Body = secretBody; } } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}")) { getCrdCalled = true; httpContext.Response.StatusCode = 404; } } else if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.StatusCode = 201; httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets")) { postSecretCalled++; secretBody = httpContext.Request.Body; // save this for next query. } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}")) { postCrdCalled++; } } else if (string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets")) { putSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}")) { putCrdCalled = true; } } return(Task.FromResult(false)); })) { var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var cmd = new EdgeDeploymentCommand(Namespace, ResourceName, client, new[] { dockerModule1, dockerModule2 }, currentModules, Runtime, configProvider.Object); await cmd.ExecuteAsync(CancellationToken.None); Assert.True(getSecretCalled, nameof(getSecretCalled)); Assert.Equal(1, postSecretCalled); Assert.False(putSecretCalled, nameof(putSecretCalled)); Assert.True(getCrdCalled, nameof(getCrdCalled)); Assert.Equal(1, postCrdCalled); Assert.False(putCrdCalled, nameof(putCrdCalled)); } }
private async Task ManageDeployments(V1ServiceList currentServices, V1DeploymentList currentDeployments, EdgeDeploymentDefinition <TConfig> customObject) { var desiredModules = ModuleSet.Create(customObject.Spec.ToArray()); var moduleIdentities = await this.moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desiredModules, this.currentModules); // Pull current configuration from annotations. Dictionary <string, string> currentV1ServicesFromAnnotations = this.GetCurrentServiceConfig(currentServices); // strip out edgeAgent so edgeAgent doesn't update itself. // TODO: remove this filter. var agentDeploymentName = this.DeploymentName(CoreConstants.EdgeAgentModuleName); Dictionary <string, string> currentDeploymentsFromAnnotations = this.GetCurrentDeploymentConfig(currentDeployments) .ToDictionary( pair => pair.Key, pair => pair.Value); var desiredServices = new List <V1Service>(); var desiredDeployments = new List <V1Deployment>(); foreach (KubernetesModule <TConfig> module in customObject.Spec) { var moduleId = moduleIdentities[module.Name]; if (string.Equals(module.Type, "docker")) { // Default labels var labels = new Dictionary <string, string> { [Constants.K8sEdgeModuleLabel] = KubeUtils.SanitizeLabelValue(moduleId.ModuleId), [Constants.K8sEdgeDeviceLabel] = KubeUtils.SanitizeLabelValue(this.deviceId), [Constants.K8sEdgeHubNameLabel] = KubeUtils.SanitizeLabelValue(this.iotHubHostname) }; // Create a Service for every network interface of each module. (label them with hub, device and module id) Option <V1Service> moduleService = this.GetServiceFromModule(labels, module, moduleId); moduleService.ForEach(service => desiredServices.Add(service)); // Create a Pod for each module, and a proxy container. V1PodTemplateSpec v1PodSpec = this.GetPodFromModule(labels, module, moduleId); // if this is the edge agent's deployment then it needs to run under a specific service account if (moduleIdentities[module.Name].ModuleId == CoreConstants.EdgeAgentModuleIdentityName) { v1PodSpec.Spec.ServiceAccountName = this.serviceAccountName; } // Bundle into a deployment string deploymentName = this.DeploymentName(moduleIdentities[module.Name].ModuleId); // Deployment data var deploymentMeta = new V1ObjectMeta(name: deploymentName, labels: labels); var selector = new V1LabelSelector(matchLabels: labels); var deploymentSpec = new V1DeploymentSpec(replicas: 1, selector: selector, template: v1PodSpec); desiredDeployments.Add(new V1Deployment(metadata: deploymentMeta, spec: deploymentSpec)); } else { Events.InvalidModuleType(module); } } // Find current Services/Deployments which need to be removed and updated var servicesRemoved = new List <V1Service>(currentServices.Items); servicesRemoved.RemoveAll(s => desiredServices.Exists(i => string.Equals(i.Metadata.Name, s.Metadata.Name))); var deploymentsRemoved = new List <V1Deployment>(currentDeployments.Items); deploymentsRemoved.RemoveAll( d => { return(desiredDeployments.Exists(i => string.Equals(i.Metadata.Name, d.Metadata.Name))); }); var newServices = new List <V1Service>(); desiredServices.ForEach( s => { string creationString = JsonConvert.SerializeObject(s); if (currentV1ServicesFromAnnotations.ContainsKey(s.Metadata.Name)) { string serviceAnnotation = currentV1ServicesFromAnnotations[s.Metadata.Name]; // If configuration matches, no need to update service if (string.Equals(serviceAnnotation, creationString)) { return; } if (s.Metadata.Annotations == null) { s.Metadata.Annotations = new Dictionary <string, string>(); } s.Metadata.Annotations[Constants.CreationString] = creationString; servicesRemoved.Add(s); newServices.Add(s); Events.UpdateService(s.Metadata.Name); } else { if (s.Metadata.Annotations == null) { s.Metadata.Annotations = new Dictionary <string, string>(); } s.Metadata.Annotations[Constants.CreationString] = creationString; newServices.Add(s); Events.CreateService(s.Metadata.Name); } }); var deploymentsUpdated = new List <V1Deployment>(); var newDeployments = new List <V1Deployment>(); List <V1Deployment> currentDeploymentsList = currentDeployments.Items.ToList(); desiredDeployments.ForEach( d => { if (currentDeploymentsFromAnnotations.ContainsKey(d.Metadata.Name)) { V1Deployment current = currentDeploymentsList.Find(i => string.Equals(i.Metadata.Name, d.Metadata.Name)); string currentFromAnnotation = currentDeploymentsFromAnnotations[d.Metadata.Name]; string creationString = JsonConvert.SerializeObject(d); // If configuration matches, or this is edgeAgent deployment and the images match, // no need to do update deployment if (string.Equals(currentFromAnnotation, creationString) || (string.Equals(d.Metadata.Name, this.DeploymentName(CoreConstants.EdgeAgentModuleName)) && V1DeploymentEx.ImageEquals(current, d))) { return; } d.Metadata.ResourceVersion = current.Metadata.ResourceVersion; if (d.Metadata.Annotations == null) { var annotations = new Dictionary <string, string> { [Constants.CreationString] = creationString }; d.Metadata.Annotations = annotations; } else { d.Metadata.Annotations[Constants.CreationString] = creationString; } deploymentsUpdated.Add(d); Events.UpdateDeployment(d.Metadata.Name); } else { string creationString = JsonConvert.SerializeObject(d); var annotations = new Dictionary <string, string> { [Constants.CreationString] = creationString }; d.Metadata.Annotations = annotations; newDeployments.Add(d); Events.CreateDeployment(d.Metadata.Name); } }); // Remove the old IEnumerable <Task <V1Status> > removeServiceTasks = servicesRemoved.Select( i => { Events.DeletingService(i); return(this.client.DeleteNamespacedServiceAsync(i.Metadata.Name, this.k8sNamespace, new V1DeleteOptions())); }); await Task.WhenAll(removeServiceTasks); IEnumerable <Task <V1Status> > removeDeploymentTasks = deploymentsRemoved.Select( d => { Events.DeletingDeployment(d); return(this.client.DeleteNamespacedDeployment1Async(d.Metadata.Name, this.k8sNamespace, new V1DeleteOptions(propagationPolicy: "Foreground"), propagationPolicy: "Foreground")); }); await Task.WhenAll(removeDeploymentTasks); // Create the new. IEnumerable <Task <V1Service> > createServiceTasks = newServices.Select( s => { Events.CreatingService(s); return(this.client.CreateNamespacedServiceAsync(s, this.k8sNamespace)); }); await Task.WhenAll(createServiceTasks); IEnumerable <Task <V1Deployment> > createDeploymentTasks = newDeployments.Select( deployment => { Events.CreatingDeployment(deployment); return(this.client.CreateNamespacedDeploymentAsync(deployment, this.k8sNamespace)); }); await Task.WhenAll(createDeploymentTasks); // Update the existing - should only do this when different. IEnumerable <Task <V1Deployment> > updateDeploymentTasks = deploymentsUpdated.Select(deployment => this.client.ReplaceNamespacedDeploymentAsync(deployment, deployment.Metadata.Name, this.k8sNamespace)); await Task.WhenAll(updateDeploymentTasks); this.currentModules = desiredModules; }
public void ReportsAccurateAvailability() { /* Setup */ Dictionary <string, double> runningTime = new Dictionary <string, double>(); Dictionary <string, double> expectedTime = new Dictionary <string, double>(); Action <double, string[]> OnSet(Dictionary <string, double> result) { return((val, tags) => { if (tags[0] == "edgeAgent") { return; // Ignore edgeAgent b/c it is calculated differently } result[tags[0]] = val; }); } var metricsProvider = new Mock <IMetricsProvider>(); var runningTimeGauge = new Mock <IMetricsGauge>(); runningTimeGauge.Setup(x => x.Set(It.IsAny <double>(), It.IsAny <string[]>())).Callback(OnSet(runningTime)); metricsProvider.Setup(x => x.CreateGauge( "total_time_running_correctly_seconds", It.IsAny <string>(), new List <string> { "module_name", MetricsConstants.MsTelemetry })) .Returns(runningTimeGauge.Object); var expectedTimeGauge = new Mock <IMetricsGauge>(); expectedTimeGauge.Setup(x => x.Set(It.IsAny <double>(), It.IsAny <string[]>())).Callback(OnSet(expectedTime)); metricsProvider.Setup(x => x.CreateGauge( "total_time_expected_running_seconds", It.IsAny <string>(), new List <string> { "module_name", MetricsConstants.MsTelemetry })) .Returns(expectedTimeGauge.Object); var systemTime = new Mock <ISystemTime>(); DateTime fakeTime = DateTime.Now; systemTime.Setup(x => x.UtcNow).Returns(() => fakeTime); DeploymentMetrics availabilityMetrics = new DeploymentMetrics(metricsProvider.Object, Path.GetTempPath(), systemTime.Object); (TestRuntimeModule[] current, TestModule[] desired) = GetTestModules(3); ModuleSet currentModuleSet = ModuleSet.Create(current as IModule[]); ModuleSet desiredModuleSet = ModuleSet.Create(desired); /* Test */ availabilityMetrics.ComputeAvailability(desiredModuleSet, currentModuleSet); Assert.Empty(runningTime); fakeTime = fakeTime.AddMinutes(10); availabilityMetrics.ComputeAvailability(desiredModuleSet, currentModuleSet); Assert.Equal(3, runningTime.Count); Assert.Equal(3, expectedTime.Count); Assert.Equal(600, runningTime[current[0].Name]); Assert.Equal(600, expectedTime[current[0].Name]); Assert.Equal(600, runningTime[current[1].Name]); Assert.Equal(600, expectedTime[current[1].Name]); Assert.Equal(600, runningTime[current[2].Name]); Assert.Equal(600, expectedTime[current[2].Name]); fakeTime = fakeTime.AddMinutes(10); current[1].RuntimeStatus = ModuleStatus.Failed; current[2].RuntimeStatus = ModuleStatus.Failed; availabilityMetrics.ComputeAvailability(desiredModuleSet, currentModuleSet); Assert.Equal(3, runningTime.Count); Assert.Equal(3, expectedTime.Count); Assert.Equal(1200, runningTime[current[0].Name]); Assert.Equal(1200, expectedTime[current[0].Name]); Assert.Equal(600, runningTime[current[1].Name]); Assert.Equal(1200, expectedTime[current[1].Name]); Assert.Equal(600, runningTime[current[2].Name]); Assert.Equal(1200, expectedTime[current[2].Name]); fakeTime = fakeTime.AddMinutes(10); current[1].RuntimeStatus = ModuleStatus.Running; availabilityMetrics.ComputeAvailability(desiredModuleSet, currentModuleSet); Assert.Equal(3, runningTime.Count); Assert.Equal(3, expectedTime.Count); Assert.Equal(1800, runningTime[current[0].Name]); Assert.Equal(1800, expectedTime[current[0].Name]); Assert.Equal(1200, runningTime[current[1].Name]); Assert.Equal(1800, expectedTime[current[1].Name]); Assert.Equal(600, runningTime[current[2].Name]); Assert.Equal(1800, expectedTime[current[2].Name]); }
public async Task TestGetModuleIdentities_WhenOffline_ReturnsEmptyList() { const string Name = "test-filters"; var serviceClient = new Mock <IServiceClient>(); string hostname = "hostname"; string deviceId = "deviceId"; string moduleSharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primaryModuleAccessKey")); string gatewayHostName = "localhost"; serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(ImmutableList <Module> .Empty.AsEnumerable())); // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. // ReSharper disable PossibleMultipleEnumeration serviceClient.Setup(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => m.Count() == 1 && m.First() == Name))).ThrowsAsync(new InvalidOperationException()); // ReSharper restore PossibleMultipleEnumeration var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars); IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) .GetModuleIdentitiesAsync(ModuleSet.Create(module), ModuleSet.Empty); serviceClient.VerifyAll(); Assert.False(modulesIdentities.Any()); }
public async Task TestGetModulesIdentity_WithUpdatedModules_NoAccessKey_ShouldUpdateIdentities() { const string Name = "test-filters"; var serviceModuleIdentity = new Module("device1", Name); serviceModuleIdentity.Authentication = new AuthenticationMechanism(); serviceModuleIdentity.Authentication.Type = AuthenticationType.Sas; var serviceClient = new Mock <IServiceClient>(); string hostname = "hostname"; string deviceId = "deviceId"; string sharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); string gatewayHostName = "localhost"; Module[] serviceIdentities = { serviceModuleIdentity }; serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); serviceClient.Setup(sc => sc.UpdateModules(It.IsAny <IEnumerable <Module> >())).Callback( (IEnumerable <Module> modules) => { foreach (Module m in modules) { m.Authentication.SymmetricKey = new SymmetricKey(); m.Authentication.SymmetricKey.PrimaryKey = sharedAccessKey; } }).Returns(Task.FromResult(serviceIdentities)); var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars); IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); serviceClient.Verify(sc => sc.UpdateModules(It.IsAny <IEnumerable <Module> >()), Times.Once()); Assert.True(modulesIdentities.Count == 1); }
public async Task TestGetModulesIdentity_WithUpdatedModules_NoServiceIdentity_ShouldCreateIdentities() { const string Name = "test-filters"; var serviceClient = new Mock <IServiceClient>(); string hostname = "hostname"; string deviceId = "deviceId"; string moduleSharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primaryModuleAccessKey")); string gatewayHostName = "localhost"; serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(ImmutableList <Module> .Empty.AsEnumerable())); var createdModuleIdentity = new Module("device1", Name); createdModuleIdentity.Authentication = new AuthenticationMechanism(); createdModuleIdentity.Authentication.Type = AuthenticationType.Sas; createdModuleIdentity.Authentication.SymmetricKey.PrimaryKey = moduleSharedAccessKey; Module[] updatedServiceIdentities = { createdModuleIdentity }; // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. // ReSharper disable PossibleMultipleEnumeration serviceClient.Setup(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => m.Count() == 1 && m.First() == Name))).Returns(Task.FromResult(updatedServiceIdentities)); // ReSharper restore PossibleMultipleEnumeration var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars); IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); // If we change to IList Mock doesn't recognize and making it a non Lambda would add a lot of complexity on this code. // ReSharper disable PossibleMultipleEnumeration serviceClient.Verify(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => m.Count() == 1 && m.First() == Name)), Times.Once()); // ReSharper restore PossibleMultipleEnumeration Assert.True(modulesIdentities.Count == 1); }
public async Task TestGetModulesIdentity_EdgeHubTest() { const string Name = "filter"; var symmetricKey = new SymmetricKey(); symmetricKey.PrimaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); symmetricKey.SecondaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("secondarySymmetricKey")); var serviceModuleFilterIdentity = new Module("device1", Name) { Authentication = new AuthenticationMechanism { SymmetricKey = symmetricKey } }; var serviceModuleEdgeHubIdentity = new Module("device1", Constants.EdgeHubModuleIdentityName) { Authentication = new AuthenticationMechanism { SymmetricKey = symmetricKey } }; var serviceClient = new Mock <IServiceClient>(); string hostname = "hostname"; string deviceId = "deviceId"; string gatewayHostName = "localhost"; Module[] serviceIdentities = { serviceModuleFilterIdentity, serviceModuleEdgeHubIdentity }; serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); serviceClient.Setup(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); serviceClient.Setup(sc => sc.UpdateModules(It.Is <IEnumerable <Module> >(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); var testMod = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars); var edgeHubMod = new TestModule(Constants.EdgeHubModuleName, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.Always, DefaultConfigurationInfo, EnvVars); IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) .GetModuleIdentitiesAsync(ModuleSet.Create(testMod, edgeHubMod), ModuleSet.Empty); serviceClient.Verify(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => !m.Any())), Times.Once); serviceClient.Verify(sc => sc.UpdateModules(It.Is <IEnumerable <Module> >(m => !m.Any())), Times.Once); Assert.True(modulesIdentities.Count == 2); IModuleIdentity edgeHubModuleIdentity = modulesIdentities[Constants.EdgeHubModuleName]; Assert.NotNull(edgeHubModuleIdentity); var edgeHubCreds = edgeHubModuleIdentity.Credentials as ConnectionStringCredentials; Assert.NotNull(edgeHubCreds); Assert.Equal("HostName=hostname;DeviceId=deviceId;ModuleId=$edgeHub;SharedAccessKey=cHJpbWFyeVN5bW1ldHJpY0tleQ==", edgeHubCreds.ConnectionString); IModuleIdentity testModuleIdentity = modulesIdentities[Name]; Assert.NotNull(testModuleIdentity); var testModCreds = testModuleIdentity.Credentials as ConnectionStringCredentials; Assert.NotNull(testModCreds); Assert.Equal("HostName=hostname;DeviceId=deviceId;ModuleId=filter;SharedAccessKey=cHJpbWFyeVN5bW1ldHJpY0tleQ==;GatewayHostName=localhost", testModCreds.ConnectionString); }
public async Task TestGetModulesIdentity_WithUpdatedModules_AuthTypeNotSas_ShouldUpdateIdentities() { const string Name = "test-filters"; string primaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); string secondaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("secondarySymmetricKey")); var serviceModuleIdentity = new Module("device1", Name); serviceModuleIdentity.Authentication = new AuthenticationMechanism(); serviceModuleIdentity.Authentication.Type = AuthenticationType.CertificateAuthority; var thumbprint = new X509Thumbprint(); thumbprint.PrimaryThumbprint = primaryKey; thumbprint.SecondaryThumbprint = secondaryKey; serviceModuleIdentity.Authentication.X509Thumbprint = thumbprint; var serviceClient = new Mock <IServiceClient>(); string hostname = "hostname.fake.com"; string deviceId = "deviceId"; string gatewayHostName = "localhost"; Module[] serviceIdentities = { serviceModuleIdentity }; serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); serviceClient.Setup(sc => sc.UpdateModules(It.IsAny <IEnumerable <Module> >())).Callback( (IEnumerable <Module> modules) => { foreach (Module m in modules) { m.Authentication.SymmetricKey = new SymmetricKey(); m.Authentication.SymmetricKey.PrimaryKey = primaryKey; } }).Returns(Task.FromResult(serviceIdentities)); var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, EnvVars); IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) .GetModuleIdentitiesAsync(ModuleSet.Create(new IModule[] { module }), ModuleSet.Empty); serviceClient.Verify(sc => sc.UpdateModules(It.IsAny <IEnumerable <Module> >()), Times.Once()); Assert.True(modulesIdentities.Count() == 1); var creds = modulesIdentities.First().Value.Credentials as ConnectionStringCredentials; Assert.NotNull(creds); IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(creds.ConnectionString); Assert.NotNull(connectionString.SharedAccessKey); }
public async Task TestGetModulesIdentity_WithUpdatedModules_HasAccessKey_ShouldNotUpdate() { const string Name = "test-filters"; var serviceModuleIdentity = new Module("device1", Name); serviceModuleIdentity.Authentication = new AuthenticationMechanism(); var symmetricKey = new SymmetricKey(); symmetricKey.PrimaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("primarySymmetricKey")); symmetricKey.SecondaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("secondarySymmetricKey")); serviceModuleIdentity.Authentication.SymmetricKey = symmetricKey; var serviceClient = new Mock <IServiceClient>(); string hostname = "hostname"; string deviceId = "deviceId"; string gatewayHostName = "localhost"; Module[] serviceIdentities = { serviceModuleIdentity }; serviceClient.Setup(sc => sc.GetModules()).Returns(Task.FromResult(serviceIdentities.AsEnumerable())); serviceClient.Setup(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); serviceClient.Setup(sc => sc.UpdateModules(It.Is <IEnumerable <Module> >(m => !m.Any()))).Returns(Task.FromResult(new Module[0])); var module = new TestModule(Name, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVars); IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await new ModuleIdentityLifecycleManager(serviceClient.Object, hostname, deviceId, gatewayHostName) .GetModuleIdentitiesAsync(ModuleSet.Create(module), ModuleSet.Empty); serviceClient.Verify(sc => sc.CreateModules(It.Is <IEnumerable <string> >(m => !m.Any())), Times.Once); serviceClient.Verify(sc => sc.UpdateModules(It.Is <IEnumerable <Module> >(m => !m.Any())), Times.Once); Assert.True(modulesIdentities.Count == 1); }
public async Task TestGetModulesIdentity_WithUpdatedModules_ShouldUpdateIdentities() { // Arrange const string Module1 = "module1"; var identity1 = new Identity { ModuleId = Module1, ManagedBy = "IotEdge", GenerationId = Guid.NewGuid().ToString() }; const string Module2 = "module2"; var identity2 = new Identity { ModuleId = Module2, ManagedBy = "Me", GenerationId = Guid.NewGuid().ToString() }; const string Module3 = "module3"; var identity3 = new Identity { ModuleId = Module3, ManagedBy = Constants.ModuleIdentityEdgeManagedByValue, GenerationId = Guid.NewGuid().ToString() }; const string Module4 = "$edgeHub"; var identity4 = new Identity { ModuleId = Module4, ManagedBy = Constants.ModuleIdentityEdgeManagedByValue, GenerationId = Guid.NewGuid().ToString() }; // We should NOT get an update request for this identity const string Module5 = "$edgeAgent"; var identity5 = new Identity { ModuleId = Module5, ManagedBy = Constants.ModuleIdentityEdgeManagedByValue, GenerationId = Guid.NewGuid().ToString() }; var identityManager = Mock.Of <IIdentityManager>(m => m.GetIdentities() == Task.FromResult(new List <Identity>() { identity2, identity3, identity4, identity5 }.AsEnumerable()) && m.CreateIdentityAsync(Module1, Constants.ModuleIdentityEdgeManagedByValue) == Task.FromResult(identity1) && m.UpdateIdentityAsync(identity2.ModuleId, identity2.GenerationId, identity2.ManagedBy) == Task.FromResult(identity2) && m.UpdateIdentityAsync(identity3.ModuleId, identity3.GenerationId, identity3.ManagedBy) == Task.FromResult(identity3) && m.UpdateIdentityAsync(identity4.ModuleId, identity4.GenerationId, identity4.ManagedBy) == Task.FromResult(identity4)); var moduleIdentityLifecycleManager = new ModuleIdentityLifecycleManager(identityManager, ModuleIdentityProviderServiceBuilder, EdgeletUri); var envVar = new Dictionary <string, EnvVal>(); var module1 = new TestModule(Module1, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, envVar); var module2 = new TestModule(Module2, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, envVar); var module3 = new TestModule(Module3, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, envVar); var module4 = new TestModule(Module4, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, envVar); var module5 = new TestModule(Module5, "v1", "test", ModuleStatus.Running, new TestConfig("image"), RestartPolicy.OnUnhealthy, DefaultConfigurationInfo, envVar); ModuleSet desired = ModuleSet.Create(module1, module2.CloneWithImage("image2"), module3.CloneWithImage("image2"), module4.CloneWithImage("image2"), module5.CloneWithImage("image2")); ModuleSet current = ModuleSet.Create(module2, module3, module4, module5); // Act IImmutableDictionary <string, IModuleIdentity> modulesIdentities = await moduleIdentityLifecycleManager.GetModuleIdentitiesAsync(desired, current); // Assert Assert.Equal(5, modulesIdentities.Count); Assert.True(modulesIdentities.TryGetValue(Module1, out IModuleIdentity moduleIdentity1)); Assert.Equal(Module1, moduleIdentity1.ModuleId); Assert.True(modulesIdentities.TryGetValue(Module2, out IModuleIdentity moduleIdentity2)); Assert.Equal(Module2, moduleIdentity2.ModuleId); Assert.True(modulesIdentities.TryGetValue(Module3, out IModuleIdentity moduleIdentity3)); Assert.Equal(Module3, moduleIdentity3.ModuleId); Assert.True(modulesIdentities.TryGetValue("edgeHub", out IModuleIdentity moduleIdentity4)); Assert.Equal(Module4, moduleIdentity4.ModuleId); Assert.IsType <IdentityProviderServiceCredentials>(moduleIdentity1.Credentials); Mock.Get(identityManager).Verify(); }