public static async Task ExecuteAsync(OutputContext output, string imageName, string imageTag) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (imageName is null) { throw new ArgumentNullException(nameof(imageName)); } if (imageTag is null) { throw new ArgumentNullException(nameof(imageTag)); } output.WriteDebugLine("running docker push"); output.WriteDebugLine($"> docker push {imageName}:{imageTag}"); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"docker", $"push {imageName}:{imageTag}", stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"done running docker push exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("docker push failed"); } }
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."); } }
private static async Task PackageApplicationAsync(OutputContext output, Application application, string applicationName, string environment) { using var step = output.BeginStep("Deploying Application Manifests..."); using var tempFile = TempFile.Create(); output.WriteInfoLine($"Writing output to '{tempFile.FilePath}'."); { using var stream = File.OpenWrite(tempFile.FilePath); using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); if (application.Globals.DeploymentKind == DeploymentKind.None) { // No extra steps } else if (application.Globals.DeploymentKind == DeploymentKind.Kubernetes) { await ApplicationYamlWriter.WriteAsync(output, writer, application); } else if (application.Globals.DeploymentKind == DeploymentKind.Oam) { await OamApplicationGenerator.WriteOamApplicationAsync(writer, output, application, applicationName, environment); } else { throw new InvalidOperationException($"Unknown DeploymentKind: " + application.Globals.DeploymentKind); } } output.WriteDebugLine("Running 'kubectl apply'."); output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"kubectl", $"apply -f \"{tempFile.FilePath}\"", System.Environment.CurrentDirectory, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"Done running 'kubectl apply' exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("'kubectl apply' failed."); } output.WriteInfoLine($"Deployed application '{applicationName}'."); step.MarkComplete(); }
public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service) { var yaml = service.Outputs.OfType <IYamlManifestOutput>().ToArray(); if (yaml.Length == 0) { output.WriteDebugLine($"No yaml manifests found for service '{service.FriendlyName}'. Skipping."); return; } using var tempFile = TempFile.Create(); output.WriteDebugLine($"Writing output to '{tempFile.FilePath}'."); { using var stream = File.OpenWrite(tempFile.FilePath); using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); var yamlStream = new YamlStream(yaml.Select(y => y.Yaml)); yamlStream.Save(writer, assignAnchors: false); } // kubectl apply logic is implemented in the client in older versions of k8s. The capability // to get the same behavior in the server isn't present in every version that's relevant. // // https://kubernetes.io/docs/reference/using-api/api-concepts/#server-side-apply // output.WriteDebugLine("Running 'kubectl apply'."); output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"kubectl", $"apply -f \"{tempFile.FilePath}\"", System.Environment.CurrentDirectory, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"Done running 'kubectl apply' exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("'kubectl apply' failed."); } output.WriteInfoLine($"Deployed service '{service.FriendlyName}'."); }
public static async Task ExecuteAsync(OutputContext output, string registryHostname, string chartFilePath) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (registryHostname is null) { throw new ArgumentNullException(nameof(registryHostname)); } if (chartFilePath is null) { throw new ArgumentNullException(nameof(chartFilePath)); } // NOTE: this is currently hardcoded to use ACR and uses the azure CLI to do // the push operation. Helm 3 has support for pushing to OCI repositories, // but it's not documented that it works yet for azure. if (!registryHostname.EndsWith(".azurecr.io")) { throw new CommandException("currently pushing of helm charts is limited to '*.azurecr.io'"); } var registryName = registryHostname.Substring(0, registryHostname.Length - ".azurecr.io".Length); output.WriteDebugLine("running az acr helm push"); output.WriteDebugLine($"> az acr helm push --name {registryName} \"{chartFilePath}\""); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( $"az", $"acr helm push --name {registryName} \"{chartFilePath}\"", stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"done running az acr helm push exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("az acr helm push failed"); } }
private static async Task EvaluateMSBuildAsync(OutputContext output, FileInfo projectFile, Project project) { try { output.WriteDebugLine("Installing msbuild targets."); TargetInstaller.Install(projectFile.FullName); output.WriteDebugLine("Installed msbuild targets."); } catch (Exception ex) { throw new CommandException("Failed to install targets.", ex); } var outputFilePath = Path.GetTempFileName(); try { var capture = output.Capture(); var opulenceRoot = Path.GetDirectoryName(typeof(Program).Assembly.Location); var restore = string.Empty; if (!File.Exists(Path.Combine(projectFile.DirectoryName, "obj", "project.assets.json"))) { restore = "/restore"; } output.WriteDebugLine("Running 'dotnet msbuild'."); output.WriteCommandLine("dotnet", $"msbuild {restore} /t:EvaluateOpulenceProjectInfo \"/p:OpulenceTargetLocation={opulenceRoot}\" \"/p:OpulenceOutputFilePath={outputFilePath}\""); var exitCode = await Process.ExecuteAsync( $"dotnet", $"msbuild {restore} /t:EvaluateOpulenceProjectInfo \"/p:OpulenceTargetLocation={opulenceRoot}\" \"/p:OpulenceOutputFilePath={outputFilePath}\"", workingDir : projectFile.DirectoryName, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"Done running 'dotnet msbuild' exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("'dotnet msbuild' failed."); } var lines = await File.ReadAllLinesAsync(outputFilePath); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; if (line.StartsWith("version=")) { project.Version = line.Substring("version=".Length).Trim(); output.WriteDebugLine($"Found application version: {line}"); continue; } if (line.StartsWith("tfm")) { project.TargetFramework = line.Substring("tfm=".Length).Trim(); output.WriteDebugLine($"Found target framework: {line}"); continue; } if (line.StartsWith("frameworks=")) { var right = line.Substring("frameworks=".Length).Trim(); project.Frameworks.AddRange(right.Split(",").Select(s => new Framework(s))); output.WriteDebugLine($"Found shared frameworks: {line}"); continue; } } } finally { File.Delete(outputFilePath); } }
public static async Task BuildHelmChartAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container, HelmChartStep chart) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (service is null) { throw new ArgumentNullException(nameof(service)); } if (project is null) { throw new ArgumentNullException(nameof(project)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } if (chart is null) { throw new ArgumentNullException(nameof(chart)); } var projectDirectory = Path.Combine(application.RootDirectory, Path.GetDirectoryName(project.RelativeFilePath) !); var outputDirectoryPath = Path.Combine(projectDirectory, "bin"); using var tempDirectory = TempDirectory.Create(); HelmChartGenerator.ApplyHelmChartDefaults(application, service, container, chart); var chartRoot = Path.Combine(projectDirectory, "charts"); var chartPath = Path.Combine(chartRoot, chart.ChartName); output.WriteDebugLine($"Looking for existing chart in '{chartPath}'."); 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, service, project, container, chart, new DirectoryInfo(tempDirectory.DirectoryPath)); } output.WriteDebugLine("Running 'helm package'."); output.WriteCommandLine("helm", $"package -d \"{outputDirectoryPath}\" --version {project.Version.Replace('+', '-')} --app-version {project.Version.Replace('+', '-')}"); var capture = output.Capture(); var exitCode = await Process.ExecuteAsync( "helm", $"package . -d \"{outputDirectoryPath}\" --version {project.Version.Replace('+', '-')} --app-version {project.Version.Replace('+', '-')}", workingDir : chartPath, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"Running 'helm package' exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("Running 'helm package' failed."); } output.WriteInfoLine($"Created Helm Chart: {Path.Combine(outputDirectoryPath, chart.ChartName + "-" + project.Version.Replace('+', '-') + ".tgz")}"); }
public static async Task InitializeAsync(OutputContext output, Application application) { output.WriteInfoLine("reading project information"); try { output.WriteDebugLine("installing msbuild targets"); TargetInstaller.Install(application.ProjectFilePath); output.WriteDebugLine("installed msbuild targets"); } catch (Exception ex) { throw new CommandException("Failed to install targets.", ex); } var outputFilePath = Path.GetTempFileName(); try { output.WriteDebugLine("executing dotnet msbuild"); var capture = output.Capture(); var opulenceRoot = Path.GetDirectoryName(typeof(Program).Assembly.Location); var exitCode = await Process.ExecuteAsync( $"dotnet", $"msbuild /t:EvaluateOpulenceProjectInfo \"/p:OpulenceTargetLocation={opulenceRoot}\" \"/p:OpulenceOutputFilePath={outputFilePath}\"", workingDir : application.ProjectDirectory, stdOut : capture.StdOut, stdErr : capture.StdErr); output.WriteDebugLine($"executed dotnet msbuild exit code: {exitCode}"); if (exitCode != 0) { throw new CommandException("Getting project info failed."); } var lines = await File.ReadAllLinesAsync(outputFilePath); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; if (line.StartsWith("version=")) { application.Version = line.Substring("version=".Length).Trim(); output.WriteDebugLine($"found application version: {line}"); continue; } if (line.StartsWith("tfm")) { application.TargetFramework = line.Substring("tfm=".Length).Trim(); output.WriteDebugLine($"found target framework: {line}"); continue; } if (line.StartsWith("frameworks=")) { var right = line.Substring("frameworks=".Length).Trim(); application.Frameworks.AddRange(right.Split(",").Select(s => new Framework(s))); output.WriteDebugLine($"found shared frameworks: {line}"); continue; } } } finally { File.Delete(outputFilePath); } output.WriteDebugLine("done reading project information"); }
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"); }
public static async Task BuildContainerImageAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (application is null) { throw new ArgumentNullException(nameof(application)); } if (service is null) { throw new ArgumentNullException(nameof(service)); } if (project is null) { throw new ArgumentNullException(nameof(project)); } if (container is null) { throw new ArgumentNullException(nameof(container)); } using var tempFile = TempFile.Create(); var dockerFilePath = Path.Combine(application.GetProjectDirectory(project), Path.GetDirectoryName(project.RelativeFilePath) !, "Dockerfile"); if (File.Exists(dockerFilePath)) { output.WriteDebugLine($"Using existing dockerfile '{dockerFilePath}'."); } else { await DockerfileGenerator.WriteDockerfileAsync(output, application, service, project, container, tempFile.FilePath); dockerFilePath = tempFile.FilePath; } output.WriteDebugLine("Running 'docker build'."); output.WriteCommandLine("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.GetProjectDirectory(project), 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}"); service.Outputs.Add(new DockerImageOutput(container.ImageName !, container.ImageTag !)); }