public async void KubernetesPlannerPlanFailsWithNonDockerModules()
        {
            var  edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null);
            bool getCrdCalled   = false;

            using (var server = new KubernetesApiServer(
                       resp: string.Empty,
                       shouldNext: async httpContext =>
            {
                string pathStr = httpContext.Request.Path.Value;
                string method = httpContext.Request.Method;
                if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}"))
                    {
                        getCrdCalled = true;
                        await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(edgeDefinition).ToBody());
                    }
                }

                return(false);
            }))
            {
                IModule   m1         = new NonDockerModule("module1", "v1", "unknown", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars, string.Empty);
                ModuleSet addRunning = ModuleSet.Create(m1);

                var client = new Kubernetes(new KubernetesClientConfiguration {
                    Host = server.Uri
                });
                var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner);
                await Assert.ThrowsAsync <InvalidModuleException>(() => planner.PlanAsync(addRunning, ModuleSet.Empty, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty));

                Assert.True(getCrdCalled);
            }
        }
                public async ValueTask DisposeAsync()
                {
                    await PortForwardingSocket.DisposeAsync()
                    .ConfigureAwait(false);

                    await KubernetesApiServer.DisposeAsync()
                    .ConfigureAwait(false);

                    _memoryOwner.Dispose();
                }
                public Fixture(
                    IServiceContainer container)
                {
                    PortForwardingSocket =
                        SocketTestFramework.SocketTestFramework.InMemory();
                    container.RegisterSingleton(
                        () => PortForwardingSocket
                        .NetworkServerFactory);

                    KubernetesApiServer =
                        global::Kubernetes.Test.API.Server.TestFramework.Start();
                    container.RegisterSingleton(
                        () => KubernetesApiServer
                        .CreateKubernetesConfiguration());
                }
        public async void KubernetesPlannerPlanExistsWhenNoChanges()
        {
            IModule          m1  = new DockerModule("module1", "v1", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            IModule          m2  = new DockerModule("module2", "v1", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            KubernetesConfig kc1 = new KubernetesConfig("image1", CreatePodParameters.Create(), Option.None <AuthConfig>());
            KubernetesConfig kc2 = new KubernetesConfig("image2", CreatePodParameters.Create(), Option.None <AuthConfig>());
            var edgeDefinition   = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>()
            {
                new KubernetesModule(m1, kc1, EdgeletModuleOwner), new KubernetesModule(m2, kc2, EdgeletModuleOwner)
            }, null);
            bool getCrdCalled = false;

            using (var server = new KubernetesApiServer(
                       resp: string.Empty,
                       shouldNext: async httpContext =>
            {
                string pathStr = httpContext.Request.Path.Value;
                string method = httpContext.Request.Method;
                if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}"))
                    {
                        getCrdCalled = true;
                        await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(edgeDefinition, EdgeDeploymentSerialization.SerializerSettings).ToBody());
                    }
                }

                return(false);
            }))
            {
                ModuleSet desired = ModuleSet.Create(m1, m2);
                ModuleSet current = ModuleSet.Create(m1, m2);

                var client = new Kubernetes(new KubernetesClientConfiguration {
                    Host = server.Uri
                });
                var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner);
                var plan    = await planner.PlanAsync(desired, current, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty);

                Assert.True(getCrdCalled);
                Assert.Single(plan.Commands);
                Assert.True(plan.Commands.First() is EdgeDeploymentCommand);
            }
        }
        public async void KubernetesPlannerPlanExistsWhenDeploymentsQueryFails()
        {
            bool getCrdCalled = 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))
                {
                    if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}"))
                    {
                        getCrdCalled = true;
                        httpContext.Response.StatusCode = 404;
                    }
                }

                return(Task.FromResult(false));
            }))
            {
                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);
                ModuleSet desired = ModuleSet.Create(m1);
                ModuleSet current = ModuleSet.Create(m2);

                var client = new Kubernetes(new KubernetesClientConfiguration {
                    Host = server.Uri
                });
                var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner);
                var plan    = await planner.PlanAsync(desired, current, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty);

                Assert.True(getCrdCalled);
                Assert.Single(plan.Commands);
                Assert.True(plan.Commands.First() is EdgeDeploymentCommand);
            }
        }
        public async void KubernetesPlannerNoModulesNoPlan()
        {
            Option <EdgeDeploymentStatus> reportedStatus = Option.None <EdgeDeploymentStatus>();
            var  edgeDefinition = new EdgeDeploymentDefinition("v1", "EdgeDeployment", new V1ObjectMeta(name: ResourceName), new List <KubernetesModule>(), null);
            bool getCrdCalled   = false;

            using (var server = new KubernetesApiServer(
                       resp: string.Empty,
                       shouldNext: async httpContext =>
            {
                string pathStr = httpContext.Request.Path.Value;
                string method = httpContext.Request.Method;
                if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{ResourceName}"))
                    {
                        getCrdCalled = true;
                        await httpContext.Response.Body.WriteAsync(JsonConvert.SerializeObject(edgeDefinition).ToBody());
                    }
                }

                return(false);
            }))
            {
                var client = new Kubernetes(new KubernetesClientConfiguration {
                    Host = server.Uri
                });
                var planner = new KubernetesPlanner(Namespace, ResourceName, client, DefaultCommandFactory, ConfigProvider, EdgeletModuleOwner);

                Plan addPlan = await planner.PlanAsync(ModuleSet.Empty, ModuleSet.Empty, RuntimeInfo, ImmutableDictionary <string, IModuleIdentity> .Empty);

                Assert.True(getCrdCalled);
                Assert.Equal(Plan.Empty, addPlan);
                Assert.False(reportedStatus.HasValue);
            }
        }
        public async void CrdCommandExecuteWithAuthCreateNewObjects()
        {
            CombinedDockerConfig config = new CombinedDockerConfig("image", new Docker.Models.CreateContainerParameters(), Option.None <AuthConfig>());
            IModule m1  = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            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)));
            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, modules, Runtime, configProvider.Object);
                await cmd.ExecuteAsync(token);

                Assert.True(getSecretCalled, nameof(getSecretCalled));
                Assert.True(postSecretCalled, nameof(postSecretCalled));
                Assert.True(getCrdCalled, nameof(getCrdCalled));
                Assert.True(postCrdCalled, nameof(postCrdCalled));
            }
        }
        public async void CrdCommandExecuteWithAuthReplaceObjects()
        {
            CombinedDockerConfig config = new CombinedDockerConfig("image", new Docker.Models.CreateContainerParameters(), Option.None <AuthConfig>());
            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 m1         = new DockerModule("module1", "v1", ModuleStatus.Running, Core.RestartPolicy.Always, Config1, ImagePullPolicy.OnCreate, DefaultConfigurationInfo, EnvVars);
            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)));
            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(), token);
                    }
                    else if (pathStr.Contains($"namespaces/{Namespace}/{Constants.EdgeDeployment.Plural}/{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/{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, modules, Runtime, configProvider.Object);
                await cmd.ExecuteAsync(token);

                Assert.True(getSecretCalled, nameof(getSecretCalled));
                Assert.True(putSecretCalled, nameof(putSecretCalled));
                Assert.True(getCrdCalled, nameof(getCrdCalled));
                Assert.True(putCrdCalled, nameof(putCrdCalled));
            }
        }
        public async void 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"))));
            }
        }