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
{
internal abstract class Application
public abstract class Application
{
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)
{
@ -63,7 +63,7 @@ namespace Opulence
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)
{

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

@ -6,7 +6,7 @@ using Microsoft.Build.Construction;
namespace Opulence
{
internal static class ApplicationFactory
public static class ApplicationFactory
{
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
{
internal sealed class ApplicationYamlWriter
public sealed class ApplicationYamlWriter
{
public static Task WriteAsync(OutputContext output, StreamWriter writer, Application application)
{

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

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

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

@ -1,13 +1,9 @@
using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using YamlDotNet.RepresentationModel;
namespace Opulence
{
internal sealed class CombineStep : ServiceExecutor.Step
public sealed class CombineStep : ServiceExecutor.Step
{
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.Text;
using System.Threading.Tasks;
using Tye;
namespace Opulence
{
public class DeployCommand
internal static class DeployCommand
{
public static Command Create()
{

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

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

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

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

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

@ -6,7 +6,7 @@ using System.Threading.Tasks;
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)
{

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

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

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

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

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

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

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

@ -7,7 +7,7 @@ using Semver;
namespace Opulence
{
internal static class ProjectReader
public static class ProjectReader
{
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
{
internal sealed class PushDockerImageStep : ServiceExecutor.Step
public sealed class PushDockerImageStep : ServiceExecutor.Step
{
public override string DisplayText => "Pushing Docker Image...";

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

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

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

@ -7,7 +7,7 @@ using System.Linq;
namespace Opulence
{
internal static class StandardOptions
public static class StandardOptions
{
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
{
get

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

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

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

@ -32,6 +32,11 @@
<Content Include="Templates\**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\tye\TempDirectory.cs" Link="TempDirectory.cs" />
<Compile Include="..\..\tye\TempFile.cs" Link="TempFile.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Imports.targets" />
</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(CreateRunCommand(args));
command.AddCommand(CreateDeployCommand());
// Show commandline help unless a subcommand was used.
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.IO;
namespace Opulence
namespace Tye
{
internal class TempDirectory : IDisposable
{

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

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

1
src/tye/tye.csproj

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

Loading…
Cancel
Save