public static void ApplyContainerDefaults(Application application, ContainerStep container) { if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } if (container.BaseImageName == null && application.Frameworks.Any(f => f.Name == "Microsoft.AspNetCore.App")) { container.BaseImageName = "mcr.microsoft.com/dotnet/core/aspnet"; } else if (container.BaseImageName == null) { container.BaseImageName = "mcr.microsoft.com/dotnet/core/runtime"; } if (container.BaseImageTag == null && application.TargetFramework == "netcoreapp3.1") { container.BaseImageTag = "3.1"; } else if (container.BaseImageTag == null && application.TargetFramework == "netcoreapp3.0") { container.BaseImageTag = "3.0"; } if (container.BaseImageTag == null) { throw new CommandException($"Unsupported TFM {application.TargetFramework}."); } container.BuildImageName ??= "mcr.microsoft.com/dotnet/core/sdk"; container.BuildImageTag ??= "3.1"; if (container.ImageName == null && application.Config.Container?.Registry?.Hostname == null) { container.ImageName ??= application.Name.ToLowerInvariant(); } else if (container.ImageName == null && application.Config.Container?.Registry?.Hostname != null) { container.ImageName ??= $"{application.Config.Container?.Registry?.Hostname}/{application.Name.ToLowerInvariant()}"; } container.ImageTag ??= application.Version.Replace("+", "-"); }
public static void ApplyHelmChartDefaults(Application application, ContainerStep container, HelmChartStep chart) { if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } if (chart is null) { throw new ArgumentNullException(nameof(chart)); } chart.ChartName ??= application.Name.ToLowerInvariant(); }
public static async Task GenerateAsync(OutputContext output, Application application, ContainerStep container, HelmChartStep chart, DirectoryInfo outputDirectory) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } if (chart is null) { throw new ArgumentNullException(nameof(chart)); } if (outputDirectory is null) { throw new ArgumentNullException(nameof(outputDirectory)); } output.WriteDebugLine("generating helm chart"); ApplyHelmChartDefaults(application, container, chart); // The directory with the charts needs to be the same as the chart name var chartDirectoryPath = Path.Combine(outputDirectory.FullName, chart.ChartName); Directory.CreateDirectory(chartDirectoryPath); var templateDirectoryPath = Path.Combine( Path.GetDirectoryName(typeof(HelmChartGenerator).Assembly.Location) !, "Templates", "Helm"); DirectoryCopy.Copy(templateDirectoryPath, chartDirectoryPath); // Write Chart.yaml // // apiVersion: v1 // name: <appname> // version: <version> // appVersion: <version> await File.WriteAllLinesAsync(Path.Combine(chartDirectoryPath, "Chart.yaml"), new[] { $"apiVersion: v1", $"name: {chart.ChartName}", $"# helm requires the version and appVersion to specified in Chart.yaml", $"# opulence will override these values when packaging the chart", $"version: {application.Version.Replace('+', '-')}", $"appVersion: {application.Version.Replace('+', '-')}" }); // Write values.yaml // // image: // repository: rynowak.azurecr.io/rochambot/gamemaster await File.WriteAllLinesAsync(Path.Combine(chartDirectoryPath, "values.yaml"), new[] { $"image:", $" repository: {container.ImageName}", }); output.WriteDebugLine("done generating helm chart"); }
public static async Task BuildHelmChartAsync(OutputContext output, Application application, ContainerStep container, HelmChartStep chart) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } if (chart is null) { throw new ArgumentNullException(nameof(chart)); } output.WriteInfoLine("building helm chart"); var outputDirectoryPath = Path.Combine(application.ProjectDirectory, "bin"); using var tempDirectory = TempDirectory.Create(); HelmChartGenerator.ApplyHelmChartDefaults(application, container, chart); var chartRoot = Path.Combine(application.ProjectDirectory, "charts"); var chartPath = Path.Combine(chartRoot, chart.ChartName); if (Directory.Exists(chartPath)) { output.WriteDebugLine($"found existing chart in '{chartPath}'"); } else { chartRoot = tempDirectory.DirectoryPath; chartPath = Path.Combine(chartRoot, chart.ChartName); output.WriteDebugLine($"generating chart in '{chartPath}"); await HelmChartGenerator.GenerateAsync(output, application, container, chart, new DirectoryInfo(tempDirectory.DirectoryPath)); } output.WriteDebugLine("running helm package"); output.WriteDebugLine($"> helm package -d \"{outputDirectoryPath}\" --version {application.Version.Replace('+', '-')} --app-version {application.Version.Replace('+', '-')}"); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( "helm", $"package . -d \"{outputDirectoryPath}\" --version {application.Version.Replace('+', '-')} --app-version {application.Version.Replace('+', '-')}", workingDir : chartPath, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"running helm package exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("helm package failed"); } output.WriteDebugLine("done building helm chart"); }
private static async Task WriteLocalPublishDockerfileAsync(StreamWriter writer, Application application, ContainerStep container) { await writer.WriteLineAsync($"FROM {container.BaseImageName}:{container.BaseImageTag}"); await writer.WriteLineAsync($"WORKDIR /app"); await writer.WriteLineAsync($"COPY . /app"); await writer.WriteLineAsync($"ENTRYPOINT [\"dotnet\", \"{application.Name}.dll\"]"); }
private static async Task WriteMultiphaseDockerfileAsync(StreamWriter writer, Application application, ContainerStep container) { await writer.WriteLineAsync($"FROM {container.BuildImageName}:{container.BuildImageTag} as SDK"); await writer.WriteLineAsync($"WORKDIR /src"); await writer.WriteLineAsync($"COPY . ."); await writer.WriteLineAsync($"RUN dotnet publish -c Release -o /out"); await writer.WriteLineAsync($"FROM {container.BaseImageName}:{container.BaseImageTag} as RUNTIME"); await writer.WriteLineAsync($"WORKDIR /app"); await writer.WriteLineAsync($"COPY --from=SDK /out ."); await writer.WriteLineAsync($"ENTRYPOINT [\"dotnet\", \"{application.Name}.dll\"]"); }
public static async Task WriteDockerfileAsync(OutputContext output, Application application, ContainerStep container, string filePath) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } if (filePath is null) { throw new ArgumentNullException(nameof(filePath)); } ApplyContainerDefaults(application, container); using var stream = File.OpenWrite(filePath); using var writer = new StreamWriter(stream, encoding: Encoding.UTF8, leaveOpen: true); output.WriteDebugLine($"writing dockerfile to '{filePath}'"); if (container.UseMultiphaseDockerfile ?? true) { await WriteMultiphaseDockerfileAsync(writer, application, container); } else { await WriteLocalPublishDockerfileAsync(writer, application, container); } output.WriteDebugLine("done writing dockerfile"); }
public static async Task BuildContainerImageAsync(OutputContext output, Application application, ContainerStep container) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } DockerfileGenerator.ApplyContainerDefaults(application, container); using var tempFile = TempFile.Create(); var dockerFilePath = Path.Combine(application.ProjectDirectory, "Dockerfile"); if (File.Exists(dockerFilePath)) { output.WriteDebugLine("using existing dockerfile"); } else { await DockerfileGenerator.WriteDockerfileAsync(output, application, container, tempFile.FilePath); dockerFilePath = tempFile.FilePath; } output.WriteDebugLine("running docker build"); output.WriteDebug($"> docker build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"docker", $"build . -t {container.ImageName}:{container.ImageTag} -f \"{dockerFilePath}\"", application.ProjectDirectory, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"done running docker build exit code:{exitCode}"); if (exitCode != 0) { throw new CommandException("Docker build failed."); } }