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);
        }
示例#2
0
        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));
            }
        }
示例#3
0
        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>());
            }
        }
示例#4
0
        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));
        }
示例#5
0
        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());
        }
示例#6
0
        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));
            }
        }
示例#7
0
        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));
            }
        }
示例#8
0
 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"))));
            }
        }