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""}]}}}", Option.None <NotaryContentTrust>()); var module = new DockerModule(Name, "1.0", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.OnUnhealthy, config, ImagePullPolicy.OnCreate, Constants.DefaultPriority, 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", string.Empty)), 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 async Task AgentStartsUpModules(TestConfig testConfig) { // 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 logFactoryMock = new Mock <ILoggerFactory>(); var logMock = new Mock <ILogger <LoggingCommandFactory> >(); logFactoryMock.Setup(l => l.CreateLogger(It.IsAny <string>())) .Returns(logMock.Object); var commandFactory = new LoggingCommandFactory(dockerCommandFactory, logFactoryMock.Object); 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 EdgeHubLaunchWithBadLogOptions() { const string Image = "hello-world:latest"; const string Name = Constants.EdgeHubModuleName; 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 dockerLoggingOptions = new Dictionary <string, string> { { "max-size", "1m" }, { "max-file", "1" } }; var loggingConfig = new DockerLoggingConfig("json-file", dockerLoggingOptions); var config = new DockerConfig(Image, @"{""Env"": [""k1=v1"", ""k2=v2""]}", Option.None <NotaryContentTrust>()); var configurationInfo = new ConfigurationInfo("43"); var module = new EdgeHubDockerModule("docker", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, config, ImagePullPolicy.OnCreate, Constants.DefaultPriority, configurationInfo, EnvVars); IConfigurationRoot configRoot = new ConfigurationBuilder().AddInMemoryCollection( new Dictionary <string, string> { { "EdgeHubConnectionString", fakeConnectionString } }).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", "Not a valid JSON")), 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, true); // 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 "/" // labels - edgeHub doesn's have a version Assert.Equal("missing", container.Config.Labels.GetOrElse(Constants.Labels.Version, "missing")); Assert.Equal("43", container.Config.Labels.GetOrElse(Constants.Labels.ConfigurationId, "missing")); // port bindings - check that we added default bindings for hub Assert.True(container.HostConfig.PortBindings.ContainsKey("8883/tcp")); Assert.True(container.HostConfig.PortBindings["8883/tcp"].Count == 1); Assert.Equal("8883", container.HostConfig.PortBindings["8883/tcp"].First().HostPort); Assert.True(container.HostConfig.PortBindings.ContainsKey("443/tcp")); Assert.True(container.HostConfig.PortBindings["443/tcp"].Count == 1); Assert.Equal("443", container.HostConfig.PortBindings["443/tcp"][0].HostPort); // 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(fakeConnectionString, envMap[Constants.IotHubConnectionStringKey]); } } finally { await DockerHelper.Client.CleanupContainerAsync(Name, Image); } }
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""}]}}}", Option.None <NotaryContentTrust>()); var module = new DockerModule(Name, "1.0", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.OnUnhealthy, config, ImagePullPolicy.OnCreate, Constants.DefaultPriority, 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); // While we only set one log config for max-size, there may be other log-configs in docker's daemon.json that the container will inherit. // So there can be more than one. 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 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, Constants.DefaultStartupOrder, 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, null)); 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 async Task EdgeHubLaunch() { const string Image = "hello-world:latest"; const string Name = Constants.EdgeHubModuleName; string sharedAccessKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("test")); string fakeConnectionString = $"Hostname=fakeiothub;Deviceid=test;SharedAccessKey={sharedAccessKey}"; try { using (var cts = new CancellationTokenSource(this.defaultTimeout)) { var mountMap = new Dictionary <string, string>() { { Constants.EdgeModuleCaCertificateFileKey, "/module.ca.cert" }, { Constants.EdgeModuleHubServerCaChainCertificateFileKey, "/module.ca.chain.cert" }, { Constants.EdgeModuleHubServerCertificateFileKey, "/module.server.cert" } }; 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" } }; var loggingConfig = new DockerLoggingConfig("json-file", dockerLoggingOptions); // Logging options will be derived from module options. var config = new DockerConfig(Image, @"{""Env"": [""k1=v1"", ""k2=v2""], ""HostConfig"": {""LogConfig"": {""Type"":""none""}, ""PortBindings"": {""8080/tcp"": [{""HostPort"": ""80""}],""443/tcp"": [{""HostPort"": ""11443""}]}}}"); var configurationInfo = new ConfigurationInfo(); var module = new EdgeHubDockerModule("docker", ModuleStatus.Running, global::Microsoft.Azure.Devices.Edge.Agent.Core.RestartPolicy.Always, config, configurationInfo, EnvVars); IConfigurationRoot configRoot = new ConfigurationBuilder().AddInMemoryCollection( new Dictionary <string, string> { { "EdgeHubConnectionString", fakeConnectionString }, { Constants.NetworkIdKey, "testnetwork" }, { Constants.EdgeDeviceHostNameKey, "testdevice" }, { Constants.EdgeModuleHubServerCaChainCertificateFileKey, mountMap[Constants.EdgeModuleHubServerCaChainCertificateFileKey] }, { Constants.EdgeModuleHubServerCertificateFileKey, mountMap[Constants.EdgeModuleHubServerCertificateFileKey] }, }).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", string.Empty)), 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, true); // 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 "/" // edgeHub doesn't have a version Assert.Equal("missing", container.Config.Labels.GetOrElse(Constants.Labels.Version, "missing")); // port bindings - added default bindings for edgeHub Assert.True(container.HostConfig.PortBindings.ContainsKey("8080/tcp")); Assert.True(container.HostConfig.PortBindings.ContainsKey("8883/tcp")); Assert.Equal("8883", container.HostConfig.PortBindings["8883/tcp"].First().HostPort); Assert.True(container.HostConfig.PortBindings.ContainsKey("443/tcp")); Assert.True(container.HostConfig.PortBindings["443/tcp"].Count == 2); Assert.Equal("11443", container.HostConfig.PortBindings["443/tcp"][0].HostPort); Assert.Equal("443", container.HostConfig.PortBindings["443/tcp"][1].HostPort); // logging Assert.Equal("none", container.HostConfig.LogConfig.Type); Assert.True(container.HostConfig.LogConfig.Config.Count == 0); // network settings Assert.Equal("testdevice", container.NetworkSettings.Networks.GetOrElse("testnetwork", new EndpointSettings()).Aliases.FirstOrDefault()); Assert.Equal("testdevice", container.NetworkSettings.Networks.GetOrElse("testnetwork", new EndpointSettings()).Aliases.FirstOrDefault()); // environment variables IDictionary <string, string> envMap = container.Config.Env.ToDictionary('='); Assert.Equal("v1", envMap["k1"]); Assert.Equal("v2", envMap["k2"]); Assert.Equal(fakeConnectionString, envMap[Constants.IotHubConnectionStringKey]); // certificates env variables Assert.Equal("/module.ca.chain.cert", envMap[Constants.EdgeModuleHubServerCaChainCertificateFileKey]); Assert.Equal("/module.server.cert", envMap[Constants.EdgeModuleHubServerCertificateFileKey]); } } finally { await DockerHelper.Client.CleanupContainerAsync(Name, Image); } }
public static EdgeAgentDockerRuntimeModule CreateEdgeAgentDockerRuntimeModule(DockerConfig config) => new EdgeAgentDockerRuntimeModule( config, ModuleStatus.Running, 0, "desc", DateTime.Now, DateTime.Now, ImagePullPolicy.Never, new ConfigurationInfo(), new Dictionary <string, EnvVal> { ["hello"] = new EnvVal("world") }, "version2");