示例#1
0
        public async Task UpstreamProtocolTest()
        {
            const string Image                = "hello-world:latest";
            const string Name                 = "test-helloworld";
            string       sharedAccessKey      = Convert.ToBase64String(Encoding.UTF8.GetBytes("test"));
            string       fakeConnectionString = $"Hostname=fakeiothub;Deviceid=test;SharedAccessKey={sharedAccessKey}";

            try
            {
                using (var cts = new CancellationTokenSource(this.defaultTimeout))
                {
                    // Arrange
                    await DockerHelper.Client.CleanupContainerAsync(Name, Image);

                    // ensure image has been pulled
                    await DockerHelper.Client.PullImageAsync(Image, cts.Token);

                    var dockerLoggingOptions = new Dictionary <string, string>
                    {
                        { "max-size", "1m" },
                        { "max-file", "1" }
                    };
                    // Logging options will be derived from these default logging options
                    var loggingConfig = new DockerLoggingConfig("json-file", dockerLoggingOptions);
                    var config        = new DockerConfig(Image, @"{""Env"": [""k1=v1"", ""k2=v2""], ""HostConfig"": {""PortBindings"": {""8080/tcp"": [{""HostPort"": ""80""}]}}}");
                    var module        = new DockerModule(Name, "1.0", ModuleStatus.Running, Core.RestartPolicy.OnUnhealthy, config, null, EnvVars);

                    IConfigurationRoot configRoot = new ConfigurationBuilder().AddInMemoryCollection(
                        new Dictionary <string, string>
                    {
                        { "EdgeHubConnectionString", fakeConnectionString },
                        { Constants.EdgeModuleCaCertificateFileKey, "/module.ca.cert" },
                        { "UpstreamProtocol", "AmqpWs" }
                    }).Build();

                    var modules = new Dictionary <string, IModule> {
                        [Name] = module
                    };
                    var systemModules        = new SystemModules(null, null);
                    var deploymentConfigInfo = new DeploymentConfigInfo(1, new DeploymentConfig("1.0", new DockerRuntimeInfo("docker", new DockerRuntimeConfig("1.25", "")), systemModules, modules));
                    var configSource         = new Mock <IConfigSource>();
                    configSource.Setup(cs => cs.Configuration).Returns(configRoot);
                    configSource.Setup(cs => cs.GetDeploymentConfigInfoAsync()).ReturnsAsync(deploymentConfigInfo);

                    var identity = new Mock <IModuleIdentity>();
                    identity.Setup(id => id.Credentials).Returns(new ConnectionStringCredentials(fakeConnectionString));

                    ICommand command = await CreateCommand.BuildAsync(DockerHelper.Client, module, identity.Object, loggingConfig, configSource.Object, false);

                    // Act
                    // run the command
                    await command.ExecuteAsync(cts.Token);

                    // Assert
                    // verify container is created and has correct settings
                    ContainerInspectResponse container = await DockerHelper.Client.Containers.InspectContainerAsync(Name);

                    Assert.Equal(Name, container.Name.Substring(1)); // for whatever reason the container name is returned with a starting "/"
                    Assert.Equal("1.0", container.Config.Labels.GetOrElse(Constants.Labels.Version, "missing"));
                    // port mapping
                    Assert.Equal("8080/tcp", container.HostConfig.PortBindings.First().Key);
                    // logging
                    Assert.Equal("json-file", container.HostConfig.LogConfig.Type);
                    Assert.True(container.HostConfig.LogConfig.Config.Count == 2);
                    Assert.Equal("1m", container.HostConfig.LogConfig.Config.GetOrElse("max-size", "missing"));
                    Assert.Equal("1", container.HostConfig.LogConfig.Config.GetOrElse("max-file", "missing"));
                    // environment variables
                    IDictionary <string, string> envMap = container.Config.Env.ToDictionary('=');
                    Assert.Equal("v1", envMap["k1"]);
                    Assert.Equal("v2", envMap["k2"]);
                    Assert.Equal("AmqpWs", envMap["UpstreamProtocol"]);
                    Assert.Equal(fakeConnectionString, envMap["EdgeHubConnectionString"]);
                    // certificates env variables
                    Assert.Equal("/module.ca.cert", envMap[Constants.EdgeModuleCaCertificateFileKey]);
                }
            }
            finally
            {
                await DockerHelper.Client.CleanupContainerAsync(Name, Image);
            }
        }
示例#2
0
        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, Constants.DefaultStartupOrder, 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);
        }
示例#3
0
        public async Task AgentStartsUpModules(TestConfig testConfig)
        {
            ILoggerFactory loggerFactory = Mock.Of <ILoggerFactory>();

            // Build the docker host URL.
            string       dockerHostUrl = ConfigHelper.TestConfig["dockerHostUrl"];
            DockerClient client        = new DockerClientConfiguration(new Uri(dockerHostUrl)).CreateClient();

            try
            {
                // Remove any running containers with the same name that may be a left-over
                // from previous test runs.
                await RemoveContainer(client, testConfig);

                // Initialize docker configuration for this module.
                DockerConfig dockerConfig = testConfig.ImageCreateOptions != null
                    ? new DockerConfig(testConfig.Image, testConfig.ImageCreateOptions)
                    : new DockerConfig(testConfig.Image);

                // Initialize an Edge Agent module object.
                var dockerModule = new DockerModule(
                    testConfig.Name,
                    testConfig.Version,
                    ModuleStatus.Running,
                    global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.OnUnhealthy,
                    dockerConfig,
                    null,
                    null);
                var modules = new Dictionary <string, IModule> {
                    [testConfig.Name] = dockerModule
                };
                var systemModules = new SystemModules(null, null);

                // Start up the agent and run a "reconcile".
                var dockerLoggingOptions = new Dictionary <string, string>
                {
                    { "max-size", "1m" },
                    { "max-file", "1" }
                };
                var loggingConfig = new DockerLoggingConfig("json-file", dockerLoggingOptions);

                string             sharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("test"));
                IConfigurationRoot configRoot      = new ConfigurationBuilder().AddInMemoryCollection(
                    new Dictionary <string, string>
                {
                    { "DeviceConnectionString", $"Hostname=fakeiothub;Deviceid=test;SharedAccessKey={sharedAccessKey}" }
                }).Build();

                var runtimeConfig        = new DockerRuntimeConfig("1.24.0", "{}");
                var runtimeInfo          = new DockerRuntimeInfo("docker", runtimeConfig);
                var deploymentConfigInfo = new DeploymentConfigInfo(1, new DeploymentConfig("1.0", runtimeInfo, systemModules, modules));

                var configSource = new Mock <IConfigSource>();
                configSource.Setup(cs => cs.Configuration).Returns(configRoot);
                configSource.Setup(cs => cs.GetDeploymentConfigInfoAsync()).ReturnsAsync(deploymentConfigInfo);

                // TODO: Fix this up with a real reporter. But before we can do that we need to use
                // the real configuration source that talks to IoT Hub above.
                NullReporter reporter = NullReporter.Instance;

                var restartStateStore                = Mock.Of <IEntityStore <string, ModuleState> >();
                var configStore                      = Mock.Of <IEntityStore <string, string> >();
                var deploymentConfigInfoSerde        = Mock.Of <ISerde <DeploymentConfigInfo> >();
                IRestartPolicyManager restartManager = new Mock <IRestartPolicyManager>().Object;

                var dockerCommandFactory = new DockerCommandFactory(client, loggingConfig, configSource.Object, new CombinedDockerConfigProvider(Enumerable.Empty <AuthConfig>()));
                IRuntimeInfoProvider runtimeInfoProvider = await RuntimeInfoProvider.CreateAsync(client);

                IEnvironmentProvider environmentProvider = await DockerEnvironmentProvider.CreateAsync(runtimeInfoProvider, restartStateStore, restartManager);

                var commandFactory = new LoggingCommandFactory(dockerCommandFactory, loggerFactory);

                var credential = new ConnectionStringCredentials("fake");
                var identity   = new Mock <IModuleIdentity>();
                identity.Setup(id => id.Credentials).Returns(credential);
                identity.Setup(id => id.ModuleId).Returns(testConfig.Name);
                IImmutableDictionary <string, IModuleIdentity> identities = new Dictionary <string, IModuleIdentity>()
                {
                    [testConfig.Name] = identity.Object
                }.ToImmutableDictionary();
                var moduleIdentityLifecycleManager = new Mock <IModuleIdentityLifecycleManager>();
                moduleIdentityLifecycleManager.Setup(m => m.GetModuleIdentitiesAsync(It.IsAny <ModuleSet>(), It.IsAny <ModuleSet>())).Returns(Task.FromResult(identities));

                Agent agent = await Agent.Create(
                    configSource.Object,
                    new RestartPlanner(commandFactory),
                    new OrderedPlanRunner(),
                    reporter,
                    moduleIdentityLifecycleManager.Object,
                    environmentProvider,
                    configStore,
                    deploymentConfigInfoSerde,
                    NullEncryptionProvider.Instance);

                await agent.ReconcileAsync(CancellationToken.None);

                // Sometimes the container is still not ready by the time we run the validator.
                // So we attempt validation multiple times and bail only if all of them fail.
                bool      validated   = false;
                int       attempts    = 0;
                const int MaxAttempts = 5;
                while (!validated && attempts < MaxAttempts)
                {
                    validated = testConfig.Validator.Validate();
                    if (!validated)
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(5));
                    }

                    ++attempts;
                }

                Assert.Equal(true, validated);
            }
            finally
            {
                await RemoveContainer(client, testConfig);
            }
        }
示例#4
0
        public async Task TestUdpModuleConfig()
        {
            const string Image                = "hello-world:latest";
            const string Name                 = "test-helloworld";
            string       sharedAccessKey      = Convert.ToBase64String(Encoding.UTF8.GetBytes("test"));
            string       fakeConnectionString = $"Hostname=fakeiothub;Deviceid=test;SharedAccessKey={sharedAccessKey}";

            try
            {
                using (var cts = new CancellationTokenSource(this.defaultTimeout))
                {
                    await DockerHelper.Client.CleanupContainerAsync(Name, Image);

                    // ensure image has been pulled
                    await DockerHelper.Client.PullImageAsync(Image, cts.Token);

                    var loggingConfig = new DockerLoggingConfig("json-file");
                    var config        = new DockerConfig(Image, @"{""HostConfig"": {""PortBindings"": {""42/udp"": [{""HostPort"": ""42""}]}}}");
                    var module        = new DockerModule(Name, "1.0", ModuleStatus.Running, Core.RestartPolicy.OnUnhealthy, config, null, EnvVars);

                    IConfigurationRoot configRoot = new ConfigurationBuilder().AddInMemoryCollection(
                        new Dictionary <string, string>
                    {
                        { "EdgeHubConnectionString", fakeConnectionString }
                    }).Build();

                    // Logging options will be derived from application level configuration
                    var modules = new Dictionary <string, IModule> {
                        [Name] = module
                    };
                    var systemModules        = new SystemModules(null, null);
                    var deploymentConfigInfo = new DeploymentConfigInfo(1, new DeploymentConfig("1.0", new DockerRuntimeInfo("docker", new DockerRuntimeConfig("1.25", @"{""Type"":""json-file"",""Config"":{""max-size"":""100M""}}")), systemModules, modules));
                    var configSource         = new Mock <IConfigSource>();
                    configSource.Setup(cs => cs.Configuration).Returns(configRoot);
                    configSource.Setup(cs => cs.GetDeploymentConfigInfoAsync()).ReturnsAsync(deploymentConfigInfo);

                    string credential = "fake";
                    var    identity   = new Mock <IModuleIdentity>();
                    identity.Setup(id => id.Credentials).Returns(new ConnectionStringCredentials(credential));

                    ICommand command = await CreateCommand.BuildAsync(DockerHelper.Client, module, identity.Object, loggingConfig, configSource.Object, false);

                    // run the command
                    await command.ExecuteAsync(cts.Token);

                    // verify container is created with correct settings.
                    ContainerInspectResponse container = await DockerHelper.Client.Containers.InspectContainerAsync(Name);

                    Assert.Equal(Name, container.Name.Substring(1)); // for whatever reason the container name is returned with a starting "/"
                    Assert.Equal("1.0", container.Config.Labels.GetOrElse(Constants.Labels.Version, "missing"));
                    // port bindings
                    Assert.Equal(1, container.HostConfig.PortBindings.Count);
                    Assert.False(container.HostConfig.PortBindings.ContainsKey("8883/tcp"));
                    Assert.False(container.HostConfig.PortBindings.ContainsKey("443/tcp"));
                    // logging
                    Assert.Equal("json-file", container.HostConfig.LogConfig.Type);
                    Assert.True(container.HostConfig.LogConfig.Config.Count == 1);
                    Assert.Equal("100M", container.HostConfig.LogConfig.Config["max-size"]);
                }
            }
            finally
            {
                await DockerHelper.Client.CleanupContainerAsync(Name, Image);
            }
        }
        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));
            }
        }
示例#6
0
        public async Task <ModuleSet> GetModulesAsync(CancellationToken token)
        {
            IEnumerable <ModuleRuntimeInfo> moduleStatuses = await this.moduleStatusProvider.GetModules(token);

            var       modules   = new List <IModule>();
            ModuleSet moduleSet = this.deploymentConfig.GetModuleSet();

            foreach (ModuleRuntimeInfo moduleRuntimeInfo in moduleStatuses)
            {
                if (moduleRuntimeInfo.Type != "docker" || !(moduleRuntimeInfo is ModuleRuntimeInfo <DockerReportedConfig> dockerRuntimeInfo))
                {
                    Events.InvalidModuleType(moduleRuntimeInfo);
                    continue;
                }

                if (!moduleSet.Modules.TryGetValue(dockerRuntimeInfo.Name, out IModule configModule) || !(configModule is DockerModule dockerModule))
                {
                    dockerModule = new DockerModule(dockerRuntimeInfo.Name, string.Empty, ModuleStatus.Unknown, Core.RestartPolicy.Unknown, new DockerConfig(Constants.UnknownImage, new CreateContainerParameters(), Option.None <string>()), ImagePullPolicy.OnCreate, Core.Constants.HighestPriority, new ConfigurationInfo(), null);
                }

                Option <ModuleState> moduleStateOption = await this.moduleStateStore.Get(moduleRuntimeInfo.Name);

                ModuleState moduleState = moduleStateOption.GetOrElse(new ModuleState(0, moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue)));

                string  image = !string.IsNullOrWhiteSpace(dockerRuntimeInfo.Config.Image) ? dockerRuntimeInfo.Config.Image : dockerModule.Config.Image;
                var     dockerReportedConfig = new DockerReportedConfig(image, dockerModule.Config.CreateOptions, dockerRuntimeInfo.Config.ImageHash, dockerModule.Config.Digest);
                IModule module;
                switch (moduleRuntimeInfo.Name)
                {
                case Core.Constants.EdgeHubModuleName:
                    module = new EdgeHubDockerRuntimeModule(
                        dockerModule.DesiredStatus,
                        dockerModule.RestartPolicy,
                        dockerReportedConfig,
                        (int)dockerRuntimeInfo.ExitCode,
                        moduleRuntimeInfo.Description,
                        moduleRuntimeInfo.StartTime.GetOrElse(DateTime.MinValue),
                        moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue),
                        moduleState.RestartCount,
                        moduleState.LastRestartTimeUtc,
                        moduleRuntimeInfo.ModuleStatus,
                        dockerModule.ImagePullPolicy,
                        dockerModule.StartupOrder,
                        dockerModule.ConfigurationInfo,
                        dockerModule.Env);
                    break;

                case Core.Constants.EdgeAgentModuleName:
                    module = new EdgeAgentDockerRuntimeModule(
                        dockerReportedConfig,
                        moduleRuntimeInfo.ModuleStatus,
                        (int)dockerRuntimeInfo.ExitCode,
                        moduleRuntimeInfo.Description,
                        moduleRuntimeInfo.StartTime.GetOrElse(DateTime.MinValue),
                        moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue),
                        dockerModule.ImagePullPolicy,
                        dockerModule.ConfigurationInfo,
                        dockerModule.Env);
                    break;

                default:
                    module = new DockerRuntimeModule(
                        moduleRuntimeInfo.Name,
                        dockerModule.Version,
                        dockerModule.DesiredStatus,
                        dockerModule.RestartPolicy,
                        dockerReportedConfig,
                        (int)dockerRuntimeInfo.ExitCode,
                        moduleRuntimeInfo.Description,
                        moduleRuntimeInfo.StartTime.GetOrElse(DateTime.MinValue),
                        moduleRuntimeInfo.ExitTime.GetOrElse(DateTime.MinValue),
                        moduleState.RestartCount,
                        moduleState.LastRestartTimeUtc,
                        moduleRuntimeInfo.ModuleStatus,
                        dockerModule.ImagePullPolicy,
                        dockerModule.StartupOrder,
                        dockerModule.ConfigurationInfo,
                        dockerModule.Env);
                    break;
                }

                modules.Add(module);
            }

            return(ModuleSet.Create(modules.ToArray()));
        }
        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"))));
            }
        }
        public async Task TestFilters()
        {
            const string Image                = "hello-world:latest";
            const string Name                 = "test-filters";
            string       sharedAccessKey      = Convert.ToBase64String(Encoding.UTF8.GetBytes("test"));
            string       fakeConnectionString = $"Hostname=fakeiothub;Deviceid=test;SharedAccessKey={sharedAccessKey}";

            try
            {
                using (var cts = new CancellationTokenSource(Timeout))
                {
                    await Client.CleanupContainerAsync(Name, Image);

                    await Client.CleanupContainerAsync("test-filters-external", Image);

                    var loggingConfig = new DockerLoggingConfig("json-file");
                    var config        = new DockerConfig(Image);
                    var module        = new DockerModule(Name, "1.0", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.OnUnhealthy, config, ImagePullPolicy.OnCreate, null, null);

                    IConfigurationRoot configRoot = new ConfigurationBuilder().AddInMemoryCollection(
                        new Dictionary <string, string>
                    {
                        { "EdgeDeviceConnectionString", fakeConnectionString }
                    }).Build();

                    var deploymentConfigModules = new Dictionary <string, IModule> {
                        [Name] = module
                    };
                    var systemModules        = new SystemModules(null, null);
                    var deploymentConfigInfo = new DeploymentConfigInfo(1, new DeploymentConfig("1.0", new DockerRuntimeInfo("docker", new DockerRuntimeConfig("1.25", string.Empty)), systemModules, deploymentConfigModules));
                    var configSource         = new Mock <IConfigSource>();
                    configSource.Setup(cs => cs.Configuration).Returns(configRoot);
                    configSource.Setup(cs => cs.GetDeploymentConfigInfoAsync()).ReturnsAsync(deploymentConfigInfo);

                    var credential = new ConnectionStringCredentials("fake");
                    var identity   = new Mock <IModuleIdentity>();
                    identity.Setup(id => id.Credentials).Returns(credential);

                    ICommand create = await CreateCommand.BuildAsync(Client, module, identity.Object, loggingConfig, configSource.Object, false);

                    // pull the image for both containers
                    await Client.PullImageAsync(Image, cts.Token);

                    // pull and create module using commands
                    await create.ExecuteAsync(cts.Token);

                    var createParams = new CreateContainerParameters
                    {
                        Name  = "test-filters-external",
                        Image = Image,
                    };
                    await Client.Containers.CreateContainerAsync(createParams);

                    // Check that only containers created via command are listed in the environment
                    RuntimeInfoProvider runtimeInfoProvider = await RuntimeInfoProvider.CreateAsync(Client);

                    IEnumerable <ModuleRuntimeInfo> modules = await runtimeInfoProvider.GetModules(cts.Token);

                    Assert.Single(modules);
                    Assert.Equal(module.Name, modules.First().Name);
                }
            }
            finally
            {
                await Client.CleanupContainerAsync(Name, Image);

                await Client.CleanupContainerAsync("test-filters-external", Image);
            }
        }
示例#10
0
 public RemoveCommand(IDockerClient client, DockerModule module)
 {
     this.client = Preconditions.CheckNotNull(client, nameof(client));
     this.module = Preconditions.CheckNotNull(module, nameof(module));
 }
        public async Task GetModulesTest()
        {
            // Arrange
            var restartPolicyManager = new Mock <IRestartPolicyManager>();

            restartPolicyManager.Setup(
                r => r.ComputeModuleStatusFromRestartPolicy(
                    It.IsAny <ModuleStatus>(),
                    It.IsAny <RestartPolicy>(),
                    It.IsAny <int>(),
                    It.IsAny <DateTime>()))
            .Returns <ModuleStatus, RestartPolicy, int, DateTime>((m, r, c, d) => m);

            string module1Hash           = Guid.NewGuid().ToString();
            string module2Hash           = Guid.NewGuid().ToString();
            string edgeHubHash           = Guid.NewGuid().ToString();
            string edgeAgentHash         = Guid.NewGuid().ToString();
            var    moduleRuntimeInfoList = new List <ModuleRuntimeInfo>();

            moduleRuntimeInfoList.Add(
                new ModuleRuntimeInfo <DockerReportedConfig>(
                    "module1",
                    "docker",
                    ModuleStatus.Stopped,
                    "dummy1",
                    0,
                    Option.Some(new DateTime(2017, 10, 10)),
                    Option.None <DateTime>(),
                    new DockerReportedConfig("mod1:v1", string.Empty, module1Hash, Option.None <string>())));
            moduleRuntimeInfoList.Add(
                new ModuleRuntimeInfo <DockerReportedConfig>(
                    "module2",
                    "docker",
                    ModuleStatus.Failed,
                    "dummy2",
                    5,
                    Option.Some(new DateTime(2017, 10, 12)),
                    Option.Some(new DateTime(2017, 10, 14)),
                    new DockerReportedConfig("mod2:v2", string.Empty, module2Hash, Option.None <string>())));
            moduleRuntimeInfoList.Add(
                new ModuleRuntimeInfo <DockerReportedConfig>(
                    "edgeHub",
                    "docker",
                    ModuleStatus.Running,
                    string.Empty,
                    0,
                    Option.Some(new DateTime(2017, 10, 10)),
                    Option.None <DateTime>(),
                    new DockerReportedConfig("edgehub:v1", string.Empty, edgeHubHash, Option.None <string>())));
            moduleRuntimeInfoList.Add(
                new ModuleRuntimeInfo <DockerReportedConfig>(
                    "edgeAgent",
                    "docker",
                    ModuleStatus.Running,
                    string.Empty,
                    0,
                    Option.Some(new DateTime(2017, 10, 10)),
                    Option.None <DateTime>(),
                    new DockerReportedConfig("edgeAgent:v1", "{\"Env\":[\"foo4=bar4\"]}", edgeAgentHash, Option.None <string>())));

            var runtimeInfoProvider = Mock.Of <IRuntimeInfoProvider>(r => r.GetModules(CancellationToken.None) == Task.FromResult(moduleRuntimeInfoList.AsEnumerable()));
            var moduleStateStore    = new Mock <IEntityStore <string, ModuleState> >();

            moduleStateStore.Setup(m => m.Get("module1")).ReturnsAsync(Option.Some(new ModuleState(1, new DateTime(2017, 10, 13))));
            moduleStateStore.Setup(m => m.Get("module2")).ReturnsAsync(Option.Some(new ModuleState(2, new DateTime(2017, 10, 13))));
            moduleStateStore.Setup(m => m.Get("edgeHub")).ReturnsAsync(Option.Some(new ModuleState(3, new DateTime(2017, 10, 13))));
            moduleStateStore.Setup(m => m.Get("edgeAgent")).ReturnsAsync(Option.Some(new ModuleState(4, new DateTime(2017, 10, 13))));

            string minDockerVersion     = "20";
            string dockerLoggingOptions = "dummy logging options";

            var module1          = new DockerModule("module1", "v1", ModuleStatus.Stopped, RestartPolicy.Always, new DockerConfig("mod1:v1", "{\"Env\":[\"foo=bar\"]}", Option.None <string>()), ImagePullPolicy.OnCreate, Constants.DefaultPriority, new ConfigurationInfo(), null);
            var module2          = new DockerModule("module2", "v2", ModuleStatus.Running, RestartPolicy.OnUnhealthy, new DockerConfig("mod2:v2", "{\"Env\":[\"foo2=bar2\"]}", Option.None <string>()), ImagePullPolicy.Never, Constants.DefaultPriority, new ConfigurationInfo(), null);
            var edgeHubModule    = new EdgeHubDockerModule("docker", ModuleStatus.Running, RestartPolicy.Always, new DockerConfig("edgehub:v1", "{\"Env\":[\"foo3=bar3\"]}", Option.None <string>()), ImagePullPolicy.OnCreate, Constants.HighestPriority, new ConfigurationInfo(), null);
            var edgeAgentModule  = new EdgeAgentDockerModule("docker", new DockerConfig("edgeAgent:v1", string.Empty, Option.None <string>()), ImagePullPolicy.OnCreate, new ConfigurationInfo(), null);
            var deploymentConfig = new DeploymentConfig(
                "1.0",
                new DockerRuntimeInfo("docker", new DockerRuntimeConfig(minDockerVersion, dockerLoggingOptions)),
                new SystemModules(edgeAgentModule, edgeHubModule),
                new Dictionary <string, IModule> {
                [module1.Name] = module1, [module2.Name] = module2
            });

            var environment = new DockerEnvironment(runtimeInfoProvider, deploymentConfig, moduleStateStore.Object, restartPolicyManager.Object, OperatingSystemType, Architecture, Version);

            // Act
            ModuleSet moduleSet = await environment.GetModulesAsync(CancellationToken.None);

            // Assert
            Assert.NotNull(moduleSet);
            Assert.True(moduleSet.Modules.TryGetValue("module1", out IModule receivedModule1));
            Assert.True(moduleSet.Modules.TryGetValue("module2", out IModule receivedModule2));
            Assert.True(moduleSet.Modules.TryGetValue("edgeHub", out IModule receivedEdgeHub));
            Assert.True(moduleSet.Modules.TryGetValue("edgeAgent", out IModule receivedEdgeAgent));

            var receivedDockerModule1 = receivedModule1 as DockerRuntimeModule;

            Assert.NotNull(receivedDockerModule1);
            Assert.Equal("module1", receivedDockerModule1.Name);
            Assert.Equal("v1", receivedDockerModule1.Version);
            Assert.Equal(ModuleStatus.Stopped, receivedDockerModule1.DesiredStatus);
            Assert.Equal(RestartPolicy.Always, receivedDockerModule1.RestartPolicy);
            Assert.Equal(ImagePullPolicy.OnCreate, receivedDockerModule1.ImagePullPolicy);
            Assert.Equal(Constants.DefaultPriority, receivedDockerModule1.Priority);
            Assert.Equal("mod1:v1", receivedDockerModule1.Config.Image);
            Assert.Equal("{\"Env\":[\"foo=bar\"]}", JsonConvert.SerializeObject(receivedDockerModule1.Config.CreateOptions));
            Assert.Equal(ModuleStatus.Stopped, receivedDockerModule1.RuntimeStatus);
            Assert.Equal("dummy1", receivedDockerModule1.StatusDescription);
            Assert.Equal(0, receivedDockerModule1.ExitCode);
            Assert.Equal(new DateTime(2017, 10, 10), receivedDockerModule1.LastStartTimeUtc);
            Assert.Equal(DateTime.MinValue, receivedDockerModule1.LastExitTimeUtc);
            Assert.Equal(new DateTime(2017, 10, 13), receivedDockerModule1.LastRestartTimeUtc);
            Assert.Equal(module1Hash, (receivedDockerModule1.Config as DockerReportedConfig)?.ImageHash);
            Assert.Equal(1, receivedDockerModule1.RestartCount);

            var receivedDockerModule2 = receivedModule2 as DockerRuntimeModule;

            Assert.NotNull(receivedDockerModule2);
            Assert.Equal("module2", receivedDockerModule2.Name);
            Assert.Equal("v2", receivedDockerModule2.Version);
            Assert.Equal(ModuleStatus.Running, receivedDockerModule2.DesiredStatus);
            Assert.Equal(RestartPolicy.OnUnhealthy, receivedDockerModule2.RestartPolicy);
            Assert.Equal(ImagePullPolicy.Never, receivedDockerModule2.ImagePullPolicy);
            Assert.Equal(Constants.DefaultPriority, receivedDockerModule2.Priority);
            Assert.Equal("mod2:v2", receivedDockerModule2.Config.Image);
            Assert.Equal("{\"Env\":[\"foo2=bar2\"]}", JsonConvert.SerializeObject(receivedDockerModule2.Config.CreateOptions));
            Assert.Equal(ModuleStatus.Failed, receivedDockerModule2.RuntimeStatus);
            Assert.Equal("dummy2", receivedDockerModule2.StatusDescription);
            Assert.Equal(5, receivedDockerModule2.ExitCode);
            Assert.Equal(new DateTime(2017, 10, 12), receivedDockerModule2.LastStartTimeUtc);
            Assert.Equal(new DateTime(2017, 10, 14), receivedDockerModule2.LastExitTimeUtc);
            Assert.Equal(new DateTime(2017, 10, 13), receivedDockerModule2.LastRestartTimeUtc);
            Assert.Equal(module2Hash, (receivedDockerModule2.Config as DockerReportedConfig)?.ImageHash);
            Assert.Equal(2, receivedDockerModule2.RestartCount);

            var receivedDockerEdgeHub = receivedEdgeHub as EdgeHubDockerRuntimeModule;

            Assert.NotNull(receivedDockerEdgeHub);
            Assert.Equal("edgeHub", receivedDockerEdgeHub.Name);
            Assert.Equal(string.Empty, receivedDockerEdgeHub.Version);
            Assert.Equal(ModuleStatus.Running, receivedDockerEdgeHub.DesiredStatus);
            Assert.Equal(RestartPolicy.Always, receivedDockerEdgeHub.RestartPolicy);
            Assert.Equal(ImagePullPolicy.OnCreate, receivedDockerEdgeHub.ImagePullPolicy);
            Assert.Equal(Constants.HighestPriority, receivedDockerEdgeHub.Priority);
            Assert.Equal("edgehub:v1", receivedDockerEdgeHub.Config.Image);
            Assert.Equal("{\"Env\":[\"foo3=bar3\"]}", JsonConvert.SerializeObject(receivedDockerEdgeHub.Config.CreateOptions));
            Assert.Equal(ModuleStatus.Running, receivedDockerEdgeHub.RuntimeStatus);
            Assert.Equal(string.Empty, receivedDockerEdgeHub.StatusDescription);
            Assert.Equal(0, receivedDockerEdgeHub.ExitCode);
            Assert.Equal(new DateTime(2017, 10, 10), receivedDockerEdgeHub.LastStartTimeUtc);
            Assert.Equal(DateTime.MinValue, receivedDockerEdgeHub.LastExitTimeUtc);
            Assert.Equal(new DateTime(2017, 10, 13), receivedDockerEdgeHub.LastRestartTimeUtc);
            Assert.Equal(edgeHubHash, (receivedDockerEdgeHub.Config as DockerReportedConfig)?.ImageHash);
            Assert.Equal(3, receivedDockerEdgeHub.RestartCount);

            var receivedDockerEdgeAgent = receivedEdgeAgent as EdgeAgentDockerRuntimeModule;

            Assert.NotNull(receivedDockerEdgeAgent);
            Assert.Equal("edgeAgent", receivedDockerEdgeAgent.Name);
            Assert.Equal(string.Empty, receivedDockerEdgeAgent.Version);
            Assert.Equal(ModuleStatus.Running, receivedDockerEdgeAgent.RuntimeStatus);
            Assert.Equal(ImagePullPolicy.OnCreate, receivedDockerEdgeAgent.ImagePullPolicy);
            Assert.Equal(Constants.HighestPriority, receivedDockerEdgeAgent.Priority);
            Assert.Equal("edgeAgent:v1", receivedDockerEdgeAgent.Config.Image);
            Assert.Equal("{\"Env\":[\"foo4=bar4\"]}", JsonConvert.SerializeObject(receivedDockerEdgeAgent.Config.CreateOptions));
            Assert.Equal(new DateTime(2017, 10, 10), receivedDockerEdgeAgent.LastStartTimeUtc);
            Assert.Equal(edgeAgentHash, (receivedDockerEdgeAgent.Config as DockerReportedConfig)?.ImageHash);
        }