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); } }
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); }
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); } }
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)); } }
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); } }
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); }