Browse Source

Add docker file support for deploy (#489)

jkotalik/porter
Justin Kotalik 6 years ago
committed by GitHub
parent
commit
e368d04e91
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 46
      src/Microsoft.Tye.Core/ApplicationExecutor.cs
  2. 32
      src/Microsoft.Tye.Core/ApplicationFactory.cs
  3. 9
      src/Microsoft.Tye.Core/ApplyContainerDefaultsStep.cs
  4. 10
      src/Microsoft.Tye.Core/BuildDockerImageStep.cs
  5. 2
      src/Microsoft.Tye.Core/ConfigModel/ConfigApplication.cs
  6. 4
      src/Microsoft.Tye.Core/ContainerServiceBuilder.cs
  7. 50
      src/Microsoft.Tye.Core/DockerContainerBuilder.cs
  8. 20
      src/Microsoft.Tye.Core/DockerFileServiceBuilder.cs
  9. 18
      src/Microsoft.Tye.Core/DockerfileGenerator.cs
  10. 35
      src/Microsoft.Tye.Core/DotnetProjectServiceBuilder.cs
  11. 2
      src/Microsoft.Tye.Core/GenerateDockerfileStep.cs
  12. 2
      src/Microsoft.Tye.Core/GenerateHelmChartStep.cs
  13. 2
      src/Microsoft.Tye.Core/GenerateOamComponentStep.cs
  14. 2
      src/Microsoft.Tye.Core/HelmChartGenerator.cs
  15. 4
      src/Microsoft.Tye.Core/ProjectReader.cs
  16. 30
      src/Microsoft.Tye.Core/ProjectServiceBuilder.cs
  17. 2
      src/Microsoft.Tye.Core/PublishProjectStep.cs
  18. 2
      src/Microsoft.Tye.Extensions/DiagnosticAgent.cs
  19. 2
      src/Microsoft.Tye.Extensions/Elastic/ElasticStackExtension.cs
  20. 2
      src/Microsoft.Tye.Extensions/Zipkin/ZipkinExtension.cs
  21. 2
      src/Microsoft.Tye.Hosting/Model/ProjectRunInfo.cs
  22. 39
      src/tye/ApplicationBuilderExtensions.cs
  23. 4
      test/E2ETest/Microsoft.Tye.E2ETests.csproj
  24. 39
      test/E2ETest/TyeGenerateTests.cs
  25. 6
      test/E2ETest/TyeRunTests.cs
  26. 109
      test/E2ETest/testassets/generate/dockerfile.yaml

46
src/Microsoft.Tye.Core/ApplicationExecutor.cs

@ -81,6 +81,29 @@ namespace Microsoft.Tye
public abstract Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service);
protected bool SkipWithoutDotnetProject(OutputContext output, ServiceBuilder service, [MaybeNullWhen(returnValue: true)] out DotnetProjectServiceBuilder project)
{
if (output is null)
{
throw new ArgumentNullException(nameof(output));
}
if (service is null)
{
throw new ArgumentNullException(nameof(service));
}
if (service is DotnetProjectServiceBuilder p)
{
project = p;
return false;
}
output.WriteInfoLine($"Service '{service.Name}' does not have a project associated. Skipping.");
project = default!;
return true;
}
protected bool SkipWithoutProject(OutputContext output, ServiceBuilder service, [MaybeNullWhen(returnValue: true)] out ProjectServiceBuilder project)
{
if (output is null)
@ -104,6 +127,29 @@ namespace Microsoft.Tye
return true;
}
protected bool SkipWithoutDockerFile(OutputContext output, ServiceBuilder service, [MaybeNullWhen(returnValue: true)] out DockerFileServiceBuilder project)
{
if (output is null)
{
throw new ArgumentNullException(nameof(output));
}
if (service is null)
{
throw new ArgumentNullException(nameof(service));
}
if (service is DockerFileServiceBuilder p)
{
project = p;
return false;
}
output.WriteInfoLine($"Service '{service.Name}' does not have a project associated. Skipping.");
project = default!;
return true;
}
protected bool SkipWithoutContainerInfo(OutputContext output, ServiceBuilder service, [MaybeNullWhen(returnValue: true)] out ContainerInfo container)
{
if (output is null)

32
src/Microsoft.Tye.Core/ApplicationFactory.cs

@ -86,7 +86,7 @@ namespace Microsoft.Tye
{
var expandedProject = Environment.ExpandEnvironmentVariables(configService.Project);
var projectFile = new FileInfo(Path.Combine(config.Source.DirectoryName, expandedProject));
var project = new ProjectServiceBuilder(configService.Name!, projectFile);
var project = new DotnetProjectServiceBuilder(configService.Name!, projectFile);
service = project;
project.Build = configService.Build ?? true;
@ -109,21 +109,41 @@ namespace Microsoft.Tye
// Do k8s by default.
project.ManifestInfo = new KubernetesManifestInfo();
}
else if (!string.IsNullOrEmpty(configService.Image) || !string.IsNullOrEmpty(configService.DockerFile))
else if (!string.IsNullOrEmpty(configService.Image))
{
var container = new ContainerServiceBuilder(configService.Name!, configService.Image!)
{
Args = configService.Args,
Replicas = configService.Replicas ?? 1,
DockerFile = configService.DockerFile != null ? Path.Combine(source.DirectoryName, configService.DockerFile) : null,
// Supplying an absolute path with trailing slashes fails for DockerFileContext when calling docker build, so trim trailing slash.
DockerFileContext = GetDockerFileContext(source, configService)
Replicas = configService.Replicas ?? 1
};
service = container;
container.Liveness = configService.Liveness != null ? GetProbeBuilder(configService.Liveness) : null;
container.Readiness = configService.Readiness != null ? GetProbeBuilder(configService.Readiness) : null;
}
else if (!string.IsNullOrEmpty(configService.DockerFile))
{
var dockerFile = new DockerFileServiceBuilder(configService.Name!, configService.Image!)
{
Args = configService.Args,
Build = configService.Build ?? true,
Replicas = configService.Replicas ?? 1,
DockerFile = Path.Combine(source.DirectoryName, configService.DockerFile),
// Supplying an absolute path with trailing slashes fails for DockerFileContext when calling docker build, so trim trailing slash.
DockerFileContext = GetDockerFileContext(source, configService)
};
service = dockerFile;
dockerFile.Liveness = configService.Liveness != null ? GetProbeBuilder(configService.Liveness) : null;
dockerFile.Readiness = configService.Readiness != null ? GetProbeBuilder(configService.Readiness) : null;
// We don't apply more container defaults here because we might need
// to prompt for the registry name.
dockerFile.ContainerInfo = new ContainerInfo() { UseMultiphaseDockerfile = false, };
// Do k8s by default.
dockerFile.ManifestInfo = new KubernetesManifestInfo();
}
else if (!string.IsNullOrEmpty(configService.Executable))
{
var expandedExecutable = Environment.ExpandEnvironmentVariables(configService.Executable);

9
src/Microsoft.Tye.Core/ApplyContainerDefaultsStep.cs

@ -22,7 +22,14 @@ namespace Microsoft.Tye
return Task.CompletedTask;
}
DockerfileGenerator.ApplyContainerDefaults(application, project, container);
if (project is DotnetProjectServiceBuilder dotnetProject)
{
DockerfileGenerator.ApplyContainerDefaults(application, dotnetProject, container);
}
else if (project is DockerFileServiceBuilder dockerFile)
{
DockerfileGenerator.ApplyContainerDefaults(application, dockerFile, container);
}
return Task.CompletedTask;
}
}

10
src/Microsoft.Tye.Core/BuildDockerImageStep.cs

@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
namespace Microsoft.Tye
@ -34,7 +35,14 @@ namespace Microsoft.Tye
throw new CommandException($"Cannot generate a docker image for '{service.Name}' because docker is not running.");
}
await DockerContainerBuilder.BuildContainerImageAsync(output, application, project, container);
if (project is DotnetProjectServiceBuilder dotnetProject)
{
await DockerContainerBuilder.BuildContainerImageAsync(output, application, dotnetProject, container);
}
else if (project is DockerFileServiceBuilder dockerFile)
{
await DockerContainerBuilder.BuildContainerImageFromDockerFileAsync(output, application, dockerFile, container);
}
}
}
}

2
src/Microsoft.Tye.Core/ConfigModel/ConfigApplication.cs

@ -139,7 +139,7 @@ namespace Microsoft.Tye.ConfigModel
var probes = new[] { (Name: "liveness", Probe: service.Liveness), (Name: "readiness", Probe: service.Readiness) }.Where(p => p.Probe != null).ToArray();
foreach (var probe in probes)
{
if (probe.Name == "liveness" && probe.Probe.SuccessThreshold != 1)
if (probe.Name == "liveness" && probe.Probe!.SuccessThreshold != 1)
{
throw new TyeYamlException(CoreStrings.FormatSuccessThresholdMustBeOne(probe.Name));
}

4
src/Microsoft.Tye.Core/ContainerServiceBuilder.cs

@ -20,10 +20,6 @@ namespace Microsoft.Tye
public string? Args { get; set; }
public string? DockerFile { get; set; }
public string? DockerFileContext { get; set; }
public int Replicas { get; set; } = 1;
public List<EnvironmentVariableBuilder> EnvironmentVariables { get; } = new List<EnvironmentVariableBuilder>();

50
src/Microsoft.Tye.Core/DockerContainerBuilder.cs

@ -12,7 +12,55 @@ namespace Microsoft.Tye
{
internal static class DockerContainerBuilder
{
public static async Task BuildContainerImageAsync(OutputContext output, ApplicationBuilder application, ProjectServiceBuilder project, ContainerInfo container)
public static async Task BuildContainerImageFromDockerFileAsync(OutputContext output, ApplicationBuilder application, DockerFileServiceBuilder containerService, ContainerInfo container)
{
if (output is null)
{
throw new ArgumentNullException(nameof(output));
}
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
if (containerService is null)
{
throw new ArgumentNullException(nameof(containerService));
}
if (containerService.DockerFile is null)
{
throw new ArgumentNullException(nameof(containerService.DockerFile));
}
var dockerFileInfo = new FileInfo(containerService.DockerFile);
var contextDirectory = containerService.DockerFileContext ?? dockerFileInfo.DirectoryName;
var dockerFilePath = Path.Combine(dockerFileInfo.DirectoryName, "Dockerfile");
output.WriteDebugLine($"Using existing Dockerfile '{dockerFilePath}'.");
output.WriteDebugLine("Running 'docker build'.");
output.WriteCommandLine("docker", $"build \"{contextDirectory}\" -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"");
var capture = output.Capture();
var exitCode = await Process.ExecuteAsync(
$"docker",
$"build \"{contextDirectory}\" -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"",
new FileInfo(containerService.DockerFile).DirectoryName,
stdOut: capture.StdOut,
stdErr: capture.StdErr);
output.WriteDebugLine($"Done running 'docker build' exit code: {exitCode}");
if (exitCode != 0)
{
throw new CommandException("'docker build' failed.");
}
output.WriteInfoLine($"Created Docker Image: '{container.ImageName}:{container.ImageTag}'");
containerService.Outputs.Add(new DockerImageOutput(container.ImageName!, container.ImageTag!));
}
public static async Task BuildContainerImageAsync(OutputContext output, ApplicationBuilder application, DotnetProjectServiceBuilder project, ContainerInfo container)
{
if (output is null)
{

20
src/Microsoft.Tye.Core/DockerFileServiceBuilder.cs

@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.Tye
{
public class DockerFileServiceBuilder : ProjectServiceBuilder
{
public DockerFileServiceBuilder(string name, string image)
: base(name)
{
Image = image;
}
public string Image { get; set; }
public string? DockerFile { get; set; }
public string? DockerFileContext { get; set; }
}
}

18
src/Microsoft.Tye.Core/DockerfileGenerator.cs

@ -12,7 +12,7 @@ namespace Microsoft.Tye
{
public static class DockerfileGenerator
{
public static async Task WriteDockerfileAsync(OutputContext output, ApplicationBuilder application, ProjectServiceBuilder project, ContainerInfo container, string filePath)
public static async Task WriteDockerfileAsync(OutputContext output, ApplicationBuilder application, DotnetProjectServiceBuilder project, ContainerInfo container, string filePath)
{
if (output is null)
{
@ -75,7 +75,7 @@ namespace Microsoft.Tye
await writer.WriteLineAsync($"ENTRYPOINT [\"dotnet\", \"{applicationEntryPoint}.dll\"]");
}
public static void ApplyContainerDefaults(ApplicationBuilder application, ProjectServiceBuilder project, ContainerInfo container)
public static void ApplyContainerDefaults(ApplicationBuilder application, DotnetProjectServiceBuilder project, ContainerInfo container)
{
if (application is null)
{
@ -125,5 +125,19 @@ namespace Microsoft.Tye
container.ImageTag ??= project.Version?.Replace("+", "-") ?? "latest";
}
public static void ApplyContainerDefaults(ApplicationBuilder application, DockerFileServiceBuilder project, ContainerInfo container)
{
if (container.ImageName == null && application.Registry?.Hostname == null)
{
container.ImageName ??= project.Name.ToLowerInvariant();
}
else if (container.ImageName == null && application.Registry?.Hostname != null)
{
container.ImageName ??= $"{application.Registry?.Hostname}/{project.Name.ToLowerInvariant()}";
}
container.ImageTag ??= "latest";
}
}
}

35
src/Microsoft.Tye.Core/DotnetProjectServiceBuilder.cs

@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.IO;
namespace Microsoft.Tye
{
public class DotnetProjectServiceBuilder : ProjectServiceBuilder
{
public DotnetProjectServiceBuilder(string name, FileInfo projectFile)
: base(name)
{
ProjectFile = projectFile;
}
public FileInfo ProjectFile { get; }
public FrameworkCollection Frameworks { get; } = new FrameworkCollection();
// These is always set on the ApplicationFactory codepath.
public string TargetFrameworkName { get; set; } = default!;
public string TargetFrameworkVersion { get; set; } = default!;
public string TargetFramework { get; set; } = default!;
public string[] TargetFrameworks { get; set; } = default!;
public string Version { get; set; } = default!;
public string TargetPath { get; set; } = default!;
public string RunCommand { get; set; } = default!;
public string RunArguments { get; set; } = default!;
public string AssemblyName { get; set; } = default!;
public string PublishDir { get; set; } = default!;
public string IntermediateOutputPath { get; set; } = default!;
public Dictionary<string, string> BuildProperties { get; } = new Dictionary<string, string>();
}
}

2
src/Microsoft.Tye.Core/GenerateDockerfileStep.cs

@ -15,7 +15,7 @@ namespace Microsoft.Tye
public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
{
if (SkipWithoutProject(output, service, out var project))
if (SkipWithoutDotnetProject(output, service, out var project))
{
return;
}

2
src/Microsoft.Tye.Core/GenerateHelmChartStep.cs

@ -15,7 +15,7 @@ namespace Microsoft.Tye
public override Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
{
if (SkipWithoutProject(output, service, out var project))
if (SkipWithoutDotnetProject(output, service, out var project))
{
return Task.CompletedTask;
}

2
src/Microsoft.Tye.Core/GenerateOamComponentStep.cs

@ -20,7 +20,7 @@ namespace Microsoft.Tye
return Task.CompletedTask;
}
if (SkipWithoutProject(output, service, out var project))
if (SkipWithoutDotnetProject(output, service, out var project))
{
return Task.CompletedTask;
}

2
src/Microsoft.Tye.Core/HelmChartGenerator.cs

@ -10,7 +10,7 @@ namespace Microsoft.Tye
{
internal static class HelmChartGenerator
{
public static async Task GenerateAsync(OutputContext output, ApplicationBuilder application, ProjectServiceBuilder project, ContainerInfo container, HelmChartStep chart, DirectoryInfo outputDirectory)
public static async Task GenerateAsync(OutputContext output, ApplicationBuilder application, DotnetProjectServiceBuilder project, ContainerInfo container, HelmChartStep chart, DirectoryInfo outputDirectory)
{
if (output is null)
{

4
src/Microsoft.Tye.Core/ProjectReader.cs

@ -61,7 +61,7 @@ namespace Microsoft.Tye
}
}
public static Task ReadProjectDetailsAsync(OutputContext output, ProjectServiceBuilder project)
public static Task ReadProjectDetailsAsync(OutputContext output, DotnetProjectServiceBuilder project)
{
if (output is null)
{
@ -142,7 +142,7 @@ namespace Microsoft.Tye
// Do not load MSBuild types before using EnsureMSBuildRegistered.
[MethodImpl(MethodImplOptions.NoInlining)]
private static void EvaluateProject(OutputContext output, ProjectServiceBuilder project)
private static void EvaluateProject(OutputContext output, DotnetProjectServiceBuilder project)
{
var sw = Stopwatch.StartNew();

30
src/Microsoft.Tye.Core/ProjectServiceBuilder.cs

@ -3,19 +3,16 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.IO;
namespace Microsoft.Tye
{
public sealed class ProjectServiceBuilder : ServiceBuilder
public class ProjectServiceBuilder : ServiceBuilder
{
public ProjectServiceBuilder(string name, FileInfo projectFile)
public ProjectServiceBuilder(string name)
: base(name)
{
ProjectFile = projectFile;
}
public FileInfo ProjectFile { get; }
public bool IsAspNet { get; set; }
public int Replicas { get; set; } = 1;
@ -23,23 +20,6 @@ namespace Microsoft.Tye
public string? Args { get; set; }
public FrameworkCollection Frameworks { get; } = new FrameworkCollection();
// These is always set on the ApplicationFactory codepath.
public string TargetFrameworkName { get; set; } = default!;
public string TargetFrameworkVersion { get; set; } = default!;
public string TargetFramework { get; set; } = default!;
public string[] TargetFrameworks { get; set; } = default!;
public string Version { get; set; } = default!;
public string TargetPath { get; set; } = default!;
public string RunCommand { get; set; } = default!;
public string RunArguments { get; set; } = default!;
public string AssemblyName { get; set; } = default!;
public string PublishDir { get; set; } = default!;
public string IntermediateOutputPath { get; set; } = default!;
public bool IsAspNet { get; set; }
public bool RelocateDiagnosticsDomainSockets { get; set; }
// Data used for building containers
public ContainerInfo? ContainerInfo { get; set; }
@ -51,12 +31,12 @@ namespace Microsoft.Tye
// Used when running in a container locally.
public List<VolumeBuilder> Volumes { get; } = new List<VolumeBuilder>();
public Dictionary<string, string> BuildProperties { get; } = new Dictionary<string, string>();
public List<SidecarBuilder> Sidecars { get; } = new List<SidecarBuilder>();
public ProbeBuilder? Liveness { get; set; }
public ProbeBuilder? Readiness { get; set; }
public bool RelocateDiagnosticsDomainSockets { get; set; }
}
}

2
src/Microsoft.Tye.Core/PublishProjectStep.cs

@ -16,7 +16,7 @@ namespace Microsoft.Tye
public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application, ServiceBuilder service)
{
if (SkipWithoutProject(output, service, out var project))
if (SkipWithoutDotnetProject(output, service, out var project))
{
return;
}

2
src/Microsoft.Tye.Extensions/DiagnosticAgent.cs

@ -8,7 +8,7 @@ namespace Microsoft.Tye
{
public static class DiagnosticAgent
{
public static SidecarBuilder GetOrAddSidecar(ProjectServiceBuilder project)
public static SidecarBuilder GetOrAddSidecar(DotnetProjectServiceBuilder project)
{
// Bring your rain boots.
project.RelocateDiagnosticsDomainSockets = true;

2
src/Microsoft.Tye.Extensions/Elastic/ElasticStackExtension.cs

@ -83,7 +83,7 @@ namespace Microsoft.Tye.Extensions.Elastic
var kibana = elastic.Bindings.Single(b => b.Name == "kibana");
elastic.Bindings.Remove(kibana);
foreach (var project in context.Application.Services.OfType<ProjectServiceBuilder>())
foreach (var project in context.Application.Services.OfType<DotnetProjectServiceBuilder>())
{
var sidecar = DiagnosticAgent.GetOrAddSidecar(project);

2
src/Microsoft.Tye.Extensions/Zipkin/ZipkinExtension.cs

@ -60,7 +60,7 @@ namespace Microsoft.Tye.Extensions.Zipkin
}
else if (context.Operation == ExtensionContext.OperationKind.Deploy)
{
foreach (var project in context.Application.Services.OfType<ProjectServiceBuilder>())
foreach (var project in context.Application.Services.OfType<DotnetProjectServiceBuilder>())
{
var sidecar = DiagnosticAgent.GetOrAddSidecar(project);

2
src/Microsoft.Tye.Hosting/Model/ProjectRunInfo.cs

@ -9,7 +9,7 @@ namespace Microsoft.Tye.Hosting.Model
{
public class ProjectRunInfo : RunInfo
{
public ProjectRunInfo(ProjectServiceBuilder project)
public ProjectRunInfo(DotnetProjectServiceBuilder project)
{
ProjectFile = project.ProjectFile;
Args = project.Args;

39
src/tye/ApplicationBuilderExtensions.cs

@ -47,19 +47,19 @@ namespace Microsoft.Tye
readiness = null;
replicas = 1;
}
else if (service is ContainerServiceBuilder container)
else if (service is DockerFileServiceBuilder dockerFile)
{
var dockerRunInfo = new DockerRunInfo(container.Image, container.Args)
var dockerRunInfo = new DockerRunInfo(dockerFile.Image, dockerFile.Args)
{
IsAspNet = container.IsAspNet
IsAspNet = dockerFile.IsAspNet
};
if (!string.IsNullOrEmpty(container.DockerFile))
if (!string.IsNullOrEmpty(dockerFile.DockerFile))
{
dockerRunInfo.DockerFile = new FileInfo(container.DockerFile);
if (!string.IsNullOrEmpty(container.DockerFileContext))
dockerRunInfo.DockerFile = new FileInfo(dockerFile.DockerFile);
if (!string.IsNullOrEmpty(dockerFile.DockerFileContext))
{
dockerRunInfo.DockerFileContext = new FileInfo(container.DockerFileContext);
dockerRunInfo.DockerFileContext = new FileInfo(dockerFile.DockerFileContext);
}
else
{
@ -67,6 +67,29 @@ namespace Microsoft.Tye
}
}
foreach (var mapping in dockerFile.Volumes)
{
dockerRunInfo.VolumeMappings.Add(new DockerVolume(mapping.Source, mapping.Name, mapping.Target));
}
runInfo = dockerRunInfo;
replicas = dockerFile.Replicas;
liveness = dockerFile.Liveness != null ? GetProbeFromBuilder(dockerFile.Liveness) : null;
readiness = dockerFile.Readiness != null ? GetProbeFromBuilder(dockerFile.Readiness) : null;
foreach (var entry in dockerFile.EnvironmentVariables)
{
env.Add(entry.ToHostingEnvironmentVariable());
}
}
else if (service is ContainerServiceBuilder container)
{
var dockerRunInfo = new DockerRunInfo(container.Image, container.Args)
{
IsAspNet = container.IsAspNet
};
foreach (var mapping in container.Volumes)
{
dockerRunInfo.VolumeMappings.Add(new DockerVolume(mapping.Source, mapping.Name, mapping.Target));
@ -94,7 +117,7 @@ namespace Microsoft.Tye
env.Add(entry.ToHostingEnvironmentVariable());
}
}
else if (service is ProjectServiceBuilder project)
else if (service is DotnetProjectServiceBuilder project)
{
if (project.TargetFrameworks.Length > 1)
{

4
test/E2ETest/Microsoft.Tye.E2ETests.csproj

@ -31,4 +31,8 @@
<Compile Remove="testassets\**\*" />
</ItemGroup>
<ItemGroup>
<None Remove="testassets\generate\dockerfile.yaml" />
</ItemGroup>
</Project>

39
test/E2ETest/TyeGenerateTests.cs

@ -427,5 +427,44 @@ namespace E2ETest
await DockerAssert.DeleteDockerImagesAsync(output, projectName);
}
}
[ConditionalFact]
[SkipIfDockerNotRunning]
public async Task Generate_DockerFile()
{
var applicationName = "dockerfile";
var environment = "production";
var projectName = "frontend-backend";
await DockerAssert.DeleteDockerImagesAsync(output, "frontend");
await DockerAssert.DeleteDockerImagesAsync(output, "backend");
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, $"{projectName}-generate-{environment}.yaml"));
var expectedContent = await File.ReadAllTextAsync($"testassets/generate/{applicationName}.yaml");
YamlAssert.Equals(expectedContent, content, output);
await DockerAssert.AssertImageExistsAsync(output, "frontend");
await DockerAssert.AssertImageExistsAsync(output, "backend");
}
finally
{
await DockerAssert.DeleteDockerImagesAsync(output, "frontend");
await DockerAssert.DeleteDockerImagesAsync(output, "backend");
}
}
}
}

6
test/E2ETest/TyeRunTests.cs

@ -161,7 +161,7 @@ namespace E2ETest
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");
var project = (DotnetProjectServiceBuilder)application.Services.First(s => s.Name == "backend");
application.Services.Remove(project);
var outputFileName = project.AssemblyName + ".dll";
@ -208,7 +208,7 @@ namespace E2ETest
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");
var project = (DotnetProjectServiceBuilder)application.Services.First(s => s.Name == "frontend");
application.Services.Remove(project);
var outputFileName = project.AssemblyName + ".dll";
@ -257,7 +257,7 @@ namespace E2ETest
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");
var project = (DotnetProjectServiceBuilder)application.Services.First(s => s.Name == "backend-baseimage");
// check ContainerInfo values
Assert.True(string.Equals(project.ContainerInfo!.BaseImageName, "mcr.microsoft.com/dotnet/core/sdk"));

109
test/E2ETest/testassets/generate/dockerfile.yaml

@ -0,0 +1,109 @@
kind: Deployment
apiVersion: apps/v1
metadata:
name: backend
labels:
app.kubernetes.io/name: 'backend'
app.kubernetes.io/part-of: 'frontend-backend'
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: backend
template:
metadata:
labels:
app.kubernetes.io/name: 'backend'
app.kubernetes.io/part-of: 'frontend-backend'
spec:
containers:
- name: backend
image: backend:latest
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: 'http://*'
- name: PORT
value: '80'
- name: SERVICE__FRONTEND__PROTOCOL
value: 'http'
- name: SERVICE__FRONTEND__PORT
value: '80'
- name: SERVICE__FRONTEND__HOST
value: 'frontend'
ports:
- containerPort: 80
...
---
kind: Service
apiVersion: v1
metadata:
name: backend
labels:
app.kubernetes.io/name: 'backend'
app.kubernetes.io/part-of: 'frontend-backend'
spec:
selector:
app.kubernetes.io/name: backend
type: ClusterIP
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
...
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: frontend
labels:
app.kubernetes.io/name: 'frontend'
app.kubernetes.io/part-of: 'frontend-backend'
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: frontend
template:
metadata:
labels:
app.kubernetes.io/name: 'frontend'
app.kubernetes.io/part-of: 'frontend-backend'
spec:
containers:
- name: frontend
image: frontend:1.0.0
imagePullPolicy: Always
env:
- name: ASPNETCORE_URLS
value: 'http://*'
- name: PORT
value: '80'
- name: SERVICE__BACKEND__PROTOCOL
value: 'http'
- name: SERVICE__BACKEND__PORT
value: '80'
- name: SERVICE__BACKEND__HOST
value: 'backend'
ports:
- containerPort: 80
...
---
kind: Service
apiVersion: v1
metadata:
name: frontend
labels:
app.kubernetes.io/name: 'frontend'
app.kubernetes.io/part-of: 'frontend-backend'
spec:
selector:
app.kubernetes.io/name: frontend
type: ClusterIP
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
...
Loading…
Cancel
Save