Browse Source

deploy command working (as basic as possible)

pull/23/head
Ryan Nowak 6 years ago
parent
commit
98d7d77a7d
  1. 6
      src/opulence/dotnet-opulence/Application.cs
  2. 2
      src/opulence/dotnet-opulence/ApplicationFactory.cs
  3. 2
      src/opulence/dotnet-opulence/ApplicationYamlWriter.cs
  4. 2
      src/opulence/dotnet-opulence/BuildDockerImageStep.cs
  5. 8
      src/opulence/dotnet-opulence/CombineStep.cs
  6. 3
      src/opulence/dotnet-opulence/DeployCommand.cs
  7. 3
      src/opulence/dotnet-opulence/DeployServiceYamlStep.cs
  8. 1
      src/opulence/dotnet-opulence/DockerContainerBuilder.cs
  9. 2
      src/opulence/dotnet-opulence/DockerfileGenerator.cs
  10. 2
      src/opulence/dotnet-opulence/GenerateKubernetesManifestStep.cs
  11. 1
      src/opulence/dotnet-opulence/HelmChartBuilder.cs
  12. 2
      src/opulence/dotnet-opulence/OutputContext.cs
  13. 2
      src/opulence/dotnet-opulence/ProjectReader.cs
  14. 2
      src/opulence/dotnet-opulence/PushDockerImageStep.cs
  15. 2
      src/opulence/dotnet-opulence/ServiceExecutor.cs
  16. 13
      src/opulence/dotnet-opulence/StandardOptions.cs
  17. 2
      src/opulence/dotnet-opulence/Verbosity.cs
  18. 5
      src/opulence/dotnet-opulence/dotnet-opulence.csproj
  19. 26
      src/tye/OpulenceApplicationAdapter.cs
  20. 149
      src/tye/Program.DeployCommand.cs
  21. 1
      src/tye/Program.cs
  22. 2
      src/tye/TempDirectory.cs
  23. 2
      src/tye/TempFile.cs
  24. 1
      src/tye/tye.csproj

6
src/opulence/dotnet-opulence/Application.cs

@ -6,7 +6,7 @@ using System.Reflection;
namespace Opulence namespace Opulence
{ {
internal abstract class Application public abstract class Application
{ {
public abstract ApplicationGlobals Globals { get; } public abstract ApplicationGlobals Globals { get; }
@ -25,7 +25,7 @@ namespace Opulence
} }
} }
internal class ServiceEntry public class ServiceEntry
{ {
public ServiceEntry(Service service, string friendlyName, IEnumerable<string>? environments = null) public ServiceEntry(Service service, string friendlyName, IEnumerable<string>? environments = null)
{ {
@ -63,7 +63,7 @@ namespace Opulence
return Environments.Count == 0 || Environments.Contains(environment, StringComparer.OrdinalIgnoreCase); return Environments.Count == 0 || Environments.Contains(environment, StringComparer.OrdinalIgnoreCase);
} }
internal bool IsMatchForProject(Application application, FileInfo projectFile) public bool IsMatchForProject(Application application, FileInfo projectFile)
{ {
if (application is null) if (application is null)
{ {

2
src/opulence/dotnet-opulence/ApplicationFactory.cs

@ -6,7 +6,7 @@ using Microsoft.Build.Construction;
namespace Opulence namespace Opulence
{ {
internal static class ApplicationFactory public static class ApplicationFactory
{ {
public static async Task<Application> CreateApplicationAsync(OutputContext output, FileInfo projectFile) public static async Task<Application> CreateApplicationAsync(OutputContext output, FileInfo projectFile)
{ {

2
src/opulence/dotnet-opulence/ApplicationYamlWriter.cs

@ -6,7 +6,7 @@ using YamlDotNet.RepresentationModel;
namespace Opulence namespace Opulence
{ {
internal sealed class ApplicationYamlWriter public sealed class ApplicationYamlWriter
{ {
public static Task WriteAsync(OutputContext output, StreamWriter writer, Application application) public static Task WriteAsync(OutputContext output, StreamWriter writer, Application application)
{ {

2
src/opulence/dotnet-opulence/BuildDockerImageStep.cs

@ -2,7 +2,7 @@
namespace Opulence namespace Opulence
{ {
internal sealed class BuildDockerImageStep : ServiceExecutor.Step public sealed class BuildDockerImageStep : ServiceExecutor.Step
{ {
public override string DisplayText => "Building Docker Image..."; public override string DisplayText => "Building Docker Image...";

8
src/opulence/dotnet-opulence/CombineStep.cs

@ -1,13 +1,9 @@
using System.CommandLine.Invocation; using System.Linq;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using YamlDotNet.RepresentationModel;
namespace Opulence namespace Opulence
{ {
internal sealed class CombineStep : ServiceExecutor.Step public sealed class CombineStep : ServiceExecutor.Step
{ {
public override string DisplayText => "Compiling Services..."; public override string DisplayText => "Compiling Services...";

3
src/opulence/dotnet-opulence/DeployCommand.cs

@ -5,10 +5,11 @@ using System.CommandLine.Invocation;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Tye;
namespace Opulence namespace Opulence
{ {
public class DeployCommand internal static class DeployCommand
{ {
public static Command Create() public static Command Create()
{ {

3
src/opulence/dotnet-opulence/DeployServiceYamlStep.cs

@ -3,11 +3,12 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Tye;
using YamlDotNet.RepresentationModel; using YamlDotNet.RepresentationModel;
namespace Opulence namespace Opulence
{ {
internal sealed class DeployServiceYamlStep : ServiceExecutor.Step public sealed class DeployServiceYamlStep : ServiceExecutor.Step
{ {
public override string DisplayText => "Deploying Manifests..."; public override string DisplayText => "Deploying Manifests...";

1
src/opulence/dotnet-opulence/DockerContainerBuilder.cs

@ -2,6 +2,7 @@
using System.CommandLine.Invocation; using System.CommandLine.Invocation;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Tye;
namespace Opulence namespace Opulence
{ {

2
src/opulence/dotnet-opulence/DockerfileGenerator.cs

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace Opulence namespace Opulence
{ {
internal static class DockerfileGenerator public static class DockerfileGenerator
{ {
public static async Task WriteDockerfileAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container, string filePath) public static async Task WriteDockerfileAsync(OutputContext output, Application application, ServiceEntry service, Project project, ContainerInfo container, string filePath)
{ {

2
src/opulence/dotnet-opulence/GenerateKubernetesManifestStep.cs

@ -2,7 +2,7 @@
namespace Opulence namespace Opulence
{ {
internal sealed class GenerateKubernetesManifestStep : ServiceExecutor.Step public sealed class GenerateKubernetesManifestStep : ServiceExecutor.Step
{ {
public override string DisplayText => "Generating Manifests..."; public override string DisplayText => "Generating Manifests...";

1
src/opulence/dotnet-opulence/HelmChartBuilder.cs

@ -2,6 +2,7 @@
using System.CommandLine.Invocation; using System.CommandLine.Invocation;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Tye;
namespace Opulence namespace Opulence
{ {

2
src/opulence/dotnet-opulence/OutputContext.cs

@ -5,7 +5,7 @@ using System.CommandLine.IO;
namespace Opulence namespace Opulence
{ {
internal sealed class OutputContext public sealed class OutputContext
{ {
private const int IndentAmount = 4; private const int IndentAmount = 4;

2
src/opulence/dotnet-opulence/ProjectReader.cs

@ -7,7 +7,7 @@ using Semver;
namespace Opulence namespace Opulence
{ {
internal static class ProjectReader public static class ProjectReader
{ {
public static async Task ReadProjectDetailsAsync(OutputContext output, FileInfo projectFile, Project project) public static async Task ReadProjectDetailsAsync(OutputContext output, FileInfo projectFile, Project project)
{ {

2
src/opulence/dotnet-opulence/PushDockerImageStep.cs

@ -3,7 +3,7 @@ using System.Threading.Tasks;
namespace Opulence namespace Opulence
{ {
internal sealed class PushDockerImageStep : ServiceExecutor.Step public sealed class PushDockerImageStep : ServiceExecutor.Step
{ {
public override string DisplayText => "Pushing Docker Image..."; public override string DisplayText => "Pushing Docker Image...";

2
src/opulence/dotnet-opulence/ServiceExecutor.cs

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Opulence namespace Opulence
{ {
internal sealed class ServiceExecutor public sealed class ServiceExecutor
{ {
private readonly OutputContext output; private readonly OutputContext output;
private readonly Application application; private readonly Application application;

13
src/opulence/dotnet-opulence/StandardOptions.cs

@ -7,7 +7,7 @@ using System.Linq;
namespace Opulence namespace Opulence
{ {
internal static class StandardOptions public static class StandardOptions
{ {
private static readonly string[] AllOutputs = new string[] { "container", "chart", }; private static readonly string[] AllOutputs = new string[] { "container", "chart", };
@ -37,6 +37,17 @@ namespace Opulence
} }
} }
public static Option Interactive
{
get
{
return new Option(new[] { "-i", "--interactive", }, "Interactive mode")
{
Argument = new Argument<bool>(),
};
}
}
public static Option Outputs public static Option Outputs
{ {
get get

2
src/opulence/dotnet-opulence/Verbosity.cs

@ -1,6 +1,6 @@
namespace Opulence namespace Opulence
{ {
internal enum Verbosity public enum Verbosity
{ {
Quiet, Quiet,
Info, Info,

5
src/opulence/dotnet-opulence/dotnet-opulence.csproj

@ -32,6 +32,11 @@
<Content Include="Templates\**" CopyToOutputDirectory="PreserveNewest" /> <Content Include="Templates\**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="..\..\tye\TempDirectory.cs" Link="TempDirectory.cs" />
<Compile Include="..\..\tye\TempFile.cs" Link="TempFile.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Resources\Imports.targets" /> <EmbeddedResource Include="Resources\Imports.targets" />
</ItemGroup> </ItemGroup>

26
src/tye/OpulenceApplicationAdapter.cs

@ -0,0 +1,26 @@
using System.Collections.Generic;
using Opulence;
namespace Tye
{
internal class OpulenceApplicationAdapter : Opulence.Application
{
private readonly Micronetes.Hosting.Model.Application application;
public OpulenceApplicationAdapter(
Micronetes.Hosting.Model.Application application,
ApplicationGlobals globals,
IReadOnlyList<ServiceEntry> services)
{
this.application = application;
Globals = globals;
Services = services;
}
public override ApplicationGlobals Globals { get; }
public override string RootDirectory => application.ContextDirectory;
public override IReadOnlyList<ServiceEntry> Services { get; }
}
}

149
src/tye/Program.DeployCommand.cs

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Opulence;
namespace Tye
{
static partial class Program
{
public static Command CreateDeployCommand()
{
var command = new Command("deploy", "Deploy the application");
var argument = new Argument("path")
{
Description = "A solution or project file to generate a yaml manifest from",
Arity = ArgumentArity.ZeroOrOne
};
command.Add(argument);
command.Add(StandardOptions.Interactive);
command.Add(StandardOptions.Verbosity);
command.Handler = CommandHandler.Create<IConsole, string, Verbosity, bool>((console, path, verbosity, interactive) =>
{
var application = ResolveApplication(path);
if (application is null)
{
throw new CommandException($"None of the supported files were found (tye.yaml, .csproj, .fsproj, .sln)");
}
return ExecuteAsync(new OutputContext(console, verbosity), application, environment: "production", interactive);
});
return command;
}
private static async Task ExecuteAsync(OutputContext output, Micronetes.Hosting.Model.Application application, string environment, bool interactive)
{
var globals = new ApplicationGlobals();
var services = new List<Opulence.ServiceEntry>();
foreach (var kvp in application.Services)
{
if (kvp.Value.Description.Project is string projectFile)
{
var project = new Project(projectFile);
var service = new Service(kvp.Key)
{
Source = project,
};
var serviceEntry = new ServiceEntry(service, kvp.Key);
await ProjectReader.ReadProjectDetailsAsync(output, new FileInfo(projectFile), project);
var container = new ContainerInfo();
service.GeneratedAssets.Container = container;
services.Add(serviceEntry);
}
}
var opulenceApplication = new OpulenceApplicationAdapter(application, globals, services);
if (opulenceApplication.Globals.Registry?.Hostname == null && interactive)
{
var registry = output.Prompt("Enter the Container Registry (ex: 'example.azurecr.io' for Azure or 'example' for dockerhub)");
opulenceApplication.Globals.Registry = new ContainerRegistry(registry);
}
else if (opulenceApplication.Globals.Registry?.Hostname == null)
{
throw new CommandException("A registry is required for deploy operations. Add the registry to 'tye.yaml' or use '-i' for interactive mode.");
}
foreach (var service in opulenceApplication.Services)
{
if (service.Service.Source is Project project && service.Service.GeneratedAssets.Container is ContainerInfo container)
{
DockerfileGenerator.ApplyContainerDefaults(opulenceApplication, service, project, container);
}
}
var steps = new List<ServiceExecutor.Step>()
{
new CombineStep() { Environment = environment, },
new BuildDockerImageStep() { Environment = environment, },
new PushDockerImageStep() { Environment = environment, },
};
steps.Add(new GenerateKubernetesManifestStep() { Environment = environment, });
// If this is command is for a project, then deploy the component manifest
// for just the project. We won't run the "application deploy" part.
if (!string.Equals(".csproj", Path.GetExtension(application.Source), StringComparison.Ordinal) &&
!string.Equals(".fsproj", Path.GetExtension(application.Source), StringComparison.Ordinal))
{
steps.Add(new DeployServiceYamlStep() { Environment = environment, });
}
var executor = new ServiceExecutor(output, opulenceApplication, steps);
foreach (var service in opulenceApplication.Services)
{
if (service.IsMatchForProject(opulenceApplication, new FileInfo(application.Source)))
{
await executor.ExecuteAsync(service);
}
}
await PackageApplicationAsync(output, opulenceApplication, Path.GetDirectoryName(application.Source), environment);
}
private static async Task PackageApplicationAsync(OutputContext output, Opulence.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);
await ApplicationYamlWriter.WriteAsync(output, writer, application);
}
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();
}
}
}

1
src/tye/Program.cs

@ -20,6 +20,7 @@ namespace Tye
command.AddCommand(CreateInitCommand()); command.AddCommand(CreateInitCommand());
command.AddCommand(CreateRunCommand(args)); command.AddCommand(CreateRunCommand(args));
command.AddCommand(CreateDeployCommand());
// Show commandline help unless a subcommand was used. // Show commandline help unless a subcommand was used.
command.Handler = CommandHandler.Create<IHelpBuilder>(help => command.Handler = CommandHandler.Create<IHelpBuilder>(help =>

2
src/opulence/dotnet-opulence/TempDirectory.cs → src/tye/TempDirectory.cs

@ -1,7 +1,7 @@
using System; using System;
using System.IO; using System.IO;
namespace Opulence namespace Tye
{ {
internal class TempDirectory : IDisposable internal class TempDirectory : IDisposable
{ {

2
src/opulence/dotnet-opulence/TempFile.cs → src/tye/TempFile.cs

@ -1,7 +1,7 @@
using System; using System;
using System.IO; using System.IO;
namespace Opulence namespace Tye
{ {
internal class TempFile : IDisposable internal class TempFile : IDisposable
{ {

1
src/tye/tye.csproj

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\micronetes\Micronetes.Hosting\Micronetes.Hosting.csproj" /> <ProjectReference Include="..\micronetes\Micronetes.Hosting\Micronetes.Hosting.csproj" />
<ProjectReference Include="..\opulence\dotnet-opulence\dotnet-opulence.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

Loading…
Cancel
Save