Beispiel #1
0
        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()));
        }
Beispiel #3
0
        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));
            }
        }
Beispiel #4
0
        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));
        }
Beispiel #5
0
        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));
            }
        }
Beispiel #6
0
        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));
            }
        }
Beispiel #7
0
        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;
        }
Beispiel #8
0
        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);
        }
Beispiel #14
0
        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();
        }