public async Task DockerFileChangeContextTest() { using var projectDirectory = CopyTestProjectDirectory("dockerfile"); File.Move(Path.Combine(projectDirectory.DirectoryPath, "backend", "Dockerfile"), Path.Combine(projectDirectory.DirectoryPath, "Dockerfile")); var content = @" name: frontend-backend services: - name: backend dockerFile: Dockerfile dockerFileContext: backend/ bindings: - containerPort: 80 protocol: http - name: backend2 dockerFile: ./Dockerfile dockerFileContext: ./backend bindings: - containerPort: 80 protocol: http - name: frontend project: frontend/frontend.csproj"; var projectFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"); await File.WriteAllTextAsync(projectFile, content); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, new FileInfo(projectFile)); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication(application, new HostOptions(), async (app, uri) => { var frontendUri = await GetServiceUrl(client, uri, "frontend"); var backendUri = await GetServiceUrl(client, uri, "backend"); var backend2Uri = await GetServiceUrl(client, uri, "backend2"); var backendResponse = await client.GetAsync(backendUri); var backend2Response = await client.GetAsync(backend2Uri); var frontendResponse = await client.GetAsync(frontendUri); Assert.True(backendResponse.IsSuccessStatusCode); Assert.True(backend2Response.IsSuccessStatusCode); Assert.True(frontendResponse.IsSuccessStatusCode); }); }
public async Task DockerNamedVolumeTest() { using var projectDirectory = CopyTestProjectDirectory("volume-test"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); // Add a volume var project = ((ProjectServiceBuilder)application.Services[0]); // Remove the existing volume so we can generate a random one for this test to avoid conflicts var volumeName = "tye_docker_volumes_test" + Guid.NewGuid().ToString().Substring(0, 10); project.Volumes.Clear(); project.Volumes.Add(new VolumeBuilder(source: null, name: volumeName, "/data")); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); var args = new[] { "--docker" }; await RunHostingApplication(application, args, async (app, serviceApi) => { var serviceUri = await GetServiceUrl(client, serviceApi, "volume-test"); Assert.NotNull(serviceUri); var response = await client.GetAsync(serviceUri); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); await client.PostAsync(serviceUri, new StringContent("Things saved to the volume!")); Assert.Equal("Things saved to the volume!", await client.GetStringAsync(serviceUri)); }); await RunHostingApplication(application, args, async (app, serviceApi) => { var serviceUri = await GetServiceUrl(client, serviceApi, "volume-test"); Assert.NotNull(serviceUri); // The volume has data persisted Assert.Equal("Things saved to the volume!", await client.GetStringAsync(serviceUri)); }); // Delete the volume await ProcessUtil.RunAsync("docker", $"volume rm {volumeName}"); }
public async Task WrongProjectPathProducesCorrectErrorMessage() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory("frontend-backend"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye-wrong-projectpath.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var exception = await Assert.ThrowsAsync <CommandException>(async() => await ApplicationFactory.CreateAsync(outputContext, projectFile)); var wrongProjectPath = Path.Combine(projectDirectory.DirectoryPath, "backend1"); Assert.Equal($"Failed to locate directory: '{wrongProjectPath}'.", exception.Message); }
public async Task FrontendBackendWatchRunTest() { using var projectDirectory = CopyTestProjectDirectory("frontend-backend"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication(application, new HostOptions() { Watch = true }, async (app, uri) => { // make sure both are running var frontendUri = await GetServiceUrl(client, uri, "frontend"); var backendUri = await GetServiceUrl(client, uri, "backend"); var backendResponse = await client.GetAsync(backendUri); var frontendResponse = await client.GetAsync(frontendUri); Assert.True(backendResponse.IsSuccessStatusCode); Assert.True(frontendResponse.IsSuccessStatusCode); var startupPath = Path.Combine(projectDirectory.DirectoryPath, "frontend", "Startup.cs"); File.AppendAllText(startupPath, "\n"); const int retries = 10; for (var i = 0; i < retries; i++) { var logs = await client.GetStringAsync(new Uri(uri, $"/api/v1/logs/frontend")); // "Application Started" should be logged twice due to the file change if (logs.IndexOf("Application started") != logs.LastIndexOf("Application started")) { return; } await Task.Delay(5000); } throw new Exception("Failed to relaunch project with dotnet watch"); }); }
public async Task MultiProjectPurgeTest() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "multi-project")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var tyeDir = new DirectoryInfo(Path.Combine(tempDirectory.DirectoryPath, ".tye")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = _sink, }; try { await TestHelpers.StartHostAndWaitForReplicasToStart(host); try { var pids = GetAllAppPids(host.Application); var containers = GetAllContainerIds(host.Application); Assert.True(Directory.Exists(tyeDir.FullName)); Assert.Subset(new HashSet <int>(GetAllPids()), new HashSet <int>(pids)); Assert.Subset(new HashSet <string>(await DockerAssert.GetRunningContainersIdsAsync(_output)), new HashSet <string>(containers)); await TestHelpers.PurgeHostAndWaitForGivenReplicasToStop(host, GetAllReplicasNames(host.Application)); var runningPids = new HashSet <int>(GetAllPids()); Assert.True(pids.All(pid => !runningPids.Contains(pid))); var runningContainers = new HashSet <string>(await DockerAssert.GetRunningContainersIdsAsync(_output)); Assert.True(containers.All(c => !runningContainers.Contains(c))); } finally { await host.StopAsync(); } } finally { host.Dispose(); Assert.False(Directory.Exists(tyeDir.FullName)); } }
public async Task DoubleNestingWorks() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory(Path.Combine("multirepo")); var content = @" name: VotingSample services: - name: vote include: vote/tye.yaml - name: results include: results/tye.yaml"; var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"); await File.WriteAllTextAsync(yamlFile, content); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var projectFile = new FileInfo(yamlFile); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); Assert.Equal(5, application.Services.Count); var vote = application.Services.Single(s => s.Name == "vote"); Assert.Equal(2, vote.Dependencies.Count); Assert.Contains("worker", vote.Dependencies); Assert.Contains("redis", vote.Dependencies); var results = application.Services.Single(s => s.Name == "results"); Assert.Single(results.Dependencies); Assert.Contains("worker", results.Dependencies); var worker = application.Services.Single(s => s.Name == "worker"); Assert.Equal(2, worker.Dependencies.Count); Assert.Contains("redis", worker.Dependencies); Assert.Contains("postgres", worker.Dependencies); var redis = application.Services.Single(s => s.Name == "redis"); Assert.Equal(3, redis.Dependencies.Count); Assert.Contains("postgres", redis.Dependencies); Assert.Contains("worker", redis.Dependencies); Assert.Contains("vote", redis.Dependencies); var postgres = application.Services.Single(s => s.Name == "postgres"); Assert.Equal(2, postgres.Dependencies.Count); Assert.Contains("worker", postgres.Dependencies); Assert.Contains("redis", postgres.Dependencies); }
public async Task FrontendDockerBackendProject() { using var projectDirectory = CopyTestProjectDirectory("frontend-backend"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); // Transform the backend into a docker image for testing var project = (ProjectServiceBuilder)application.Services.First(s => s.Name == "frontend"); application.Services.Remove(project); var outputFileName = project.AssemblyName + ".dll"; var container = new ContainerServiceBuilder(project.Name, $"mcr.microsoft.com/dotnet/core/aspnet:{project.TargetFrameworkVersion}") { IsAspNet = true }; container.Dependencies.UnionWith(project.Dependencies); container.Volumes.Add(new VolumeBuilder(project.PublishDir, name: null, target: "/app")); container.Args = $"dotnet /app/{outputFileName} {project.Args}"; // We're not setting up the dev cert here container.Bindings.AddRange(project.Bindings.Where(b => b.Protocol != "https")); await ProcessUtil.RunAsync("dotnet", $"publish \"{project.ProjectFile.FullName}\" /nologo", outputDataReceived : _sink.WriteLine, errorDataReceived : _sink.WriteLine); application.Services.Add(container); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication(application, new HostOptions(), async (app, uri) => { var frontendUri = await GetServiceUrl(client, uri, "frontend"); var backendUri = await GetServiceUrl(client, uri, "backend"); var backendResponse = await client.GetAsync(backendUri); var frontendResponse = await client.GetAsync(frontendUri); Assert.True(backendResponse.IsSuccessStatusCode); Assert.True(frontendResponse.IsSuccessStatusCode); }); }
public async Task MultipleProjectGenerateTest() { await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); await DockerAssert.DeleteDockerImagesAsync(output, "test/worker"); var projectName = "multi-project"; var environment = "production"; var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", projectName)); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); // Need to add docker registry for generate application.Registry = new ContainerRegistry("test"); try { await GenerateHost.ExecuteGenerateAsync(outputContext, application, environment, interactive : false); // name of application is the folder var content = File.ReadAllText(Path.Combine(tempDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); var expectedContent = File.ReadAllText($"testassets/generate/{projectName}.yaml"); Assert.Equal(expectedContent.NormalizeNewLines(), content.NormalizeNewLines()); await DockerAssert.AssertImageExistsAsync(output, "test/backend"); await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); await DockerAssert.AssertImageExistsAsync(output, "test/worker"); } finally { await DockerAssert.AssertImageExistsAsync(output, "test/backend"); await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); await DockerAssert.AssertImageExistsAsync(output, "test/worker"); } }
public async Task ServiceWithoutLivenessShouldDefaultToHealthyTests() { using var projectDirectory = CopyTestProjectDirectory("health-checks"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye-readiness.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); await using var host = new TyeHost(application.ToHostingApplication(), new HostOptions()) { Sink = _sink, }; await StartHostAndWaitForReplicasToStart(host, new[] { "health-readiness" }, ReplicaState.Healthy); }
public async Task MultipleProjectGenerateTest() { await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); await DockerAssert.DeleteDockerImagesAsync(output, "test/worker"); var projectName = "multi-project"; var environment = "production"; using var projectDirectory = CopyTestProjectDirectory(projectName); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); // Need to add docker registry for generate application.Registry = new ContainerRegistry("test"); try { await GenerateHost.ExecuteGenerateAsync(outputContext, application, environment, interactive : false); // name of application is the folder var content = await File.ReadAllTextAsync(Path.Combine(projectDirectory.DirectoryPath, $"{projectName}-generate-{environment}.yaml")); var expectedContent = await File.ReadAllTextAsync($"testassets/generate/{projectName}.yaml"); YamlAssert.Equals(expectedContent, content, output); await DockerAssert.AssertImageExistsAsync(output, "test/backend"); await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); await DockerAssert.AssertImageExistsAsync(output, "test/worker"); } finally { await DockerAssert.AssertImageExistsAsync(output, "test/backend"); await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); await DockerAssert.AssertImageExistsAsync(output, "test/worker"); } }
public async Task Generate_Ingress() { var applicationName = "apps-with-ingress"; var environment = "production"; await DockerAssert.DeleteDockerImagesAsync(output, "appa"); await DockerAssert.DeleteDockerImagesAsync(output, "appa"); using var projectDirectory = TestHelpers.CopyTestProjectDirectory(applicationName); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); try { await GenerateHost.ExecuteGenerateAsync(outputContext, application, environment, interactive : false); // name of application is the folder var content = await File.ReadAllTextAsync(Path.Combine(projectDirectory.DirectoryPath, $"{applicationName}-generate-{environment}.yaml")); if (await KubectlDetector.GetKubernetesServerVersion(outputContext) >= new Version(1, 19)) { var expectedContent = await File.ReadAllTextAsync($"testassets/generate/{applicationName}.1.19.yaml"); YamlAssert.Equals(expectedContent, content, output); } else { var expectedContent = await File.ReadAllTextAsync($"testassets/generate/{applicationName}.1.18.yaml"); YamlAssert.Equals(expectedContent, content, output); } await DockerAssert.AssertImageExistsAsync(output, "appa"); await DockerAssert.AssertImageExistsAsync(output, "appb"); } finally { await DockerAssert.DeleteDockerImagesAsync(output, "appa"); await DockerAssert.DeleteDockerImagesAsync(output, "appb"); } }
public async Task IngressRunTest() { using var projectDirectory = CopyTestProjectDirectory("apps-with-ingress"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication(application, Array.Empty <string>(), async (app, uri) => { var ingressUri = await GetServiceUrl(client, uri, "ingress"); var appAUri = await GetServiceUrl(client, uri, "appA"); var appBUri = await GetServiceUrl(client, uri, "appB"); var appAResponse = await client.GetAsync(appAUri); var appBResponse = await client.GetAsync(appBUri); Assert.True(appAResponse.IsSuccessStatusCode); Assert.True(appBResponse.IsSuccessStatusCode); var responseA = await client.GetAsync(ingressUri + "/A"); var responseB = await client.GetAsync(ingressUri + "/B"); Assert.StartsWith("Hello from Application A", await responseA.Content.ReadAsStringAsync()); Assert.StartsWith("Hello from Application B", await responseB.Content.ReadAsStringAsync()); var requestA = new HttpRequestMessage(HttpMethod.Get, ingressUri); requestA.Headers.Host = "a.example.com"; var requestB = new HttpRequestMessage(HttpMethod.Get, ingressUri); requestB.Headers.Host = "b.example.com"; responseA = await client.SendAsync(requestA); responseB = await client.SendAsync(requestB); Assert.StartsWith("Hello from Application A", await responseA.Content.ReadAsStringAsync()); Assert.StartsWith("Hello from Application B", await responseB.Content.ReadAsStringAsync()); }); }
public async Task TargetFrameworkFromCliArgsDoesNotOverrideSingleTFM() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory(Path.Combine("single-project")); var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var projectFile = new FileInfo(yamlFile); var applicationBuilder = await ApplicationFactory.CreateAsync(outputContext, projectFile, "net5.0"); Assert.Single(applicationBuilder.Services); var service = applicationBuilder.Services.Single(s => s.Name == "test-project"); var containsTargetFramework = ((DotnetProjectServiceBuilder)service).BuildProperties.TryGetValue("TargetFramework", out var targetFramework); Assert.False(containsTargetFramework); }
public async Task DockerNetworkAssignmentForNonExistingNetworkTest() { using var projectDirectory = CopyTestProjectDirectory("frontend-backend"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var dockerNetwork = "tye_docker_network_test" + Guid.NewGuid().ToString().Substring(0, 10); application.Network = dockerNetwork; var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication( application, new HostOptions() { Docker = true, }, async (app, uri) => { // Make sure we're running containers Assert.True(app.Services.All(s => s.Value.Description.RunInfo is DockerRunInfo)); foreach (var serviceBuilder in application.Services) { var serviceUri = await GetServiceUrl(client, uri, serviceBuilder.Name); var serviceResponse = await client.GetAsync(serviceUri); Assert.True(serviceResponse.IsSuccessStatusCode); var serviceResult = await client.GetStringAsync($"{uri}api/v1/services/{serviceBuilder.Name}"); var service = JsonSerializer.Deserialize <V1Service>(serviceResult, _options); Assert.NotNull(service); Assert.NotEqual(dockerNetwork, service.Replicas.FirstOrDefault().Value.DockerNetwork); } }); }
public async Task NullDebugTargetsDoesNotThrow() { using var projectDirectory = CopyTestProjectDirectory(Path.Combine("single-project", "test-project")); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "test-project.csproj")); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); await using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = _sink, }; await host.StartAsync(); }
public async Task TyeBuild_SinglePhase_ExistingDockerfileWithBuildArgsMultiArgs() { var projectName = "single-phase-dockerfile-multi-args"; var environment = "production"; var imageName = "test/web"; await DockerAssert.DeleteDockerImagesAsync(output, imageName); using var projectDirectory = CopyTestProjectDirectory(projectName); Assert.True(File.Exists(Path.Combine(projectDirectory.DirectoryPath, "Dockerfile")), "Dockerfile should exist."); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); application.Registry = new ContainerRegistry("test"); try { await BuildHost.ExecuteBuildAsync(outputContext, application, environment, interactive : false); Assert.Single(application.Services.Single().Outputs.OfType <DockerImageOutput>()); var builder = (DockerFileServiceBuilder)application.Services.First(); var valuePair = builder.BuildArgs.ElementAt(0); Assert.Equal(3, builder.BuildArgs.Count); Assert.Equal("pat", valuePair.Key); Assert.Equal("thisisapat", valuePair.Value); valuePair = builder.BuildArgs.ElementAt(1); Assert.Equal("number_of_replicas", valuePair.Key); Assert.Equal("2", valuePair.Value); valuePair = builder.BuildArgs.ElementAt(2); Assert.Equal("number_of_shards", valuePair.Key); Assert.Equal("5", valuePair.Value); await DockerAssert.AssertImageExistsAsync(output, imageName); } finally { await DockerAssert.DeleteDockerImagesAsync(output, imageName); } }
public async Task CyclesStopImmediately() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory(Path.Combine("single-project")); var content = @" name: single-project services: - name: test-project include: tye.yaml"; var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"); await File.WriteAllTextAsync(yamlFile, content); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, new FileInfo(yamlFile)); Assert.Empty(application.Services); }
public async Task TargetFrameworkFromCliArgsOverwriteYaml() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory(Path.Combine("multi-targetframeworks")); var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye-with-netcoreapp21.yaml"); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var projectFile = new FileInfo(yamlFile); var applicationBuilder = await ApplicationFactory.CreateAsync(outputContext, projectFile, "netcoreapp3.1"); Assert.Single(applicationBuilder.Services); var service = applicationBuilder.Services.Single(s => s.Name == "multi-targetframeworks"); var containsTargetFramework = ((DotnetProjectServiceBuilder)service).BuildProperties.TryGetValue("TargetFramework", out var targetFramework); Assert.True(containsTargetFramework); Assert.Equal("netcoreapp3.1", targetFramework); }
public async Task ThrowIfNoSpecificTargetFramework() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory(Path.Combine("multi-targetframeworks")); var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye-no-buildproperties.yaml"); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var projectFile = new FileInfo(yamlFile); var applicationBuilder = await ApplicationFactory.CreateAsync(outputContext, projectFile); Assert.Single(applicationBuilder.Services); var service = applicationBuilder.Services.Single(s => s.Name == "multi-targetframeworks"); var containsTargetFramework = ((DotnetProjectServiceBuilder)service).BuildProperties.TryGetValue("TargetFramework", out var targetFramework); Assert.False(containsTargetFramework); Assert.Throws <InvalidOperationException>(() => applicationBuilder.ToHostingApplication()); }
public async Task MultipleProjectBuildTest() { await DockerAssert.DeleteDockerImagesAsync(output, "test/backend"); await DockerAssert.DeleteDockerImagesAsync(output, "test/frontend"); await DockerAssert.DeleteDockerImagesAsync(output, "test/worker"); var projectName = "multi-project"; var environment = "production"; var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", projectName)); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); application.Registry = new ContainerRegistry("test"); try { await BuildHost.ExecuteBuildAsync(outputContext, application, environment, interactive : false); await DockerAssert.AssertImageExistsAsync(output, "test/backend"); await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); await DockerAssert.AssertImageExistsAsync(output, "test/worker"); } finally { await DockerAssert.AssertImageExistsAsync(output, "test/backend"); await DockerAssert.AssertImageExistsAsync(output, "test/frontend"); await DockerAssert.AssertImageExistsAsync(output, "test/worker"); } }
public async Task FrontendBackendRunTestWithDocker() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "frontend-backend")); using var tempDirectory = TempDirectory.Create(preferUserDirectoryOnMacOS: true); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), new[] { "--docker" }) { Sink = sink, }; await host.StartAsync(); try { // Make sure we're runningn containers Assert.True(host.Application.Services.All(s => s.Value.Description.RunInfo is DockerRunInfo)); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); var dashboardUri = new Uri(host.DashboardWebApplication !.Addresses.First()); await CheckServiceIsUp(host.Application, client, "backend", dashboardUri, timeout : TimeSpan.FromSeconds(60)); await CheckServiceIsUp(host.Application, client, "frontend", dashboardUri, timeout : TimeSpan.FromSeconds(60)); } finally { await host.StopAsync(); } }
public async Task FrontendBackendRunTestWithDockerAndBuildConfigurationAsProperty(string buildConfiguration) { using var projectDirectory = CopyTestProjectDirectory("frontend-backend"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, $"tye-{buildConfiguration.ToLower()}-configuration.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication(application, new HostOptions() { Docker = true, }, async (app, uri) => { // Make sure we're running containers Assert.True(app.Services.All(s => s.Value.Description.RunInfo is DockerRunInfo)); var frontendUri = await GetServiceUrl(client, uri, "frontend"); var backendUri = await GetServiceUrl(client, uri, "backend"); var backendResponse = await client.GetAsync(backendUri); var frontendResponse = await client.GetAsync(frontendUri); Assert.True(backendResponse.IsSuccessStatusCode); Assert.True(frontendResponse.IsSuccessStatusCode); Assert.True(app.Services.All(s => s.Value.Description.RunInfo != null && ((DockerRunInfo)s.Value.Description.RunInfo).VolumeMappings.Count > 0)); var outputFileInfos = app.Services.Select(s => new FileInfo((s.Value?.Description?.RunInfo as DockerRunInfo)?.VolumeMappings[0].Source ?? throw new InvalidOperationException())).ToList(); Assert.True(outputFileInfos.All(f => f.Directory?.Parent?.Parent?.Name == buildConfiguration)); }); }
public async Task DockerHostVolumeTest() { using var projectDirectory = CopyTestProjectDirectory("volume-test"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); // Add a volume var project = (ProjectServiceBuilder)application.Services[0]; using var tempDir = TempDirectory.Create(preferUserDirectoryOnMacOS: true); project.Volumes.Clear(); project.Volumes.Add(new VolumeBuilder(source: tempDir.DirectoryPath, name: null, target: "/data")); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; await File.WriteAllTextAsync(Path.Combine(tempDir.DirectoryPath, "file.txt"), "This content came from the host"); var client = new HttpClient(new RetryHandler(handler)); var options = new HostOptions() { Docker = true, }; await RunHostingApplication(application, options, async (app, serviceApi) => { var serviceUri = await GetServiceUrl(client, serviceApi, "volume-test"); Assert.NotNull(serviceUri); // The volume has data the host mapped data Assert.Equal("This content came from the host", await client.GetStringAsync(serviceUri)); }); }
public async Task EnvironmentVariablesOnlySetForDirectDependencies() { using var projectDirectory = TestHelpers.CopyTestProjectDirectory(Path.Combine("multirepo")); var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "results", "tye.yaml"); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, new FileInfo(yamlFile)); var app = application.ToHostingApplication(); var dictionary = new Dictionary <string, string>(); app.PopulateEnvironment(app.Services["results"], (s1, s2) => dictionary[s1] = s2); // Just the WORKER is defined. Assert.Equal(8, dictionary.Count); Assert.Equal("http", dictionary["SERVICE__WORKER__PROTOCOL"]); // No POSTGRES or REDIS Assert.False(dictionary.ContainsKey("SERVICE__POSTGRES__PROTOCOL")); Assert.False(dictionary.ContainsKey("SERVICE__REDIS__PROTOCOL")); }
public async Task ServicWithoutLivenessShouldBecomeReadyWhenReadyTests() { using var projectDirectory = CopyTestProjectDirectory("health-checks"); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye-readiness.yaml")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); await using var host = new TyeHost(application.ToHostingApplication(), new HostOptions()) { Sink = _sink, }; await StartHostAndWaitForReplicasToStart(host, new[] { "health-readiness" }, ReplicaState.Healthy); var replicas = host.Application.Services["health-readiness"].Replicas.Select(r => r.Value).ToList(); Assert.True(await DoOperationAndWaitForReplicasToChangeState(host, ReplicaState.Ready, replicas.Count, replicas.Select(r => r.Name).ToHashSet(), null, TimeSpan.Zero, async _ => { await Task.WhenAll(replicas.Select(r => SetHealthyReadyInReplica(r, ready: true))); })); }
public async Task DockerBaseImageAndTagTest() { using var projectDirectory = CopyTestProjectDirectory(Path.Combine("frontend-backend", "backend")); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "backend-baseimage.csproj")); var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); // Transform the backend into a docker image for testing var project = (ProjectServiceBuilder)application.Services.First(s => s.Name == "backend-baseimage"); // check ContainerInfo values Assert.True(string.Equals(project.ContainerInfo !.BaseImageName, "mcr.microsoft.com/dotnet/core/sdk")); Assert.True(string.Equals(project.ContainerInfo !.BaseImageTag, "3.1-buster")); // check projectInfo values var projectRunInfo = new ProjectRunInfo(project); Assert.True(string.Equals(projectRunInfo !.ContainerBaseImage, project.ContainerInfo.BaseImageName)); Assert.True(string.Equals(projectRunInfo !.ContainerBaseTag, project.ContainerInfo.BaseImageTag)); }
public async Task NullDebugTargetsDoesNotThrow() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; await host.StartAsync(); await host.StopAsync(); }
public async Task Generate_DirectDependencyForEnvVars() { var applicationName = "multirepo"; var projectName = "results"; var otherProject = "worker"; var environment = "production"; await DockerAssert.DeleteDockerImagesAsync(output, projectName); await DockerAssert.DeleteDockerImagesAsync(output, otherProject); using var projectDirectory = TestHelpers.CopyTestProjectDirectory(applicationName); var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "results", "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); try { await GenerateHost.ExecuteGenerateAsync(outputContext, application, environment, interactive : false); // name of application is the folder var content = await File.ReadAllTextAsync(Path.Combine(projectDirectory.DirectoryPath, "results", $"VotingSample-generate-{environment}.yaml")); var expectedContent = await File.ReadAllTextAsync($"testassets/generate/{applicationName}.yaml"); YamlAssert.Equals(expectedContent, content, output); await DockerAssert.AssertImageExistsAsync(output, projectName); await DockerAssert.AssertImageExistsAsync(output, otherProject); } finally { await DockerAssert.DeleteDockerImagesAsync(output, projectName); await DockerAssert.DeleteDockerImagesAsync(output, otherProject); } }
public async Task MultiRepo_WorksWithCloning() { using var projectDirectory = TempDirectory.Create(preferUserDirectoryOnMacOS: true); var content = @" name: VotingSample services: - name: vote repository: https://github.com/jkotalik/TyeMultiRepoVoting - name: results repository: https://github.com/jkotalik/TyeMultiRepoResults"; var yamlFile = Path.Combine(projectDirectory.DirectoryPath, "tye.yaml"); await File.WriteAllTextAsync(yamlFile, content); // Debug targets can be null if not specified, so make sure calling host.Start does not throw. var outputContext = new OutputContext(_sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, new FileInfo(yamlFile)); var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); await RunHostingApplication(application, new HostOptions(), async (app, uri) => { var votingUri = await GetServiceUrl(client, uri, "vote"); var workerUri = await GetServiceUrl(client, uri, "worker"); var votingResponse = await client.GetAsync(votingUri); var workerResponse = await client.GetAsync(workerUri); Assert.True(votingResponse.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.NotFound, workerResponse.StatusCode); }); }
public async Task FrontendBackendRunTest() { var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "frontend-backend")); using var tempDirectory = TempDirectory.Create(); DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); var outputContext = new OutputContext(sink, Verbosity.Debug); var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); using var host = new TyeHost(application.ToHostingApplication(), Array.Empty <string>()) { Sink = sink, }; await host.StartAsync(); try { var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (a, b, c, d) => true, AllowAutoRedirect = false }; var client = new HttpClient(new RetryHandler(handler)); var dashboardUri = new Uri(host.DashboardWebApplication !.Addresses.First()); await CheckServiceIsUp(host.Application, client, "backend", dashboardUri); await CheckServiceIsUp(host.Application, client, "frontend", dashboardUri); } finally { await host.StopAsync(); } }