From 0159d5720527a854210b3c466d64e7bd53cbb00b Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Sat, 25 Apr 2020 12:33:07 -0700 Subject: [PATCH] Add application step and use it --- src/Microsoft.Tye.Core/ApplicationExecutor.cs | 18 ++++++ ...DeployApplicationKubernetesManifestStep.cs | 64 +++++++++++++++++++ ...nerateApplicationKubernetesManifestStep.cs | 31 +++++++++ src/tye/GenerateHost.cs | 26 ++------ src/tye/Program.DeployCommand.cs | 58 ++--------------- 5 files changed, 125 insertions(+), 72 deletions(-) create mode 100644 src/Microsoft.Tye.Core/DeployApplicationKubernetesManifestStep.cs create mode 100644 src/Microsoft.Tye.Core/GenerateApplicationKubernetesManifestStep.cs diff --git a/src/Microsoft.Tye.Core/ApplicationExecutor.cs b/src/Microsoft.Tye.Core/ApplicationExecutor.cs index 71380338..429e8da8 100644 --- a/src/Microsoft.Tye.Core/ApplicationExecutor.cs +++ b/src/Microsoft.Tye.Core/ApplicationExecutor.cs @@ -19,6 +19,8 @@ namespace Microsoft.Tye this.output = output; } + public List ApplicationSteps = new List(); + public List ServiceSteps { get; } = new List(); public async Task ExecuteAsync(ApplicationBuilder application) @@ -34,6 +36,22 @@ namespace Microsoft.Tye } tracker.MarkComplete(); } + + { + foreach (var step in ApplicationSteps) + { + using var stepTracker = output.BeginStep(step.DisplayText); + await step.ExecuteAsync(output, application); + stepTracker.MarkComplete(); + } + } + } + + public abstract class ApplicationStep + { + public abstract string DisplayText { get; } + + public abstract Task ExecuteAsync(OutputContext output, ApplicationBuilder application); } public abstract class ServiceStep diff --git a/src/Microsoft.Tye.Core/DeployApplicationKubernetesManifestStep.cs b/src/Microsoft.Tye.Core/DeployApplicationKubernetesManifestStep.cs new file mode 100644 index 00000000..eea8c262 --- /dev/null +++ b/src/Microsoft.Tye.Core/DeployApplicationKubernetesManifestStep.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.CommandLine.Invocation; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Tye +{ + public sealed class DeployApplicationKubernetesManifestStep : ApplicationExecutor.ApplicationStep + { + public override string DisplayText => "Deploying Application Manifests..."; + + public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application) + { + using var step = output.BeginStep(""); + + if (!await KubectlDetector.Instance.IsKubectlInstalled.Value) + { + throw new CommandException($"Cannot apply manifests because kubectl is not installed."); + } + + if (!await KubectlDetector.Instance.IsKubectlConnectedToCluster.Value) + { + throw new CommandException($"Cannot apply manifests for because kubectl is not connected to a cluster."); + } + + using var tempFile = TempFile.Create(); + output.WriteInfoLine($"Writing output to '{tempFile.FilePath}'."); + + { + await using var stream = File.OpenWrite(tempFile.FilePath); + await using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); + + await ApplicationYamlWriter.WriteAsync(output, writer, application); + } + + var ns = $"namespace ${application.Namespace}"; + if (string.IsNullOrEmpty(application.Namespace)) + { + ns = "current namespace"; + } + output.WriteDebugLine($"Running 'kubectl apply' in ${ns}"); + 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 '{application.Name}'."); + } + } +} diff --git a/src/Microsoft.Tye.Core/GenerateApplicationKubernetesManifestStep.cs b/src/Microsoft.Tye.Core/GenerateApplicationKubernetesManifestStep.cs new file mode 100644 index 00000000..77f6705d --- /dev/null +++ b/src/Microsoft.Tye.Core/GenerateApplicationKubernetesManifestStep.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Tye +{ + public sealed class GenerateApplicationKubernetesManifestStep : ApplicationExecutor.ApplicationStep + { + public override string DisplayText => "Generating Application Manifests..."; + + public string Environment { get; set; } = "production"; + + public override async Task ExecuteAsync(OutputContext output, ApplicationBuilder application) + { + var outputFilePath = Path.GetFullPath(Path.Combine(application.Source.DirectoryName, $"{application.Name}-generate-{Environment}.yaml")); + output.WriteInfoLine($"Writing output to '{outputFilePath}'."); + { + File.Delete(outputFilePath); + + await using var stream = File.OpenWrite(outputFilePath); + await using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); + + await ApplicationYamlWriter.WriteAsync(output, writer, application); + } + } + } +} diff --git a/src/tye/GenerateHost.cs b/src/tye/GenerateHost.cs index 9f949fa5..4d317716 100644 --- a/src/tye/GenerateHost.cs +++ b/src/tye/GenerateHost.cs @@ -45,28 +45,14 @@ namespace Microsoft.Tye new BuildDockerImageStep() { Environment = environment, }, // Make an image but don't push it new GenerateKubernetesManifestStep() { Environment = environment, }, }, - }; - await executor.ExecuteAsync(application); - - await GenerateApplicationManifestAsync(output, application, environment); - } - private static async Task GenerateApplicationManifestAsync(OutputContext output, ApplicationBuilder application, string environment) - { - using var step = output.BeginStep("Generating Application Manifests..."); - - var outputFilePath = Path.GetFullPath(Path.Combine(application.Source.DirectoryName, $"{application.Name}-generate-{environment}.yaml")); - output.WriteInfoLine($"Writing output to '{outputFilePath}'."); - { - File.Delete(outputFilePath); - - await using var stream = File.OpenWrite(outputFilePath); - await using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); - - await ApplicationYamlWriter.WriteAsync(output, writer, application); - } + ApplicationSteps = + { + new GenerateApplicationKubernetesManifestStep() { Environment = environment, }, + } + }; - step.MarkComplete(); + await executor.ExecuteAsync(application); } } } diff --git a/src/tye/Program.DeployCommand.cs b/src/tye/Program.DeployCommand.cs index e78b12d2..5f616c4f 100644 --- a/src/tye/Program.DeployCommand.cs +++ b/src/tye/Program.DeployCommand.cs @@ -83,61 +83,15 @@ namespace Microsoft.Tye new PushDockerImageStep() { Environment = environment, }, new ValidateSecretStep() { Environment = environment, Interactive = interactive, Force = force, }, new GenerateKubernetesManifestStep() { Environment = environment, }, + }, + + ApplicationSteps = + { + new DeployApplicationKubernetesManifestStep(), } }; - await executor.ExecuteAsync(application); - - await DeployApplicationManifestAsync(output, application, application.Source.Directory.Name); - } - - private static async Task DeployApplicationManifestAsync(OutputContext output, ApplicationBuilder application, string applicationName) - { - using var step = output.BeginStep("Deploying Application Manifests..."); - if (!await KubectlDetector.Instance.IsKubectlInstalled.Value) - { - throw new CommandException($"Cannot apply manifests because kubectl is not installed."); - } - - if (!await KubectlDetector.Instance.IsKubectlConnectedToCluster.Value) - { - throw new CommandException($"Cannot apply manifests for because kubectl is not connected to a cluster."); - } - - using var tempFile = TempFile.Create(); - output.WriteInfoLine($"Writing output to '{tempFile.FilePath}'."); - - { - await using var stream = File.OpenWrite(tempFile.FilePath); - await using var writer = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true); - - await ApplicationYamlWriter.WriteAsync(output, writer, application); - } - - string ns = $"namespace ${application.Namespace}"; - if (String.IsNullOrEmpty(application.Namespace)) - { - ns = "current namespace"; - } - output.WriteDebugLine($"Running 'kubectl apply' in ${ns}"); - 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(); + await executor.ExecuteAsync(application); } internal static void ApplyRegistry(OutputContext output, ApplicationBuilder application, bool interactive, bool requireRegistry)