public async void KubernetesPlannerPlanFailsWithNonDockerModules() { var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); bool getCrdCalled = 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($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}")) { getCrdCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(edgeDefinition).ToBody()); } } return(false); })) { IModule m1 = new NonDockerModule("module1", "v1", "unknown", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars, string.Empty); ModuleSet addRunning = ModuleSet.Create(m1); var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner); await Assert.ThrowsAsync <InvalidModuleException>(() => planner.PlanAsync(addRunning, ModuleSet.Empty, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty)); Assert.True(getCrdCalled); } }
public async void Execute_UpdatesEdgeDeploymentDefinition_WhenExistsWithSameName() { IModule dockerModule = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Core.Constants.DefaultPriority, DefaultConfigurationInfo, EnvVars); var dockerConfigProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >(); dockerConfigProvider.Setup(cp => cp.GetCombinedConfig(dockerModule, Runtime)) .Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.None <string>(), 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 existingDeployment = new EdgeDeploymentDefinition(KubernetesConstants.EdgeDeployment.ApiVersion, KubernetesConstants.EdgeDeployment.Kind, new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>()); var client = new Mock <IKubernetes>(); client.SetupListSecrets().ReturnsAsync(() => CreateResponse(new V1SecretList { Items = new List <V1Secret>() })); client.SetupCreateSecret().ReturnsAsync(() => CreateResponse(HttpStatusCode.Created, new V1Secret())); client.SetupUpdateEdgeDeploymentDefinition().ReturnsAsync(CreateResponse(HttpStatusCode.Created, new object())); var cmd = new EdgeDeploymentCommand(ResourceName, Selector, Namespace, client.Object, new[] { dockerModule }, Option.Some(existingDeployment), Runtime, configProvider.Object, EdgeletModuleOwner); await cmd.ExecuteAsync(CancellationToken.None); client.VerifyAll(); }
public async void GettingSameStatusTwiceReportsOnce() { var returnedStatus = EdgeDeploymentStatus.Success("Successfully deployed"); var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); var response = new HttpOperationResponse <object>() { Body = edgeDefinition, Response = new HttpResponseMessage(HttpStatusCode.OK) }; // DeployModules returns a status, confirm this is what is reported. var controller = Mock.Of <IEdgeDeploymentController>(); Mock.Get(controller).Setup(c => c.DeployModulesAsync(It.IsAny <ModuleSet>(), It.IsAny <ModuleSet>())) .ReturnsAsync(returnedStatus); var client = Mock.Of <IKubernetes>(); Mock.Get(client).Setup(c => c.ReplaceNamespacedCustomObjectStatusWithHttpMessagesAsync(It.IsAny <object>(), Constants.EdgeDeployment.Group, Constants.EdgeDeployment.Version, DeviceNamespace, Constants.EdgeDeployment.Plural, It.IsAny <string>(), null, It.IsAny <CancellationToken>())) .ReturnsAsync(response); var edgeOperator = new EdgeDeploymentOperator( ResourceName, DeviceNamespace, client, controller); await edgeOperator.EdgeDeploymentOnEventHandlerAsync(WatchEventType.Added, edgeDefinition); await edgeOperator.EdgeDeploymentOnEventHandlerAsync(WatchEventType.Modified, edgeDefinition); Mock.Get(controller).Verify(c => c.DeployModulesAsync(It.IsAny <ModuleSet>(), It.IsAny <ModuleSet>()), Times.Exactly(2)); Mock.Get(client).Verify(c => c.ReplaceNamespacedCustomObjectStatusWithHttpMessagesAsync(It.IsAny <object>(), Constants.EdgeDeployment.Group, Constants.EdgeDeployment.Version, DeviceNamespace, Constants.EdgeDeployment.Plural, It.IsAny <string>(), null, It.IsAny <CancellationToken>()), Times.Once); }
public async void CrdCommandExecuteDeploysModulesWithEnvVars() { IDictionary <string, EnvVal> moduleEnvVars = new Dictionary <string, EnvVal> { { "ACamelCaseEnvVar", new EnvVal("ACamelCaseEnvVarValue") } }; IModule dockerModule = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, moduleEnvVars); 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))); EdgeDeploymentDefinition postedEdgeDeploymentDefinition = null; bool postCrdCalled = 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)) { 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($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}")) { postCrdCalled = true; using (var reader = new StreamReader(httpContext.Response.Body)) { string crdBody = await reader.ReadToEndAsync(); postedEdgeDeploymentDefinition = JsonConvert.DeserializeObject <EdgeDeploymentDefinition>(crdBody); } } } 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(postCrdCalled); Assert.Equal("module1", postedEdgeDeploymentDefinition.Spec[0].Name); Assert.Equal("test-image:1", postedEdgeDeploymentDefinition.Spec[0].Config.Image); Assert.True(postedEdgeDeploymentDefinition.Spec[0].Env.Contains(new KeyValuePair <string, EnvVal>("ACamelCaseEnvVar", new EnvVal("ACamelCaseEnvVarValue")))); } }
public async void CrdCommandExecuteEdgeAgentGetsCurrentImage() { IModule dockerModule = new DockerModule("edgeAgent", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); IRuntimeModule currentModule = new EdgeAgentDockerRuntimeModule(AgentConfig1, ModuleStatus.Running, 0, "description", DateTime.Today, DateTime.Today, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); 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))); configProvider.Setup(cp => cp.GetCombinedConfig(currentModule, Runtime)) .Returns(() => new CombinedKubernetesConfig(AgentConfig1.Image, CreatePodParameters.Create(image: AgentConfig1.Image), Option.Maybe(ImagePullSecret))); var edgeDefinition = Option.None <EdgeDeploymentDefinition>(); KubernetesConfig kc = new KubernetesConfig(AgentConfig1.Image, CreatePodParameters.Create(), Option.None <AuthConfig>()); var edgeDefinitionCurrentModule = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>() { new KubernetesModule(currentModule, kc, EdgeletModuleOwner) }, 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)) { httpContext.Response.StatusCode = 404; } else if (string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}")) { StreamReader reader = new StreamReader(httpContext.Request.Body); string bodyText = reader.ReadToEnd(); var body = JsonConvert.DeserializeObject <EdgeDeploymentDefinition>(bodyText); edgeDefinition = Option.Maybe(body); } } return(Task.FromResult(false)); })) { var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var cmd = new EdgeDeploymentCommand(Namespace, ResourceName, client, new[] { dockerModule }, Option.Maybe(edgeDefinitionCurrentModule), Runtime, configProvider.Object, EdgeletModuleOwner); await cmd.ExecuteAsync(CancellationToken.None); Assert.True(edgeDefinition.HasValue); var receivedEdgeDefinition = edgeDefinition.OrDefault(); var agentModule = receivedEdgeDefinition.Spec[0]; Assert.Equal(AgentConfig1.Image, agentModule.Config.Image); } }
public void ConstructorThrowsOnInvalidParams() { KubernetesConfig config = new KubernetesConfig("image", CreatePodParameters.Create(), Option.None <AuthConfig>()); IModule m1 = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); KubernetesModule km1 = new KubernetesModule(m1, config, EdgeletModuleOwner); KubernetesModule[] modules = { km1 }; EdgeDeploymentDefinition edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); Assert.Throws <ArgumentException>(() => new EdgeDeploymentCommand(null, ResourceName, DefaultClient, modules, Option.Maybe(edgeDefinition), Runtime, ConfigProvider, EdgeletModuleOwner)); Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, null, DefaultClient, modules, Option.Maybe(edgeDefinition), Runtime, ConfigProvider, EdgeletModuleOwner)); Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, null, modules, Option.Maybe(edgeDefinition), Runtime, ConfigProvider, EdgeletModuleOwner)); Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, null, Option.Maybe(edgeDefinition), Runtime, ConfigProvider, EdgeletModuleOwner)); Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, modules, Option.Maybe(edgeDefinition), null, ConfigProvider, EdgeletModuleOwner)); Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, modules, Option.Maybe(edgeDefinition), Runtime, null, EdgeletModuleOwner)); Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, modules, Option.Maybe(edgeDefinition), Runtime, ConfigProvider, null)); }
public async void StatusIsFailedWithSpecialMessageWhenHttpExceptionIsThrown() { HttpOperationException controllerException = new HttpOperationException(ExceptionMessage) { Request = new HttpRequestMessageWrapper(new HttpRequestMessage(HttpMethod.Put, new Uri("http://valid-uri")), "content") }; Option <EdgeDeploymentStatus> reportedStatus = Option.None <EdgeDeploymentStatus>(); EdgeDeploymentStatus expectedStatus = new EdgeDeploymentStatus(EdgeDeploymentStatusType.Failure, $"{controllerException.Request.Method} [{controllerException.Request.RequestUri}]({controllerException.Message})"); var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); var response = new HttpOperationResponse <object>() { Body = edgeDefinition, Response = new HttpResponseMessage(HttpStatusCode.OK) }; var controller = Mock.Of <IEdgeDeploymentController>(); Mock.Get(controller).Setup(c => c.DeployModulesAsync(It.IsAny <ModuleSet>(), It.IsAny <ModuleSet>())) .ThrowsAsync(controllerException); var client = Mock.Of <IKubernetes>(); Mock.Get(client).Setup(c => c.ReplaceNamespacedCustomObjectStatusWithHttpMessagesAsync(It.IsAny <object>(), Constants.EdgeDeployment.Group, Constants.EdgeDeployment.Version, DeviceNamespace, Constants.EdgeDeployment.Plural, It.IsAny <string>(), null, It.IsAny <CancellationToken>())) .Callback((object o, string group, string version, string _namespace, string plural, string name, Dictionary <string, List <string> > headers, CancellationToken token) => { Assert.True(o is JObject); EdgeDeploymentDefinition e = ((JObject)o).ToObject <EdgeDeploymentDefinition>(); reportedStatus = e.Status; }) .ReturnsAsync(response); var edgeOperator = new EdgeDeploymentOperator( ResourceName, DeviceNamespace, client, controller); HttpOperationException ex = await Assert.ThrowsAsync <HttpOperationException>(() => edgeOperator.EdgeDeploymentOnEventHandlerAsync(WatchEventType.Added, edgeDefinition)); Assert.Equal(controllerException, ex); Assert.True(reportedStatus.HasValue); Assert.Equal(expectedStatus, reportedStatus.OrDefault()); Mock.Get(controller).VerifyAll(); Mock.Get(client).VerifyAll(); }
public async void NoProcessingDeploymentIfEdgeDeploymentNameMismatch() { var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: "not-the-resource-name"), new List <KubernetesModule>(), null); var client = Mock.Of <IKubernetes>(); var controller = Mock.Of <IEdgeDeploymentController>(); var edgeOperator = new EdgeDeploymentOperator( ResourceName, DeviceNamespace, client, controller); await edgeOperator.EdgeDeploymentOnEventHandlerAsync(WatchEventType.Added, edgeDefinition); Mock.Get(controller).VerifyAll(); Mock.Get(client).VerifyAll(); }
public async void PurgeModulesOnDelete() { var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); var client = Mock.Of <IKubernetes>(); var controller = Mock.Of <IEdgeDeploymentController>(c => c.PurgeModulesAsync() == Task.CompletedTask); var edgeOperator = new EdgeDeploymentOperator( ResourceName, DeviceNamespace, client, controller); await edgeOperator.EdgeDeploymentOnEventHandlerAsync(WatchEventType.Deleted, edgeDefinition); Mock.Get(controller).VerifyAll(); Mock.Get(client).VerifyAll(); }
public async void KubernetesPlannerPlanExistsWhenNoChanges() { IModule m1 = new DockerModule("module1", "v1", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); IModule m2 = new DockerModule("module2", "v1", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); KubernetesConfig kc1 = new KubernetesConfig("image1", CreatePodParameters.Create(), Option.None <AuthConfig>()); KubernetesConfig kc2 = new KubernetesConfig("image2", CreatePodParameters.Create(), Option.None <AuthConfig>()); var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>() { new KubernetesModule(m1, kc1, EdgeletModuleOwner), new KubernetesModule(m2, kc2, EdgeletModuleOwner) }, null); bool getCrdCalled = 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($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}")) { getCrdCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(edgeDefinition, EdgeDeploymentSerialization.SerializerSettings).ToBody()); } } return(false); })) { ModuleSet desired = ModuleSet.Create(m1, m2); ModuleSet current = ModuleSet.Create(m1, m2); var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner); var plan = await planner.PlanAsync(desired, current, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty); Assert.True(getCrdCalled); Assert.Single(plan.Commands); Assert.True(plan.Commands.First() is EdgeDeploymentCommand); } }
public async void WhatDeployModulesReturnsIsWhatIsReported() { var returnedStatus = EdgeDeploymentStatus.Success("Successfully deployed"); Option <EdgeDeploymentStatus> reportedStatus = Option.None <EdgeDeploymentStatus>(); var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); var response = new HttpOperationResponse <object>() { Body = edgeDefinition, Response = new HttpResponseMessage(HttpStatusCode.OK) }; // DeployModules returns a status, confirm this is what is reported. var controller = Mock.Of <IEdgeDeploymentController>(); Mock.Get(controller).Setup(c => c.DeployModulesAsync(It.IsAny <ModuleSet>(), It.IsAny <ModuleSet>())) .ReturnsAsync(returnedStatus); var client = Mock.Of <IKubernetes>(); Mock.Get(client).Setup(c => c.ReplaceNamespacedCustomObjectStatusWithHttpMessagesAsync(It.IsAny <object>(), Constants.EdgeDeployment.Group, Constants.EdgeDeployment.Version, DeviceNamespace, Constants.EdgeDeployment.Plural, It.IsAny <string>(), null, It.IsAny <CancellationToken>())) .Callback((object o, string group, string version, string _namespace, string plural, string name, Dictionary <string, List <string> > headers, CancellationToken token) => { Assert.True(o is JObject); EdgeDeploymentDefinition e = ((JObject)o).ToObject <EdgeDeploymentDefinition>(); reportedStatus = e.Status; }) .ReturnsAsync(response); var edgeOperator = new EdgeDeploymentOperator( ResourceName, DeviceNamespace, client, controller); await edgeOperator.EdgeDeploymentOnEventHandlerAsync(WatchEventType.Added, edgeDefinition); Assert.True(reportedStatus.HasValue); Assert.Equal(returnedStatus, reportedStatus.OrDefault()); Mock.Get(controller).VerifyAll(); Mock.Get(client).VerifyAll(); }
public async void Execute_PreservesCaseOfEnvVars_WhenModuleDeployed() { IDictionary <string, EnvVal> moduleEnvVars = new Dictionary <string, EnvVal> { { "ACamelCaseEnvVar", new EnvVal("ACamelCaseEnvVarValue") } }; IModule dockerModule = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Core.Constants.DefaultPriority, DefaultConfigurationInfo, moduleEnvVars); var dockerConfigProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >(); dockerConfigProvider.Setup(cp => cp.GetCombinedConfig(dockerModule, Runtime)) .Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.None <string>(), 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 client = new Mock <IKubernetes>(); client.SetupListSecrets().ReturnsAsync(() => CreateResponse(new V1SecretList { Items = new List <V1Secret>() })); client.SetupCreateSecret().ReturnsAsync(() => CreateResponse(HttpStatusCode.Created, new V1Secret())); EdgeDeploymentDefinition edgeDeploymentDefinition = null; client.SetupCreateEdgeDeploymentDefinition() .Callback( (object body, string group, string version, string ns, string plural, string name, Dictionary <string, List <string> > headers, CancellationToken token) => { edgeDeploymentDefinition = ((JObject)body).ToObject <EdgeDeploymentDefinition>(); }) .ReturnsAsync(() => CreateResponse <object>(edgeDeploymentDefinition)); var cmd = new EdgeDeploymentCommand(ResourceName, Selector, Namespace, client.Object, new[] { dockerModule }, Option.None <EdgeDeploymentDefinition>(), Runtime, configProvider.Object, EdgeletModuleOwner); await cmd.ExecuteAsync(CancellationToken.None); Assert.Equal("module1", edgeDeploymentDefinition.Spec[0].Name); Assert.Equal("test-image:1", edgeDeploymentDefinition.Spec[0].Config.Image); Assert.True(edgeDeploymentDefinition.Spec[0].Env.Contains(new KeyValuePair <string, EnvVal>("ACamelCaseEnvVar", new EnvVal("ACamelCaseEnvVarValue")))); }
public async void KubernetesPlannerNoModulesNoPlan() { Option <EdgeDeploymentStatus> reportedStatus = Option.None <EdgeDeploymentStatus>(); var edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null); bool getCrdCalled = 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($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}")) { getCrdCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(edgeDefinition).ToBody()); } } return(false); })) { var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri }); var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner); Plan addPlan = await planner.PlanAsync(ModuleSet.Empty, ModuleSet.Empty, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty); Assert.True(getCrdCalled); Assert.Equal(Plan.Empty, addPlan); Assert.False(reportedStatus.HasValue); } }
public async void CrdCommandExecuteWithAuthReplaceObjects() { string resourceName = Hostname + Constants.K8sNameDivider + DeviceId.ToLower(); string metaApiVersion = Constants.K8sApi + "/" + Constants.K8sApiVersion; 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: Ns); IModule m1 = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); var km1 = new KubernetesModule <DockerConfig>((IModule <DockerConfig>)m1); KubernetesModule <DockerConfig>[] modules = { km1 }; var token = default(CancellationToken); Option <IRuntimeInfo> runtimeOption = Option.Maybe(Runtime); var auth = new AuthConfig() { Username = "******", Password = "******", ServerAddress = "docker.io" }; var configProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >(); configProvider.Setup(cp => cp.GetCombinedConfig(km1, Runtime)).Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.Maybe(auth))); var existingSecret = new V1Secret("v1", secretData, type: Constants.K8sPullSecretType, kind: "Secret", metadata: secretMeta); var existingDeployment = new EdgeDeploymentDefinition <DockerConfig>(metaApiVersion, Constants.K8sCrdKind, new V1ObjectMeta(name: resourceName), new List <KubernetesModule <DockerConfig> >()); bool getSecretCalled = false; bool putSecretCalled = false; bool getCrdCalled = false; bool putCrdCalled = false; using (var server = new MockKubeApiServer( 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/{Ns}/secrets/{secretName}")) { getSecretCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(existingSecret).ToBody(), token); } else if (pathStr.Contains($"namespaces/{Ns}/{Constants.K8sCrdPlural}/{resourceName}")) { getCrdCalled = true; await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(existingDeployment).ToBody(), token); } } else if (string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"api/v1/namespaces/{Ns}/secrets/{secretName}")) { putSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Ns}/{Constants.K8sCrdPlural}/{resourceName}")) { putCrdCalled = true; } } return(false); })) { var client = new Kubernetes( new KubernetesClientConfiguration { Host = server.Uri.ToString() }); var cmd = new KubernetesCrdCommand <CombinedDockerConfig>(Ns, Hostname, DeviceId, client, modules, runtimeOption, configProvider.Object); await cmd.ExecuteAsync(token); Assert.True(getSecretCalled, nameof(getSecretCalled)); Assert.True(putSecretCalled, nameof(putSecretCalled)); Assert.True(getCrdCalled, nameof(getCrdCalled)); Assert.True(putCrdCalled, nameof(putCrdCalled)); } }
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); 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 }, 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)); } }
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; }