Ejemplo n.º 1
0
        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("+", "-");
        }
Ejemplo n.º 2
0
        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();
        }
Ejemplo n.º 3
0
        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");
        }
Ejemplo n.º 4
0
        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");
        }
Ejemplo n.º 5
0
        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\"]");
        }
Ejemplo n.º 6
0
        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\"]");
        }
Ejemplo n.º 7
0
        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");
        }
Ejemplo n.º 8
0
        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.");
            }
        }