public void ValidatePodPropertyTranslation() { var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>()); var labels = new Dictionary <string, string> { // Add a label { "demo", "test" } }; var hostConfig = new HostConfig { // Make container privileged Privileged = true, // Add a readonly mount Binds = new List <string> { "/home/blah:/home/blah2:ro" } }; var config = new KubernetesConfig("image", CreatePodParameters.Create(labels: labels, hostConfig: hostConfig), Option.None <AuthConfig>()); var docker = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVarsDict); var module = new KubernetesModule(docker, config, EdgeletModuleOwner); var moduleLabels = new Dictionary <string, string>(); var mapper = CreateMapper(); var deployment = mapper.CreateDeployment(identity, module, moduleLabels); var pod = deployment.Spec.Template; Assert.NotNull(pod); // Validate annotation Assert.True(pod.Metadata.Annotations.ContainsKey("demo")); // Two containers should exist - proxy and the module Assert.Equal(2, pod.Spec.Containers.Count); // There should only be one module container var moduleContainer = pod.Spec.Containers.Single(p => p.Name != "proxy"); // We made this container privileged Assert.True(moduleContainer.SecurityContext.Privileged); // Validate that there are 1 mounts for module container Assert.Equal(1, moduleContainer.VolumeMounts.Count); // Validate the custom mount that we added Assert.Contains(moduleContainer.VolumeMounts, vm => vm.Name.Equals("homeblah")); var mount = moduleContainer.VolumeMounts.Single(vm => vm.Name.Equals("homeblah")); // Lets make sure that it is read only Assert.True(mount.ReadOnlyProperty); // Validate proxy container var proxyContainer = pod.Spec.Containers.Single(p => p.Name == "proxy"); // Validate that there are 2 mounts for proxy container: config and trust-bundle Assert.Equal(2, proxyContainer.VolumeMounts.Count); Assert.Contains(proxyContainer.VolumeMounts, vm => vm.Name.Equals("configVolumeName")); Assert.Contains(proxyContainer.VolumeMounts, vm => vm.Name.Equals("trustBundleVolumeName")); // Validate pod volumes Assert.Equal(3, pod.Spec.Volumes.Count); Assert.Contains(pod.Spec.Volumes, v => v.Name.Equals("homeblah")); Assert.Contains(pod.Spec.Volumes, v => v.Name.Equals("configVolumeName")); Assert.Contains(pod.Spec.Volumes, v => v.Name.Equals("trustBundleVolumeName")); // Validate no image pull secrets for public images Assert.Null(pod.Spec.ImagePullSecrets); // Validate null pod security context by default Assert.Null(pod.Spec.SecurityContext); }
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)); } }
private Option <V1Service> GetServiceFromModule(Dictionary <string, string> labels, KubernetesModule <TConfig> module, IModuleIdentity moduleIdentity) { var portList = new List <V1ServicePort>(); Option <Dictionary <string, string> > serviceAnnotations = Option.None <Dictionary <string, string> >(); bool onlyExposedPorts = true; if (module is IModule <AgentDocker.CombinedDockerConfig> moduleWithDockerConfig) { if (moduleWithDockerConfig.Config.CreateOptions?.Labels != null) { // Add annotations from Docker labels. This provides the customer a way to assign annotations to services if they want // to tie backend services to load balancers via an Ingress Controller. var annotations = new Dictionary <string, string>(); foreach (KeyValuePair <string, string> label in moduleWithDockerConfig.Config.CreateOptions?.Labels) { annotations.Add(KubeUtils.SanitizeAnnotationKey(label.Key), label.Value); } serviceAnnotations = Option.Some(annotations); } // Handle ExposedPorts entries if (moduleWithDockerConfig.Config?.CreateOptions?.ExposedPorts != null) { // Entries in the Exposed Port list just tell Docker that this container wants to listen on that port. // We interpret this as a "ClusterIP" service type listening on that exposed port, backed by this module. // Users of this Module's exposed port should be able to find the service by connecting to "<module name>:<port>" this.GetExposedPorts(moduleWithDockerConfig.Config.CreateOptions.ExposedPorts) .ForEach( exposedList => exposedList.ForEach((item) => portList.Add(new V1ServicePort(item.Port, name: $"ExposedPort-{item.Port}-{item.Protocol.ToLower()}", protocol: item.Protocol)))); } // Handle HostConfig PortBindings entries if (moduleWithDockerConfig.Config?.CreateOptions?.HostConfig?.PortBindings != null) { foreach (KeyValuePair <string, IList <DockerModels.PortBinding> > portBinding in moduleWithDockerConfig.Config?.CreateOptions?.HostConfig?.PortBindings) { string[] portProtocol = portBinding.Key.Split('/'); if (portProtocol.Length == 2) { if (int.TryParse(portProtocol[0], out int port) && this.ValidateProtocol(portProtocol[1], out string protocol)) { // Entries in Docker portMap wants to expose a port on the host (hostPort) and map it to the container's port (port) // We interpret that as the pod wants the cluster to expose a port on a public IP (hostPort), and target it to the container's port (port) foreach (DockerModels.PortBinding hostBinding in portBinding.Value) { if (int.TryParse(hostBinding.HostPort, out int hostPort)) { // If a port entry contains the same "port", then remove it and replace with a new ServicePort that contains a target. var duplicate = portList.SingleOrDefault(a => a.Port == hostPort); if (duplicate != default(V1ServicePort)) { portList.Remove(duplicate); } portList.Add(new V1ServicePort(hostPort, name: $"HostPort-{port}-{protocol.ToLower()}", protocol: protocol, targetPort: port)); onlyExposedPorts = false; } else { Events.PortBindingValue(module, portBinding.Key); } } } } } } } if (portList.Count > 0) { // Selector: by module name and device name, also how we will label this puppy. var objectMeta = new V1ObjectMeta(annotations: serviceAnnotations.GetOrElse(() => null), labels: labels, name: KubeUtils.SanitizeDNSValue(moduleIdentity.ModuleId)); // How we manage this service is dependent on the port mappings user asks for. // If the user tells us to only use ClusterIP ports, we will always set the type to ClusterIP. // If all we had were exposed ports, we will assume ClusterIP. Otherwise, we use the given value as the default service type // // If the user wants to expose the ClusterIPs port externally, they should manually create a service to expose it. // This gives the user more control as to how they want this to work. string serviceType; if (onlyExposedPorts) { serviceType = "ClusterIP"; } else { serviceType = this.defaultMapServiceType; } return(Option.Some(new V1Service(metadata: objectMeta, spec: new V1ServiceSpec(type: serviceType, ports: portList, selector: labels)))); } else { return(Option.None <V1Service>()); } }
public void CompareModule() { Dictionary <string, EnvVal> goodEnv = new Dictionary <string, EnvVal>(); Dictionary <string, EnvVal> newEnv = new Dictionary <string, EnvVal> { ["a"] = new EnvVal("B") }; IList <string> dockerEnv = new List <string> { "c=d" }; KubernetesConfig goodConfig = new KubernetesConfig("image:tag", CreateOptions(), Option.None <AuthConfig>()); KubernetesConfig imageDifferent = new KubernetesConfig("image:newtag", CreateOptions(), Option.None <AuthConfig>()); var auth1 = new AuthConfig("secret1"); KubernetesConfig auth1Config = new KubernetesConfig("image:tag", CreateOptions(), Option.Some(auth1)); var auth2 = new AuthConfig("secret2"); KubernetesConfig auth2Config = new KubernetesConfig("image:tag", CreateOptions(), Option.Some(auth2)); var auth3 = new AuthConfig("secret3"); KubernetesConfig auth3Config = new KubernetesConfig("image:tag", CreateOptions(), Option.Some(auth3)); var auth4 = new AuthConfig("secret4"); KubernetesConfig auth4Config = new KubernetesConfig("image:tag", CreateOptions(), Option.Some(auth4)); KubernetesConfig createContainerConfigDifferent = new KubernetesConfig("image:tag", CreateOptions(dockerEnv), Option.None <AuthConfig>()); ConfigurationInfo goodInfo = new ConfigurationInfo(string.Empty); var m1 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m2 = new KubernetesModule("name2", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m3 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m4 = new KubernetesModule("name1", "v2", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m5 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m6 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Stopped, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m7 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m8 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Never, goodInfo, goodEnv, goodConfig, ImagePullPolicy.OnCreate); var m9 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, imageDifferent, ImagePullPolicy.OnCreate); var m10 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, auth1Config, ImagePullPolicy.OnCreate); var m11 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, auth2Config, ImagePullPolicy.OnCreate); var m12 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, auth3Config, ImagePullPolicy.OnCreate); var m13 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, auth4Config, ImagePullPolicy.OnCreate); var m14 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, goodEnv, createContainerConfigDifferent, ImagePullPolicy.OnCreate); var m15 = new KubernetesModule("name1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, goodInfo, newEnv, goodConfig, ImagePullPolicy.OnCreate); Assert.NotEqual(m1, m2); Assert.NotEqual(m3, m4); Assert.NotEqual(m5, m6); Assert.NotEqual(m7, m8); Assert.NotEqual(m1, m9); Assert.NotEqual(m9, m1); Assert.NotEqual(m10, m9); Assert.NotEqual(m9, m10); Assert.NotEqual(m10, m11); Assert.NotEqual(m11, m10); Assert.NotEqual(m10, m12); Assert.NotEqual(m10, m13); Assert.NotEqual(m11, m14); Assert.NotEqual(m11, m15); Assert.True(m5.IsOnlyModuleStatusChanged(m6)); Assert.False(m1.IsOnlyModuleStatusChanged(m2)); Assert.False(m1.IsOnlyModuleStatusChanged(m9)); }
V1PodTemplateSpec GetPodFromModule(Dictionary <string, string> labels, KubernetesModule <TConfig> module, IModuleIdentity moduleIdentity) { if (module is IModule <AgentDocker.CombinedDockerConfig> moduleWithDockerConfig) { // pod labels var podLabels = new Dictionary <string, string>(labels); // pod annotations var podAnnotations = new Dictionary <string, string>(); podAnnotations.Add(Constants.K8sEdgeOriginalModuleId, moduleIdentity.ModuleId); // Convert docker labels to annotations because docker labels don't have the same restrictions as // Kuberenetes labels. if (moduleWithDockerConfig.Config.CreateOptions?.Labels != null) { foreach (KeyValuePair <string, string> label in moduleWithDockerConfig.Config.CreateOptions?.Labels) { podAnnotations.Add(KubeUtils.SanitizeAnnotationKey(label.Key), label.Value); } } // Per container settings: // exposed ports Option <List <V1ContainerPort> > exposedPortsOption = (moduleWithDockerConfig.Config?.CreateOptions?.ExposedPorts != null) ? this.GetExposedPorts(moduleWithDockerConfig.Config.CreateOptions.ExposedPorts).Map( servicePorts => servicePorts.Select(tuple => new V1ContainerPort(tuple.Port, protocol: tuple.Protocol)).ToList()) : Option.None <List <V1ContainerPort> >(); // privileged container Option <V1SecurityContext> securityContext = (moduleWithDockerConfig.Config?.CreateOptions?.HostConfig?.Privileged == true) ? Option.Some(new V1SecurityContext(privileged: true)) : Option.None <V1SecurityContext>(); // Environment Variables. List <V1EnvVar> env = this.CollectEnv(moduleWithDockerConfig, (KubernetesModuleIdentity)moduleIdentity); // Bind mounts (List <V1Volume> volumeList, List <V1VolumeMount> proxyMounts, List <V1VolumeMount> volumeMountList) = this.GetVolumesFromModule(moduleWithDockerConfig).GetOrElse((null, null, null)); // Image string moduleImage = moduleWithDockerConfig.Config.Image; var containerList = new List <V1Container>() { new V1Container( KubeUtils.SanitizeDNSValue(moduleIdentity.ModuleId), env: env, image: moduleImage, volumeMounts: volumeMountList, securityContext: securityContext.GetOrElse(() => null), ports: exposedPortsOption.GetOrElse(() => null)), // TODO: Add Proxy container here - configmap for proxy configuration. new V1Container( "proxy", env: env, // TODO: check these for validity for proxy. image: this.proxyImage, volumeMounts: proxyMounts) }; Option <List <V1LocalObjectReference> > imageSecret = moduleWithDockerConfig.Config.AuthConfig.Map( auth => { var secret = new ImagePullSecret(auth); var authList = new List <V1LocalObjectReference> { new V1LocalObjectReference(secret.Name) }; return(authList); }); var modulePodSpec = new V1PodSpec(containerList, volumes: volumeList, imagePullSecrets: imageSecret.GetOrElse(() => null)); var objectMeta = new V1ObjectMeta(labels: podLabels, annotations: podAnnotations); return(new V1PodTemplateSpec(metadata: objectMeta, spec: modulePodSpec)); } else { Events.InvalidModuleType(module); } return(new V1PodTemplateSpec()); }
public async void CrdCommandExecuteWithAuthCreateNewObjects() { 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 = new 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))); bool getSecretCalled = false; bool postSecretCalled = false; bool getCrdCalled = false; bool postCrdCalled = false; using (var server = new MockKubeApiServer( 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/{Ns}/secrets")) { getSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Ns}/{Constants.K8sCrdPlural}")) { 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/{Ns}/secrets")) { postSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Ns}/{Constants.K8sCrdPlural}")) { postCrdCalled = true; } } return(Task.FromResult(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(postSecretCalled, nameof(postSecretCalled)); Assert.True(getCrdCalled, nameof(getCrdCalled)); Assert.True(postCrdCalled, nameof(postCrdCalled)); } }
public async void CrdCommandExecuteTwoModulesWithSamePullSecret() { string resourceName = Hostname + Constants.K8sNameDivider + DeviceId.ToLower(); string secretName = "username-docker.io"; IModule m1 = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); var km1 = new KubernetesModule <DockerConfig>((IModule <DockerConfig>)m1); IModule m2 = new DockerModule("module2", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config2, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars); var km2 = new KubernetesModule <DockerConfig>((IModule <DockerConfig>)m2); KubernetesModule <DockerConfig>[] modules = { km1, km2 }; var token = new 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(It.IsAny <KubernetesModule <DockerConfig> >(), Runtime)).Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.Maybe(auth))); 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 MockKubeApiServer( 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/{Ns}/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/{Ns}/{Constants.K8sCrdPlural}/{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/{Ns}/secrets")) { postSecretCalled++; secretBody = httpContext.Request.Body; // save this for next query. } else if (pathStr.Contains($"namespaces/{Ns}/{Constants.K8sCrdPlural}")) { postCrdCalled++; } } else if (string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase)) { httpContext.Response.Body = httpContext.Request.Body; if (pathStr.Contains($"api/v1/namespaces/{Ns}/secrets")) { putSecretCalled = true; } else if (pathStr.Contains($"namespaces/{Ns}/{Constants.K8sCrdPlural}")) { putCrdCalled = true; } } return(Task.FromResult(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.Equal(1, postSecretCalled); Assert.False(putSecretCalled, nameof(putSecretCalled)); Assert.True(getCrdCalled, nameof(getCrdCalled)); Assert.Equal(1, postCrdCalled); Assert.False(putCrdCalled, nameof(putCrdCalled)); } }
public Option <List <V1PersistentVolumeClaim> > CreatePersistentVolumeClaims(KubernetesModule module, IDictionary <string, string> labels) => module.Config.CreateOptions.HostConfig .FlatMap(hostConfig => Option.Maybe(hostConfig.Mounts)) .Map(mounts => mounts.Where(this.ShouldCreatePvc).Select(mount => this.ExtractPvc(module, mount, labels)).ToList()) .Filter(mounts => mounts.Any());
public async void CrdCommandExecuteDeploysModulesWithEnvVars() { CombinedDockerConfig config = new CombinedDockerConfig("image", new Docker.Models.CreateContainerParameters(), Option.None <AuthConfig>()); IDictionary <string, EnvVal> moduleEnvVars = new Dictionary <string, EnvVal>() { { "ACamelCaseEnvVar", new EnvVal("ACamelCaseEnvVarValue") } }; IModule m1 = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, moduleEnvVars); var km1 = new KubernetesModule((IModule <DockerConfig>)m1, config); KubernetesModule[] modules = { km1 }; var token = default(CancellationToken); 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))); 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, modules, Runtime, configProvider.Object); await cmd.ExecuteAsync(token); 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")))); } }