private static async Task ExecuteAsync(OutputContext output, DirectoryInfo directory) { var opulenceFilePath = DirectorySearch.AscendingSearch(directory.FullName, "opulence.json"); if (opulenceFilePath != null) { output.WriteInfoLine($"found 'opulence.json' at '{Path.GetDirectoryName(opulenceFilePath)}'"); return; } output.WriteInfoLine("locating nearest sln file"); var solutionFilePath = DirectorySearch.AscendingWildcardSearch(directory.FullName, "*.sln").FirstOrDefault()?.FullName; if (opulenceFilePath == null && solutionFilePath != null && Confirm(output, $"use '{Path.GetDirectoryName(solutionFilePath)}' as root?")) { opulenceFilePath = Path.Combine(Path.GetDirectoryName(solutionFilePath) !, "opulence.json"); } if (opulenceFilePath == null && Confirm(output, "use project directory as root?")) { opulenceFilePath = Path.Combine(directory.FullName, "opulence.json"); } if (opulenceFilePath == null) { throw new CommandException("cannot determine root directory"); } var config = new OpulenceConfig() { Container = new ContainerConfig() { Registry = new RegistryConfig(), } }; while (true) { output.WriteAlways("entry the container registry hostname (ex: example.azurecr.io): "); var line = Console.ReadLine(); output.WriteAlwaysLine(string.Empty); if (!string.IsNullOrEmpty(line)) { config.Container.Registry.Hostname = line.Trim(); break; } } using var stream = File.OpenWrite(opulenceFilePath); await JsonSerializer.SerializeAsync(stream, config, new JsonSerializerOptions() { WriteIndented = true, }); output.WriteInfo($"initialized opulence config at '{opulenceFilePath}'"); }
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) { if (SkipWithoutProject(output, service, out var project)) { return; } if (SkipWithoutContainerInfo(output, service, out var container)) { return; } if (container.UseMultiphaseDockerfile == false) { throw new CommandException("Generated Dockerfile workflow does not support single-phase Dockerfiles."); } container.UseMultiphaseDockerfile ??= true; var dockerFilePath = Path.Combine(application.GetProjectDirectory(project), "Dockerfile"); if (File.Exists(dockerFilePath) && !Force) { throw new CommandException("'Dockerfile' already exists for project. use '--force' to overwrite."); } File.Delete(dockerFilePath); await DockerfileGenerator.WriteDockerfileAsync(output, application, service, project, container, dockerFilePath); output.WriteInfoLine($"Generated Dockerfile at '{dockerFilePath}'."); }
public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service) { if (SkipWithoutProject(output, service, out var project)) { return; } if (SkipWithoutContainerInfo(output, service, out var container)) { return; } var chartDirectory = Path.Combine(application.GetProjectDirectory(project), "charts"); if (Directory.Exists(chartDirectory) && !Force) { throw new CommandException("'charts' directory already exists for project. use '--force' to overwrite."); } else if (Directory.Exists(chartDirectory)) { Directory.Delete(chartDirectory, recursive: true); } var chart = new HelmChartStep(); await HelmChartGenerator.GenerateAsync( output, application, service, project, container, chart, new DirectoryInfo(chartDirectory)); output.WriteInfoLine($"Generated Helm Chart at '{Path.Combine(chartDirectory, chart.ChartName)}'."); }
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); }
private static async Task PackageApplicationAsync(OutputContext output, Application application, DirectoryInfo outputDirectory, string applicationName, string environment) { using var step = output.BeginStep("Writing Application Manifests..."); var outputFile = Path.Combine(outputDirectory.FullName, $"{applicationName}-{environment}.yaml"); output.WriteInfoLine($"Writing output to '{outputFile}'."); File.Delete(outputFile); using var stream = File.OpenWrite(outputFile); 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); } step.MarkComplete(); }
public static async Task ReadProjectDetailsAsync(OutputContext output, FileInfo projectFile, Project project) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (projectFile is null) { throw new ArgumentNullException(nameof(projectFile)); } if (project is null) { throw new ArgumentNullException(nameof(project)); } using (var step = output.BeginStep("Reading Project Details...")) { await EvaluateMSBuildAsync(output, projectFile, project); if (!SemVersion.TryParse(project.Version, out var version)) { output.WriteInfoLine($"No version or invalid version 'application.Version' found, using default."); version = new SemVersion(0, 1, 0); project.Version = version.ToString(); } step.MarkComplete(); } }
private static async Task ExecuteAsync(OutputContext output, FileInfo projectFile) { var config = await OpulenceConfigFactory.ReadConfigAsync(output, projectFile.DirectoryName); if (config?.Container?.Registry?.Hostname == null) { throw new CommandException("a registry is required for push operations. run `dotnet-opulence init`"); } 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]; output.WriteInfoLine($"executing step: {step.DisplayName}"); if (step is ContainerStep container) { await DockerContainerBuilder.BuildContainerImageAsync(output, application, container); } else if (step is HelmChartStep chart) { await HelmChartBuilder.BuildHelmChartAsync(output, application, application.Steps.Get <ContainerStep>() !, chart); } } { if (application.Steps.Get <ContainerStep>() is ContainerStep container) { output.WriteInfoLine("pushing container"); await DockerPush.ExecuteAsync(output, container.ImageName !, container.ImageTag !); } if (application.Steps.Get <HelmChartStep>() is HelmChartStep chart) { output.WriteInfoLine("pushing chart"); var chartFilePath = Path.Combine(application.ProjectDirectory, "bin", $"{chart.ChartName}-{application.Version.Replace('+', '-')}.tgz"); await HelmPush.ExecuteAsync(output, application.Config.Container !.Registry !.Hostname !, chartFilePath); } } }
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"); }
private static async Task ExecuteAsync(OutputContext output, FileInfo projectFile) { 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]; output.WriteInfoLine($"executing step: {step.DisplayName}"); if (step is ContainerStep container) { await DockerContainerBuilder.BuildContainerImageAsync(output, application, container); } else if (step is HelmChartStep chart) { await HelmChartBuilder.BuildHelmChartAsync(output, application, application.Steps.Get <ContainerStep>() !, chart); } } }
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}'."); }
protected bool SkipWithoutProject(OutputContext output, ServiceEntry service, [MaybeNullWhen(returnValue: true)] out Project project) { if (output is null) { throw new ArgumentNullException(nameof(output)); } if (service is null) { throw new ArgumentNullException(nameof(service)); } if (service.Service.Source is Project p) { project = p; return(false); } output.WriteInfoLine($"Service '{service.FriendlyName}' does not have a project associated. Skipping."); project = default !;
public override async Task ExecuteAsync(OutputContext output, Application application, ServiceEntry service) { if (SkipWithoutProject(output, service, out var _)) { return; } if (SkipWithoutContainerInfo(output, service, out var _)) { return; } if (SkipForEnvironment(output, service, Environment)) { return; } foreach (var image in service.Outputs.OfType <DockerImageOutput>()) { await DockerPush.ExecuteAsync(output, image.ImageName, image.ImageTag); output.WriteInfoLine($"Pushed docker image: '{image.ImageName}:{image.ImageTag}'"); } }
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 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 !)); }
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, DirectoryInfo directory) { output.WriteBanner(); string?solutionFilePath = null; string?opulenceFilePath = null; using (var step = output.BeginStep("Looking For Existing Config...")) { opulenceFilePath = DirectorySearch.AscendingSearch(directory.FullName, "Opulence.csx"); if (opulenceFilePath != null) { output.WriteInfoLine($"Found 'Opulence.csx' at '{opulenceFilePath}'"); step.MarkComplete(); return; } else { output.WriteInfoLine("Not Found"); step.MarkComplete(); } } using (var step = output.BeginStep("Looking For .sln File...")) { solutionFilePath = DirectorySearch.AscendingWildcardSearch(directory.FullName, "*.sln").FirstOrDefault()?.FullName; if (opulenceFilePath == null && solutionFilePath != null && output.Confirm($"Use '{Path.GetDirectoryName(solutionFilePath)}' as Root?")) { opulenceFilePath = Path.Combine(Path.GetDirectoryName(solutionFilePath) !, "Opulence.csx"); step.MarkComplete(); } else { output.WriteInfoLine("Not Found."); step.MarkComplete(); } } if (opulenceFilePath == null && Path.GetFullPath(directory.FullName) != Path.GetFullPath(Environment.CurrentDirectory)) { // User specified a directory other than the current one using (var step = output.BeginStep("Trying Project Directory...")) { if (output.Confirm("Use Project Directory as Root?")) { opulenceFilePath = Path.Combine(directory.FullName, "Opulence.csx"); } step.MarkComplete(); } } if (opulenceFilePath == null) { using (var step = output.BeginStep("Trying Current Directory...")) { if (output.Confirm("Use Current Directory as Root?")) { opulenceFilePath = Path.Combine(directory.FullName, "Opulence.csx"); } step.MarkComplete(); } } if (opulenceFilePath == null) { throw new CommandException("Cannot Determine Root Directory."); } using (var step = output.BeginStep("Writing Opulence.csx ...")) { var hostname = output.Prompt("Enter the Container Registry (ex: 'example.azurecr.io' for Azure or 'example' for dockerhub)"); var services = new List <(string, string)>(); if (solutionFilePath != null && output.Confirm($"Use solution file '{solutionFilePath}' to initialize services?")) { services.AddRange(ReadServicesFromSolution(solutionFilePath)); services.Sort((a, b) => a.Item1.CompareTo(b.Item1)); } using var stream = File.OpenWrite(opulenceFilePath); using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); await WriteOpulenceConfigAsync(writer, hostname, services); output.WriteInfoLine($"Initialized Opulence Config at '{opulenceFilePath}'."); step.MarkComplete(); } }
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")}"); }
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)); } } }