public void CreateServiceExposedPortsOnlyCreatesExposedPortService()
        {
            var module = CreateKubernetesModule(CreatePodParameters.Create(exposedPorts: ExposedPorts));
            var mapper = new KubernetesServiceMapper(PortMapServiceType.LoadBalancer);
            Option <V1Service> result = mapper.CreateService(CreateIdentity, module, DefaultLabels);

            Assert.True(result.HasValue);
            var           service = result.OrDefault();
            V1ServicePort port80  = service.Spec.Ports.Single(p => p.Port == 80);

            Assert.Equal("exposedport-80-tcp", port80.Name);
            Assert.Equal("TCP", port80.Protocol);
            V1ServicePort port5000 = service.Spec.Ports.Single(p => p.Port == 5000);

            Assert.Equal("exposedport-5000-udp", port5000.Name);
            Assert.Equal("UDP", port5000.Protocol);
        }
        public void ServiceAnnotationsAreLabels()
        {
            var dockerLabels = new Dictionary <string, string>
            {
                ["Complicated Value that doesn't fit in k8s label name"] = "Complicated Value that doesn't fit in k8s label value",
                ["Label2"] = "Value2"
            };
            var module = CreateKubernetesModule(CreatePodParameters.Create(exposedPorts: ExposedPorts, hostConfig: HostPorts, labels: dockerLabels));
            var mapper = new KubernetesServiceMapper(PortMapServiceType.LoadBalancer);
            Option <V1Service> result = mapper.CreateService(CreateIdentity, module, DefaultLabels);

            Assert.True(result.HasValue);
            var service = result.OrDefault();

            Assert.Equal("Complicated Value that doesn't fit in k8s label value", service.Metadata.Annotations["ComplicatedValuethatdoesntfitink8slabelname"]);
            Assert.Equal("Value2", service.Metadata.Annotations["Label2"]);
        }
예제 #3
0
        public void AppliesNodeSelectorFromCreateOptionsToPodSpec()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVarsDict);
            IDictionary <string, string> nodeSelector = new Dictionary <string, string>
            {
                ["disktype"] = "ssd"
            };
            var config = new KubernetesConfig("image", CreatePodParameters.Create(nodeSelector: nodeSelector), Option.None <AuthConfig>());
            var module = new KubernetesModule(docker, config);
            var mapper = new KubernetesDeploymentMapper("namespace", "edgehub", "proxy", "configPath", "configVolumeName", "configMapName", "trustBundlePAth", "trustBundleVolumeName", "trustBundleConfigMapName", string.Empty, string.Empty, "apiVersion", new Uri("http://workload"), new Uri("http://management"));
            var labels = new Dictionary <string, string>();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            Assert.Equal(nodeSelector, deployment.Spec.Template.Spec.NodeSelector, new DictionaryComparer <string, string>());
        }
예제 #4
0
        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 void AppliesNodeSelectorFromCreateOptionsToPodSpec()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVarsDict);
            IDictionary <string, string> nodeSelector = new Dictionary <string, string>
            {
                ["disktype"] = "ssd"
            };
            var config = new KubernetesConfig("image", CreatePodParameters.Create(nodeSelector: nodeSelector), Option.None <AuthConfig>());
            var module = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var labels = new Dictionary <string, string>();
            var mapper = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            Assert.Equal(nodeSelector, deployment.Spec.Template.Spec.NodeSelector, new DictionaryComparer <string, string>());
        }
예제 #6
0
        public void SimpleDeploymentStoppedHasZeroReplicas()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var config   = new KubernetesConfig("image", CreatePodParameters.Create(), Option.None <AuthConfig>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Stopped, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Constants.DefaultPriority, DefaultConfigurationInfo, EnvVarsDict);
            var module   = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var labels   = new Dictionary <string, string>();
            var mapper   = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            Assert.NotNull(deployment);
            Assert.Equal(1, deployment.Metadata.OwnerReferences.Count);
            Assert.Equal(V1Deployment.KubeKind, deployment.Metadata.OwnerReferences[0].Kind);
            Assert.Equal(EdgeletModuleOwner.Name, deployment.Metadata.OwnerReferences[0].Name);
            Assert.Equal(0, deployment.Spec.Replicas);
        }
예제 #7
0
        public void ApplyPodSecurityContextFromCreateOptionsWhenProvided()
        {
            var identity        = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var securityContext = new V1PodSecurityContext {
                RunAsNonRoot = true, RunAsUser = 20001
            };
            var config = new KubernetesConfig("image", CreatePodParameters.Create(securityContext: securityContext), Option.Some(new AuthConfig("user-registry1")));
            var module = new KubernetesModule("module1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, DefaultConfigurationInfo, EnvVarsDict, config, ImagePullPolicy.OnCreate, EdgeletModuleOwner);
            var labels = new Dictionary <string, string>();
            var mapper = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            Assert.Equal(1, deployment.Spec.Template.Spec.ImagePullSecrets.Count);
            Assert.Equal(true, deployment.Spec.Template.Spec.SecurityContext.RunAsNonRoot);
            Assert.Equal(20001, deployment.Spec.Template.Spec.SecurityContext.RunAsUser);
        }
예제 #8
0
        public void UnknownProtocolDoesNotCreateService()
        {
            // Add unknown protocol
            var exposedPorts = new Dictionary <string, EmptyStruct> {
                { "123/XXX", default(EmptyStruct) }
            };
            var createOptions = CreatePodParameters.Create(exposedPorts: exposedPorts);
            var config        = new KubernetesConfig("image", createOptions, Option.None <AuthConfig>());
            var moduleId      = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "moduleid", Mock.Of <ICredentials>());
            var docker        = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Constants.DefaultPriority, DefaultConfigurationInfo, EnvVars);
            var module        = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var moduleLabels  = new Dictionary <string, string>();
            var mapper        = new KubernetesServiceMapper(PortMapServiceType.ClusterIP);

            var service = mapper.CreateService(moduleId, module, moduleLabels);

            Assert.False(service.HasValue);
        }
예제 #9
0
        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 void EdgeAgentEnvSettingsHaveLotsOfStuff()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "$edgeAgent", Mock.Of <ICredentials>());
            var config   = new KubernetesConfig("image", CreatePodParameters.Create(), Option.Some(new AuthConfig("user-registry1")));
            var module   = new KubernetesModule("edgeAgent", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, DefaultConfigurationInfo, EnvVarsDict, config, ImagePullPolicy.OnCreate, EdgeletModuleOwner);
            var labels   = new Dictionary <string, string>();
            var features = new Dictionary <string, bool>
            {
                ["feature1"] = true,
                ["feature2"] = false
            };
            var mapper = CreateMapper(runAsNonRoot: true, persistentVolumeName: "pvname", storageClassName: "scname", proxyImagePullSecretName: "secret name", experimentalFeatures: features);

            var deployment = mapper.CreateDeployment(identity, module, labels);

            var container = deployment.Spec.Template.Spec.Containers.Single(c => c.Name == "edgeagent");

            Assert.Equal(Constants.KubernetesMode, container.Env.Single(e => e.Name == Constants.ModeKey).Value);
            var managementUri = container.Env.Single(e => e.Name == Constants.EdgeletManagementUriVariableName);

            Assert.Equal("http://management/", container.Env.Single(e => e.Name == Constants.EdgeletManagementUriVariableName).Value);
            Assert.Equal("azure-iot-edge", container.Env.Single(e => e.Name == Constants.NetworkIdKey).Value);
            Assert.Equal("proxy", container.Env.Single(e => e.Name == KubernetesConstants.ProxyImageEnvKey).Value);
            Assert.Equal("secret name", container.Env.Single(e => e.Name == KubernetesConstants.ProxyImagePullSecretNameEnvKey).Value);
            Assert.Equal("configPath", container.Env.Single(e => e.Name == KubernetesConstants.ProxyConfigPathEnvKey).Value);
            Assert.Equal("configVolumeName", container.Env.Single(e => e.Name == KubernetesConstants.ProxyConfigVolumeEnvKey).Value);
            Assert.Equal("configMapName", container.Env.Single(e => e.Name == KubernetesConstants.ProxyConfigMapNameEnvKey).Value);
            Assert.Equal("trustBundlePath", container.Env.Single(e => e.Name == KubernetesConstants.ProxyTrustBundlePathEnvKey).Value);
            Assert.Equal("trustBundleVolumeName", container.Env.Single(e => e.Name == KubernetesConstants.ProxyTrustBundleVolumeEnvKey).Value);
            Assert.Equal("trustBundleConfigMapName", container.Env.Single(e => e.Name == KubernetesConstants.ProxyTrustBundleConfigMapEnvKey).Value);
            Assert.Equal("namespace", container.Env.Single(e => e.Name == KubernetesConstants.K8sNamespaceKey).Value);
            Assert.Equal("True", container.Env.Single(e => e.Name == KubernetesConstants.RunAsNonRootKey).Value);
            Assert.Equal("v1", container.Env.Single(e => e.Name == KubernetesConstants.EdgeK8sObjectOwnerApiVersionKey).Value);
            Assert.Equal("Deployment", container.Env.Single(e => e.Name == KubernetesConstants.EdgeK8sObjectOwnerKindKey).Value);
            Assert.Equal("iotedged", container.Env.Single(e => e.Name == KubernetesConstants.EdgeK8sObjectOwnerNameKey).Value);
            Assert.Equal("123", container.Env.Single(e => e.Name == KubernetesConstants.EdgeK8sObjectOwnerUidKey).Value);
            Assert.Equal("ClusterIP", container.Env.Single(e => e.Name == KubernetesConstants.PortMappingServiceType).Value);
            Assert.Equal("False", container.Env.Single(e => e.Name == KubernetesConstants.EnableK8sServiceCallTracingName).Value);
            Assert.Equal("pvname", container.Env.Single(e => e.Name == KubernetesConstants.PersistentVolumeNameKey).Value);
            Assert.Equal("scname", container.Env.Single(e => e.Name == KubernetesConstants.StorageClassNameKey).Value);
            Assert.Equal("100", container.Env.Single(e => e.Name == KubernetesConstants.PersistentVolumeClaimDefaultSizeInMbKey).Value);
            Assert.Equal("True", container.Env.Single(e => e.Name == "feature1").Value);
            Assert.Equal("False", container.Env.Single(e => e.Name == "feature2").Value);
        }
예제 #11
0
        public void ExposingPortsCreatesAServiceWithPorts()
        {
            var exposedPorts = new Dictionary <string, EmptyStruct> {
                ["10/TCP"] = default(EmptyStruct)
            };
            var createOptions = CreatePodParameters.Create(exposedPorts: exposedPorts);
            var config        = new KubernetesConfig("image", createOptions, Option.None <AuthConfig>());
            var docker        = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Constants.DefaultPriority, DefaultConfigurationInfo, EnvVars);
            var module        = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var moduleLabels  = new Dictionary <string, string>();
            var mapper        = new KubernetesServiceMapper(PortMapServiceType.ClusterIP);
            var moduleId      = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "moduleid", Mock.Of <ICredentials>());

            var service = mapper.CreateService(moduleId, module, moduleLabels).OrDefault();

            Assert.Equal(1, service.Spec.Ports.Count);
            AssertPort(new V1ServicePort(10, "exposedport-10-tcp", null, "TCP"), service.Spec.Ports.First());
            Assert.Equal("ClusterIP", service.Spec.Type);
        }
예제 #12
0
        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);

            KubernetesModule[] modules        = { km1 };
            ModuleSet          currentModules = new ModuleSet(new Dictionary <string, IModule> {
                ["module1"] = m1
            });

            Assert.Throws <ArgumentException>(() => new EdgeDeploymentCommand(null, ResourceName, DefaultClient, modules, currentModules, Runtime, ConfigProvider));
            Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, null, DefaultClient, modules, currentModules, Runtime, ConfigProvider));
            Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, null, modules, currentModules, Runtime, ConfigProvider));
            Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, null, currentModules, Runtime, ConfigProvider));
            Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, modules, null, Runtime, ConfigProvider));
            Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, modules, currentModules, null, ConfigProvider));
            Assert.Throws <ArgumentNullException>(() => new EdgeDeploymentCommand(Namespace, ResourceName, DefaultClient, modules, currentModules, Runtime, null));
        }
        public void CreateServiceHostPortsCreatesHostportService()
        {
            var module = CreateKubernetesModule(CreatePodParameters.Create(hostConfig: HostPorts));
            var mapper = new KubernetesServiceMapper(PortMapServiceType.LoadBalancer);
            Option <V1Service> result = mapper.CreateService(CreateIdentity, module, DefaultLabels);

            Assert.True(result.HasValue);
            var           service = result.OrDefault();
            V1ServicePort port80  = service.Spec.Ports.Single(p => p.Port == 8080);

            Assert.Equal("hostport-80-tcp", port80.Name);
            Assert.Equal("TCP", port80.Protocol);
            Assert.Equal(80, (int)port80.TargetPort);
            V1ServicePort port5000 = service.Spec.Ports.Single(p => p.Port == 5050);

            Assert.Equal("hostport-5000-udp", port5000.Name);
            Assert.Equal("UDP", port5000.Protocol);
            Assert.Equal(5000, (int)port5000.TargetPort);
        }
        public void LeaveVolumesIntactWhenNothingProvidedInCreateOptions()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVarsDict);
            var config   = new KubernetesConfig("image", CreatePodParameters.Create(), Option.None <AuthConfig>());
            var module   = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var labels   = new Dictionary <string, string>();
            var mapper   = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            // 2 volumes for proxy by default
            Assert.Equal(2, deployment.Spec.Template.Spec.Volumes.Count);
            var moduleContainer = deployment.Spec.Template.Spec.Containers.Single(container => container.Name == "module1");

            Assert.Equal(0, moduleContainer.VolumeMounts.Count);
            var proxyContainer = deployment.Spec.Template.Spec.Containers.Single(container => container.Name == "proxy");

            Assert.Equal(2, proxyContainer.VolumeMounts.Count);
        }
예제 #15
0
        public void LeaveVolumesIntactWhenNothingProvidedInCreateOptions()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVarsDict);
            var config   = new KubernetesConfig("image", CreatePodParameters.Create(), Option.None <AuthConfig>());
            var module   = new KubernetesModule(docker, config);
            var mapper   = new KubernetesDeploymentMapper("namespace", "edgehub", "proxy", "configPath", "configVolumeName", "configMapName", "trustBundlePath", "trustBundleVolumeName", "trustBindleConfigMapName", string.Empty, string.Empty, "apiVersion", new Uri("http://workload"), new Uri("http://management"));
            var labels   = new Dictionary <string, string>();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            // 2 volumes for proxy by default
            Assert.Equal(2, deployment.Spec.Template.Spec.Volumes.Count);
            var moduleContainer = deployment.Spec.Template.Spec.Containers.Single(container => container.Name == "module1");

            Assert.Equal(0, moduleContainer.VolumeMounts.Count);
            var proxyContainer = deployment.Spec.Template.Spec.Containers.Single(container => container.Name == "proxy");

            Assert.Equal(2, proxyContainer.VolumeMounts.Count);
        }
예제 #16
0
        public void EntrypointOptionsContainerCommands()
        {
            var entrypoint = new List <string> {
                "command", "argument-a"
            };
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var config   = new KubernetesConfig("image", CreatePodParameters.Create(entrypoint: entrypoint), Option.Some(new AuthConfig("user-registry1")));
            var module   = new KubernetesModule("module1", "v1", "docker", ModuleStatus.Running, RestartPolicy.Always, DefaultConfigurationInfo, EnvVarsDict, config, ImagePullPolicy.OnCreate, EdgeletModuleOwner);
            var labels   = new Dictionary <string, string>();
            var mapper   = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            var container = deployment.Spec.Template.Spec.Containers.Single(c => c.Name == "module1");

            Assert.NotNull(container.Command);
            Assert.Equal(2, container.Command.Count);
            Assert.Equal("command", container.Command[0]);
            Assert.Equal("argument-a", container.Command[1]);
        }
        public void EmptyDirMappingForVolume()
        {
            var identity   = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "ModuleId", Mock.Of <ICredentials>());
            var labels     = new Dictionary <string, string>();
            var hostConfig = VolumeMountHostConfig;
            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 mapper     = CreateMapper(storageClassName: null);
            var deployment = mapper.CreateDeployment(identity, module, labels);
            var pod        = deployment.Spec.Template;

            Assert.True(pod != null);
            var podVolume = pod.Spec.Volumes.Single(v => v.Name == "a-volume");

            Assert.NotNull(podVolume.EmptyDir);
            var podVolumeMount = pod.Spec.Containers.Single(p => p.Name != "proxy").VolumeMounts.Single(vm => vm.Name == "a-volume");

            Assert.Equal("/tmp/volume", podVolumeMount.MountPath);
            Assert.True(podVolumeMount.ReadOnlyProperty);
        }
예제 #18
0
        private KubernetesModule CreateKubernetesModuleWithHostConfig(string moduleName, string persistentVolumeName)
        {
            var hostConfig = new HostConfig
            {
                Mounts = new List <Mount>
                {
                    new Mount
                    {
                        Type     = "volume",
                        ReadOnly = true,
                        Source   = persistentVolumeName,
                        Target   = "/tmp/volume"
                    }
                }
            };
            var createOptions       = CreatePodParameters.Create(hostConfig: hostConfig);
            KubernetesConfig config = new KubernetesConfig("image", createOptions, Option.None <AuthConfig>());
            IModule          m1     = new DockerModule(moduleName, "v1", ModuleStatus.Running, Core.RestartPolicy.Always, new DockerConfig("test-image:1"), ImagePullPolicy.OnCreate, Core.Constants.DefaultStartupOrder, null, null);

            return(new KubernetesModule(m1, config, new KubernetesModuleOwner("v1", "Deployment", "iotedged", "123")));
        }
        public void AddsVolumesFromCreateOptionsToContainerSpecEvenIfTheyOverrideExistingOnes()
        {
            var identity = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "Module1", Mock.Of <ICredentials>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVarsDict);
            var volumes  = new[]
            {
                new KubernetesModuleVolumeSpec(
                    new V1Volume("homeblah", configMap: new V1ConfigMapVolumeSource(name: "additional-config-map")),
                    new[] { new V1VolumeMount(name: "homeblah", mountPath: "/home/blah") })
            };
            var hostConfig = new HostConfig {
                Binds = new List <string> {
                    "/home/blah:/home/blah2:ro"
                }
            };
            var config = new KubernetesConfig("image", CreatePodParameters.Create(volumes: volumes, hostConfig: hostConfig), Option.None <AuthConfig>());
            var module = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var labels = new Dictionary <string, string>();
            var mapper = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            // Validate module volume mounts
            var moduleContainer = deployment.Spec.Template.Spec.Containers.Single(container => container.Name == "module1");

            Assert.Equal(2, moduleContainer.VolumeMounts.Count(vm => vm.Name.Equals("homeblah")));

            // Validate proxy volume mounts
            var proxyContainer = deployment.Spec.Template.Spec.Containers.Single(p => p.Name == "proxy");

            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(4, deployment.Spec.Template.Spec.Volumes.Count);
            Assert.Equal(2, deployment.Spec.Template.Spec.Volumes.Count(v => v.Name.Equals("homeblah")));
            Assert.Contains(deployment.Spec.Template.Spec.Volumes, v => v.Name.Equals("configVolumeName"));
            Assert.Contains(deployment.Spec.Template.Spec.Volumes, v => v.Name.Equals("trustBundleVolumeName"));
        }
예제 #20
0
        public void VolumeNameMappingForVolume()
        {
            var config           = new KubernetesConfig("image", CreatePodParameters.Create(hostConfig: VolumeMountHostConfig), Option.None <AuthConfig>());
            var docker           = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Constants.DefaultPriority, DefaultConfigurationInfo, EnvVarsDict);
            var module           = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var mapper           = new KubernetesPvcMapper("pvname", "storage-class", 37);
            var resourceQuantity = new ResourceQuantity("37Mi");

            var pvcs = mapper.CreatePersistentVolumeClaims(module, DefaultLabels);

            Assert.True(pvcs.HasValue);
            var pvcList = pvcs.OrDefault();

            Assert.True(pvcList.Any());
            Assert.Equal(2, pvcList.Count);

            var aVolumeClaim = pvcList[0];

            Assert.Equal("pvname", aVolumeClaim.Metadata.Name);
            Assert.True(aVolumeClaim.Metadata.Labels.SequenceEqual(DefaultLabels));
            Assert.Equal("ReadOnlyMany", aVolumeClaim.Spec.AccessModes[0]);
            Assert.Equal("storage-class", aVolumeClaim.Spec.StorageClassName);
            Assert.Equal("pvname", aVolumeClaim.Spec.VolumeName);
            Assert.Equal(resourceQuantity, aVolumeClaim.Spec.Resources.Requests["storage"]);
            Assert.Equal(1, aVolumeClaim.Metadata.OwnerReferences.Count);
            Assert.Equal(V1Deployment.KubeKind, aVolumeClaim.Metadata.OwnerReferences[0].Kind);
            Assert.Equal(EdgeletModuleOwner.Name, aVolumeClaim.Metadata.OwnerReferences[0].Name);

            var bVolumeClaim = pvcList[1];

            Assert.Equal("pvname", bVolumeClaim.Metadata.Name);
            Assert.True(bVolumeClaim.Metadata.Labels.SequenceEqual(DefaultLabels));
            Assert.Equal("ReadWriteMany", bVolumeClaim.Spec.AccessModes[0]);
            Assert.Equal("storage-class", bVolumeClaim.Spec.StorageClassName);
            Assert.Equal("pvname", bVolumeClaim.Spec.VolumeName);
            Assert.Equal(resourceQuantity, bVolumeClaim.Spec.Resources.Requests["storage"]);
            Assert.Equal(1, bVolumeClaim.Metadata.OwnerReferences.Count);
            Assert.Equal(V1Deployment.KubeKind, bVolumeClaim.Metadata.OwnerReferences[0].Kind);
            Assert.Equal(EdgeletModuleOwner.Name, bVolumeClaim.Metadata.OwnerReferences[0].Name);
        }
        public async void Execute_UpdatesImagePullSecret_WhenExistsWithSameName()
        {
            string secretName = "username-docker.io";
            var    secretData = new Dictionary <string, byte[]> {
                [KubernetesConstants.K8sPullSecretData] = Encoding.UTF8.GetBytes("Invalid Secret Data")
            };
            var     secretMeta           = new V1ObjectMeta(name: secretName, namespaceProperty: Namespace);
            var     existingSecret       = new V1Secret("v1", secretData, type: KubernetesConstants.K8sPullSecretType, kind: "Secret", metadata: secretMeta);
            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 client = new Mock <IKubernetes>();

            client.SetupListSecrets().ReturnsAsync(() => CreateResponse(new V1SecretList {
                Items = new List <V1Secret> {
                    existingSecret
                }
            }));
            V1Secret updatedSecret = null;

            client.SetupUpdateSecret()
            .Callback(
                (V1Secret body, string name, string ns, string dryRun, string fieldManager, string pretty, Dictionary <string, List <string> > customHeaders, CancellationToken token) => { updatedSecret = body; })
            .ReturnsAsync(() => CreateResponse(new V1Secret()));
            client.SetupCreateEdgeDeploymentDefinition().ReturnsAsync(() => CreateResponse(HttpStatusCode.Created, new object()));
            var cmd = new EdgeDeploymentCommand(ResourceName, Selector, Namespace, client.Object, new[] { dockerModule }, Option.None <EdgeDeploymentDefinition>(), Runtime, configProvider.Object, EdgeletModuleOwner);

            await cmd.ExecuteAsync(CancellationToken.None);

            Assert.NotNull(updatedSecret);
            client.VerifyAll();
        }
예제 #22
0
        public void EmptyDirMappingForVolume()
        {
            var identity   = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "ModuleId", Mock.Of <ICredentials>());
            var labels     = new Dictionary <string, string>();
            var hostConfig = VolumeMountHostConfig;
            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);
            var mapper     = new KubernetesDeploymentMapper("namespace", "edgehub", "proxy", "configPath", "configVolumeName", "configMapName", "trustBundlePAth", "trustBundleVolumeName", "trustBundleConfigMapName", string.Empty, null, "apiVersion", new Uri("http://workload"), new Uri("http://management"));

            var deployment = mapper.CreateDeployment(identity, module, labels);
            var pod        = deployment.Spec.Template;

            Assert.True(pod != null);
            var podVolume = pod.Spec.Volumes.Single(v => v.Name == "a-volume");

            Assert.NotNull(podVolume.EmptyDir);
            var podVolumeMount = pod.Spec.Containers.Single(p => p.Name != "proxy").VolumeMounts.Single(vm => vm.Name == "a-volume");

            Assert.Equal("/tmp/volume", podVolumeMount.MountPath);
            Assert.True(podVolumeMount.ReadOnlyProperty);
        }
예제 #23
0
        public void SimpleServiceCreationHappyPath()
        {
            var exposedPorts = new Dictionary <string, EmptyStruct> {
                ["10/TCP"] = default(EmptyStruct)
            };
            var createOptions = CreatePodParameters.Create(exposedPorts: exposedPorts);
            var config        = new KubernetesConfig("image", createOptions, Option.None <AuthConfig>());
            var docker        = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Constants.DefaultPriority, DefaultConfigurationInfo, EnvVars);
            var module        = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var moduleLabels  = new Dictionary <string, string>();
            var mapper        = new KubernetesServiceMapper(PortMapServiceType.ClusterIP);
            var moduleId      = new ModuleIdentity("hostname", "gatewayhost", "deviceid", "moduleid", Mock.Of <ICredentials>());

            var service = mapper.CreateService(moduleId, module, moduleLabels).OrDefault();

            Assert.NotNull(service);
            Assert.Equal(1, service.Metadata.OwnerReferences.Count);
            Assert.Equal(V1Deployment.KubeKind, service.Metadata.OwnerReferences[0].Kind);
            Assert.Equal(EdgeletModuleOwner.Name, service.Metadata.OwnerReferences[0].Name);
            Assert.Equal(1, service.Spec.Ports.Count);
            Assert.Equal(0, service.Spec.Selector.Count);
        }
        public async void Execute_CreatesNewImagePullSecret_WhenEmpty()
        {
            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 client = new Mock <IKubernetes>();

            client.SetupListSecrets()
            .ReturnsAsync(
                () =>
                new HttpOperationResponse <V1SecretList>
            {
                Response = new HttpResponseMessage(),
                Body     = new V1SecretList {
                    Items = new List <V1Secret>()
                }
            });
            V1Secret createdSecret = null;

            client.SetupCreateSecret()
            .Callback(
                (V1Secret body, string ns, string dryRun, string fieldManager, string pretty, Dictionary <string, List <string> > customHeaders, CancellationToken token) => { createdSecret = body; })
            .ReturnsAsync(() => CreateResponse(HttpStatusCode.Created, new V1Secret()));
            client.SetupCreateEdgeDeploymentDefinition().ReturnsAsync(() => CreateResponse(HttpStatusCode.Created, new object()));
            var cmd = new EdgeDeploymentCommand(ResourceName, Selector, Namespace, client.Object, new[] { dockerModule }, Option.None <EdgeDeploymentDefinition>(), Runtime, configProvider.Object, EdgeletModuleOwner);

            await cmd.ExecuteAsync(CancellationToken.None);

            Assert.NotNull(createdSecret);
            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"))));
        }
예제 #26
0
        public void AppliesVolumesFromCreateOptionsToContainerSpec()
        {
            var identity = new ModuleIdentity("hostname", "deviceid", "Module1", Mock.Of <ICredentials>());
            var docker   = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, Constants.DefaultStartupOrder, DefaultConfigurationInfo, EnvVarsDict);
            var volumes  = new[]
            {
                new KubernetesModuleVolumeSpec(
                    new V1Volume("additional-volume", configMap: new V1ConfigMapVolumeSource(name: "additional-config-map")),
                    new[] { new V1VolumeMount(name: "additional-volume", mountPath: "/etc") })
            };
            var config = new KubernetesConfig("image", CreatePodParameters.Create(volumes: volumes), Option.None <AuthConfig>());
            var module = new KubernetesModule(docker, config, EdgeletModuleOwner);
            var labels = new Dictionary <string, string>();
            var mapper = CreateMapper();

            var deployment = mapper.CreateDeployment(identity, module, labels);

            // Validate module volume mounts
            var moduleContainer = deployment.Spec.Template.Spec.Containers.Single(container => container.Name == "module1");

            Assert.Equal(1, moduleContainer.VolumeMounts.Count);
            Assert.Contains(moduleContainer.VolumeMounts, vm => vm.Name.Equals("additional-volume"));

            // Validate proxy volume mounts
            var proxyContainer = deployment.Spec.Template.Spec.Containers.Single(p => p.Name == "proxy");

            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, deployment.Spec.Template.Spec.Volumes.Count);
            Assert.Contains(deployment.Spec.Template.Spec.Volumes, v => v.Name.Equals("additional-volume"));
            Assert.Contains(deployment.Spec.Template.Spec.Volumes, v => v.Name.Equals("configVolumeName"));
            Assert.Contains(deployment.Spec.Template.Spec.Volumes, v => v.Name.Equals("trustBundleVolumeName"));
        }
예제 #27
0
        public async void CrdCommandExecuteWithAuthCreateNewObjects()
        {
            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)));
            bool getSecretCalled  = false;
            bool postSecretCalled = false;
            bool getCrdCalled     = false;
            bool postCrdCalled    = false;

            using (var server = new KubernetesApiServer(
                       resp: string.Empty,
                       shouldNext: httpContext =>
            {
                string pathStr = httpContext.Request.Path.Value;
                string method = httpContext.Request.Method;
                if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    httpContext.Response.StatusCode = 404;
                    if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets"))
                    {
                        getSecretCalled = true;
                    }
                    else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}"))
                    {
                        getCrdCalled = true;
                    }
                }
                else if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    httpContext.Response.StatusCode = 201;
                    httpContext.Response.Body = httpContext.Request.Body;
                    if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets"))
                    {
                        postSecretCalled = true;
                    }
                    else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}"))
                    {
                        postCrdCalled = true;
                    }
                }

                return(Task.FromResult(false));
            }))
            {
                var client = new Kubernetes(new KubernetesClientConfiguration {
                    Host = server.Uri
                });
                var cmd = new EdgeDeploymentCommand(Namespace, ResourceName, client, new[] { dockerModule }, Runtime, configProvider.Object);

                await cmd.ExecuteAsync(CancellationToken.None);

                Assert.True(getSecretCalled, nameof(getSecretCalled));
                Assert.True(postSecretCalled, nameof(postSecretCalled));
                Assert.True(getCrdCalled, nameof(getCrdCalled));
                Assert.True(postCrdCalled, nameof(postCrdCalled));
            }
        }
예제 #28
0
        public async void CrdCommandExecuteWithAuthReplaceObjects()
        {
            string secretName = "username-docker.io";
            var    secretData = new Dictionary <string, byte[]> {
                [Constants.K8sPullSecretData] = Encoding.UTF8.GetBytes("Invalid Secret Data")
            };
            var     secretMeta           = new V1ObjectMeta(name: secretName, namespaceProperty: Namespace);
            IModule dockerModule         = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            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));
            }
        }
예제 #29
0
        public async void CrdCommandExecuteTwoModulesWithSamePullSecret()
        {
            string  secretName           = "username-docker.io";
            IModule dockerModule1        = new DockerModule("module1", "v1", ModuleStatus.Running, RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            IModule dockerModule2        = new DockerModule("module2", "v1", ModuleStatus.Running, RestartPolicy.Always, Config2, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            var     dockerConfigProvider = new Mock <ICombinedConfigProvider <CombinedDockerConfig> >();

            dockerConfigProvider.Setup(cp => cp.GetCombinedConfig(It.IsAny <DockerModule>(), Runtime))
            .Returns(() => new CombinedDockerConfig("test-image:1", Config1.CreateOptions, Option.Maybe(DockerAuth)));
            var configProvider = new Mock <ICombinedConfigProvider <CombinedKubernetesConfig> >();

            configProvider.Setup(cp => cp.GetCombinedConfig(It.IsAny <DockerModule>(), Runtime))
            .Returns(() => new CombinedKubernetesConfig("test-image:1", CreatePodParameters.Create(image: "test-image:1"), Option.Maybe(ImagePullSecret)));
            bool   getSecretCalled  = false;
            bool   putSecretCalled  = false;
            int    postSecretCalled = 0;
            bool   getCrdCalled     = false;
            bool   putCrdCalled     = false;
            int    postCrdCalled    = 0;
            Stream secretBody       = Stream.Null;

            using (var server = new KubernetesApiServer(
                       resp: string.Empty,
                       shouldNext: httpContext =>
            {
                string pathStr = httpContext.Request.Path.Value;
                string method = httpContext.Request.Method;
                if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets/{secretName}"))
                    {
                        if (secretBody == Stream.Null)
                        {
                            // 1st pass, secret should not exist
                            getSecretCalled = true;
                            httpContext.Response.StatusCode = 404;
                        }
                        else
                        {
                            // 2nd pass, use secret from creation.
                            httpContext.Response.Body = secretBody;
                        }
                    }
                    else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}"))
                    {
                        getCrdCalled = true;
                        httpContext.Response.StatusCode = 404;
                    }
                }
                else if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase))
                {
                    httpContext.Response.StatusCode = 201;
                    httpContext.Response.Body = httpContext.Request.Body;
                    if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets"))
                    {
                        postSecretCalled++;
                        secretBody = httpContext.Request.Body;     // save this for next query.
                    }
                    else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}"))
                    {
                        postCrdCalled++;
                    }
                }
                else if (string.Equals(method, "PUT", StringComparison.OrdinalIgnoreCase))
                {
                    httpContext.Response.Body = httpContext.Request.Body;
                    if (pathStr.Contains($"api/v1/namespaces/{Namespace}/secrets"))
                    {
                        putSecretCalled = true;
                    }
                    else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}"))
                    {
                        putCrdCalled = true;
                    }
                }

                return(Task.FromResult(false));
            }))
            {
                var client = new Kubernetes(new KubernetesClientConfiguration {
                    Host = server.Uri
                });
                var cmd = new EdgeDeploymentCommand(Namespace, ResourceName, client, new[] { dockerModule1, dockerModule2 }, Runtime, configProvider.Object);

                await cmd.ExecuteAsync(CancellationToken.None);

                Assert.True(getSecretCalled, nameof(getSecretCalled));
                Assert.Equal(1, postSecretCalled);
                Assert.False(putSecretCalled, nameof(putSecretCalled));
                Assert.True(getCrdCalled, nameof(getCrdCalled));
                Assert.Equal(1, postCrdCalled);
                Assert.False(putCrdCalled, nameof(putCrdCalled));
            }
        }
        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);
        }