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 <OpulenceConfig?> ReadConfigAsync(OutputContext output, string directoryPath) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (directoryPath is null) { throw new ArgumentNullException(nameof(directoryPath)); } output.WriteDebugLine($"searching for opulence.json above '{directoryPath}'"); var configFilePath = DirectorySearch.AscendingSearch(directoryPath, "opulence.json"); if (configFilePath == null) { output.WriteDebugLine("no configuration found"); return(null); } output.WriteDebugLine($"configuration found at '{configFilePath}'"); using var stream = File.OpenRead(configFilePath); return(await JsonSerializer.DeserializeAsync <OpulenceConfig>(stream)); }
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 static async Task WriteDockerfileAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container, string filePath) { 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 (filePath is null) { throw new ArgumentNullException(nameof(filePath)); } using var stream = File.OpenWrite(filePath); using var writer = new StreamWriter(stream, encoding: Encoding.UTF8, leaveOpen: true); var entryPoint = Path.GetFileNameWithoutExtension(project.RelativeFilePath); output.WriteDebugLine($"Writing dockerfile to '{filePath}'."); if (container.UseMultiphaseDockerfile ?? true) { await WriteMultiphaseDockerfileAsync(writer, entryPoint, container); } else { await WriteLocalPublishDockerfileAsync(writer, entryPoint, container); } output.WriteDebugLine("Done writing dockerfile."); }
public override 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(Task.CompletedTask); } var outputFilePath = Path.Combine(OutputDirectory.FullName, $"{service.Service.Name}.yaml"); output.WriteInfoLine($"Writing output to '{outputFilePath}'."); if (File.Exists(outputFilePath) && !Force) { throw new CommandException($"'{service.Service.Name}.yaml' already exists for project. use '--force' to overwrite."); } File.Delete(outputFilePath); using var stream = File.OpenWrite(outputFilePath); using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); var yamlStream = new YamlStream(yaml.Select(y => y.Yaml)); yamlStream.Save(writer, assignAnchors: false); return(Task.CompletedTask); }
protected bool SkipForEnvironment(OutputContext output, ServiceEntry service, string environment) { if (!service.AppliesToEnvironment(environment)) { output.WriteDebugLine($"Service '{service.FriendlyName}' is not part of environment '{environment}'. Skipping."); return(true); } return(false); }
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"); } }
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 <Application> CreateApplicationAsync(OutputContext output, FileInfo projectFile) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (projectFile is null) { throw new ArgumentNullException(nameof(projectFile)); } if (string.Equals(projectFile.Extension, ".sln", StringComparison.Ordinal)) { output.WriteDebugLine($"Solution '{projectFile.FullName}' was provided as input."); return(await CreateApplicationForSolutionAsync(output, projectFile)); } else { output.WriteDebugLine($"Project '{projectFile.FullName}' was provided as input."); return(await CreateApplicationForProjectAsync(output, projectFile)); } }
public static Task WriteAsync(OutputContext output, StreamWriter writer, Application application) { var yaml = application.Services.SelectMany(s => s.Outputs.OfType <IYamlManifestOutput>()).ToArray(); if (yaml.Length == 0) { output.WriteDebugLine($"No yaml manifests found. Skipping."); return(Task.CompletedTask); } var yamlStream = new YamlStream(yaml.Select(y => y.Yaml)); yamlStream.Save(writer, assignAnchors: false); return(Task.CompletedTask); }
public static async Task RunProjectScriptAsync(OutputContext output, Application application) { var scriptFilePath = Path.ChangeExtension(application.ProjectFilePath, ".csx"); output.WriteDebugLine($"looking for project script at {scriptFilePath}"); if (!File.Exists(scriptFilePath)) { output.WriteDebugLine($"no project script found"); return; } output.WriteInfoLine($"configuring application using {Path.GetFileName(scriptFilePath)}"); var code = File.ReadAllText(scriptFilePath); var script = CSharpScript.Create <ConfigurationGlobals>( code, options: ScriptOptions.Default, globalsType: typeof(ConfigurationGlobals), assemblyLoader: null); script = script.ContinueWith <ConfigurationGlobals>(@"Package(App)", options: ScriptOptions.Default); output.WriteDebugLine("compiling project script"); var diagnostics = script.Compile(); if (diagnostics.Length > 0) { var builder = new StringBuilder(); builder.AppendLine($"Script '{scriptFilePath}' had compilation errors."); foreach (var diagnostic in diagnostics) { builder.AppendLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic)); } throw new CommandException(builder.ToString()); } output.WriteDebugLine("done compiling project script"); var obj = new ConfigurationGlobals() { App = application, }; output.WriteDebugLine("running project script"); try { await script.RunAsync(obj); } catch (Exception ex) { throw new CommandException("failed executing project script", ex); } output.WriteDebugLine("done running project script"); }
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 override async Task <object?> ExecuteAsync() { if (callbacks.Count == 0) { return(Task.FromResult <object?>(null)); } if (callbacks.Count > 1) { throw new InvalidOperationException("More than one application type is not supported."); } var kvp = callbacks.Single(); var type = kvp.Key; var delegates = kvp.Value; output.WriteDebugLine($"Creating instance of application type '{type}'."); var application = Activator.CreateInstance(type); output.WriteDebugLine($"Done creating instance of application type '{type}'."); var wrapper = new ApplicationWrapper(application !, rootDirectory); wrapper.Globals.Name ??= name; foreach (var service in wrapper.Services) { output.WriteDebugLine($"Found service '{service.FriendlyName} {{ Name: {service.Service.Name} }}'."); string?projectRelativeFilePath = null; string?projectFilePath = null; if (solution != null) { var project = FindProjectInSolution(solution, service.FriendlyName); if (project == null) { output.WriteDebugLine($"Could not find project for service '{service.FriendlyName}'."); continue; } output.WriteDebugLine($"Found project '{project.RelativePath}' for service '{service.FriendlyName}'."); projectRelativeFilePath = project.RelativePath.Replace('\\', Path.DirectorySeparatorChar); projectFilePath = project.AbsolutePath.Replace('\\', Path.DirectorySeparatorChar); } else if (projectFile != null) { var normalized = Names.NormalizeToFriendly(Path.GetFileNameWithoutExtension(projectFile.Name)); if (!string.Equals(normalized, service.FriendlyName)) { output.WriteDebugLine($"Skipping service '{service.FriendlyName}'."); continue; } projectRelativeFilePath = projectFile.FullName; projectFilePath = projectFile.FullName; } if (projectFilePath != null) { var project = new Project(projectRelativeFilePath !); await ProjectReader.ReadProjectDetailsAsync(output, new FileInfo(projectFilePath), project); service.Service.Source = project; // Apply defaults to everything that has a project. var container = new ContainerInfo(); service.Service.GeneratedAssets.Container = container; DockerfileGenerator.ApplyContainerDefaults(wrapper, service, project, container); } } output.WriteDebugLine($"Running {delegates.Count} customization callbacks."); for (var i = 0; i < delegates.Count; i++) { delegates[i].DynamicInvoke(application); } output.WriteDebugLine($"Done running {delegates.Count} customization callbacks."); return(application); }
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 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 !)); }
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 <ApplicationWrapper?> RunCustomizationScriptAsync(OutputContext output, FileInfo solutionFile, SolutionFile solution) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (solutionFile is null) { throw new ArgumentNullException(nameof(solutionFile)); } if (solution is null) { throw new ArgumentNullException(nameof(solution)); } using (var step = output.BeginStep("Applying Application Customizations...")) { var scriptFilePath = Path.Combine(solutionFile.DirectoryName, "Opulence.csx"); output.WriteDebugLine($"Looking for customization script at '{scriptFilePath}'."); if (!File.Exists(scriptFilePath)) { output.WriteDebugLine($"No customization script found."); step.MarkComplete("Skipping..."); return(null); } output.WriteInfoLine($"Configuring application using '{Path.GetFileName(scriptFilePath)}'."); var code = File.ReadAllText(scriptFilePath); var script = CSharpScript.Create <object>( code, options: ScriptOptions.Default.WithImports(new [] { "System", "System.Collections.Generic", }), globalsType: typeof(PipelineHolder), assemblyLoader: null); script = script.ContinueWith <object>(@"return await Pipeline.ExecuteAsync(__Pipeline);", options: ScriptOptions.Default); output.WriteDebugLine($"Compiling {Path.GetFileName(scriptFilePath)}'."); script.Compile(); var diagnostics = script.Compile(); if (diagnostics.Length > 0) { var builder = new StringBuilder(); output.WriteDebugLine($"Script '{scriptFilePath}' had compilation errors."); builder.AppendLine($"Script '{scriptFilePath}' had compilation errors."); foreach (var diagnostic in diagnostics) { output.WriteDebugLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic)); builder.AppendLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic)); } throw new CommandException(builder.ToString()); } output.WriteDebugLine($"Done compiling {Path.GetFileName(scriptFilePath)}'."); var pipeline = new CustomizationPipeline( output, rootDirectory: Path.GetDirectoryName(scriptFilePath) !, name: Names.NormalizeToDns(Path.GetFileNameWithoutExtension(solutionFile.Name)), solution, projectFile: null); var holder = new PipelineHolder(pipeline); output.WriteDebugLine($"Running {Path.GetFileName(scriptFilePath)}'."); object obj; try { var result = await script.RunAsync(holder); obj = result.ReturnValue; } catch (Exception ex) { throw new CommandException($"Failed executing {Path.GetFileName(scriptFilePath)}'.", ex); } step.MarkComplete(); return(new ApplicationWrapper(obj, Path.GetDirectoryName(scriptFilePath) !)); } }
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 ExecuteAsync(OutputContext output, FileInfo projectFile, List <string> outputs, bool force) { var config = await OpulenceConfigFactory.ReadConfigAsync(output, projectFile.DirectoryName); if (config == null) { // Allow operating without config for now. output.WriteInfoLine("config was not found, using defaults"); config = new OpulenceConfig() { Container = new ContainerConfig() { Registry = new RegistryConfig(), } }; } var application = ApplicationFactory.CreateDefault(config, projectFile); await ProjectReader.InitializeAsync(output, application); await ScriptRunner.RunProjectScriptAsync(output, application); for (var i = 0; i < application.Steps.Count; i++) { var step = application.Steps[i]; if (step is ContainerStep container) { if (!outputs.Contains("container")) { // We should still apply the defaults here because they'll be used by // the helm step. DockerfileGenerator.ApplyContainerDefaults(application, container); output.WriteDebugLine("skipping container"); continue; } output.WriteInfoLine("generating dockerfile"); var dockerFilePath = Path.Combine(application.ProjectDirectory, "Dockerfile"); if (File.Exists(dockerFilePath) && !force) { throw new CommandException("'Dockerfile' already exists for project. use --force to overwrite"); } // force multi-phase dockerfile - this makes much more sense in the workflow // where you're going to maintain the dockerfile yourself. container.UseMultiphaseDockerfile = true; File.Delete(dockerFilePath); await DockerfileGenerator.WriteDockerfileAsync(output, application, container, dockerFilePath); } else if (step is HelmChartStep chart) { if (!outputs.Contains("chart")) { output.WriteDebugLine("skipping helm chart"); continue; } output.WriteInfoLine("generating helm charts"); var chartDirectory = Path.Combine(application.ProjectDirectory, "charts"); if (Directory.Exists(chartDirectory) && !force) { throw new CommandException("'charts' directory already exists for project. use --force to overwrite"); } await HelmChartGenerator.GenerateAsync( output, application, application.Steps.Get <ContainerStep>() !, chart, new DirectoryInfo(chartDirectory)); } } }
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")}"); }