Skip to content

Commit

Permalink
Added options to build project resource container images using custom…
Browse files Browse the repository at this point in the history
… Dockerfile instead of 'dotnet publish' (prom3theu5#270)
  • Loading branch information
dombrovsky committed Dec 18, 2024
1 parent 19f5dfb commit 0235213
Show file tree
Hide file tree
Showing 23 changed files with 161 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,35 @@ private AspirateSettings PerformConfigurationBootstrapping()

HandleContainerTag(aspirateConfiguration);

HandleContainerBuildArgs(aspirateConfiguration);

HandleContainerBuildContext(aspirateConfiguration);

HandleTemplateDirectory(aspirateConfiguration);

AddTemplatesToTemplateDirectoryIfRequired(aspirateConfiguration);

return aspirateConfiguration;
}

private void HandleContainerBuildContext(AspirateSettings aspirateConfiguration)
{
if (!string.IsNullOrEmpty(CurrentState.ContainerBuildContext))
{
aspirateConfiguration.ContainerSettings.Context = CurrentState.ContainerBuildContext;
Logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done:[/] Set [blue]'Container Build Context'[/] to [blue]'{aspirateConfiguration.ContainerSettings.Context}'[/].");
}
}

private void HandleContainerBuildArgs(AspirateSettings aspirateConfiguration)
{
if (CurrentState.ContainerBuildArgs?.Count > 0)
{
aspirateConfiguration.ContainerSettings.BuildArgs = CurrentState.ContainerBuildArgs;
Logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done:[/] Set [blue]'Container Build Args'[/] to [blue]'{string.Join(' ', aspirateConfiguration.ContainerSettings.BuildArgs)}'[/].");
}
}

private void HandleContainerBuilder(AspirateSettings aspirateConfiguration)
{
if (!string.IsNullOrEmpty(CurrentState.ContainerBuilder))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ public override async Task<bool> ExecuteAsync()
{
ContainerBuilder = CurrentState.ContainerBuilder.ToLower(),
Prefix = CurrentState.ContainerRepositoryPrefix,
}, CurrentState.NonInteractive, CurrentState.RuntimeIdentifier);
Registry = CurrentState.ContainerRegistry,
BuildArgs = CurrentState.ContainerBuildArgs.ToDictionary(arg => arg.Split('=')[0], arg => arg.Split('=')[1]),
BuildContext = CurrentState.ContainerBuildContext,
Tags = CurrentState.ContainerImageTags
}, CurrentState.NonInteractive, CurrentState.RuntimeIdentifier, CurrentState.PreferDockerfile);
}

Logger.MarkupLine("[bold]Building and push completed for all selected project components.[/]");
Expand Down
3 changes: 3 additions & 0 deletions src/Aspirate.Commands/Commands/Build/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ public BuildCommand() : base("build", "Builds and pushes containers")
AddOption(ProjectPathOption.Instance);
AddOption(AspireManifestOption.Instance);
AddOption(ContainerBuilderOption.Instance);
AddOption(ContainerBuildContextOption.Instance);
AddOption(ContainerImageTagOption.Instance);
AddOption(ContainerBuildArgsOption.Instance);
AddOption(PreferDockerfileOption.Instance);
AddOption(ContainerRegistryOption.Instance);
AddOption(ContainerRepositoryPrefixOption.Instance);
AddOption(RuntimeIdentifierOption.Instance);
Expand Down
5 changes: 3 additions & 2 deletions src/Aspirate.Commands/Commands/Build/BuildOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ public sealed class BuildOptions : BaseCommandOptions, IBuildOptions, IContainer
public string? ProjectPath { get; set; }
public string? AspireManifest { get; set; }
public string? ContainerBuilder { get; set; }
public string? ContainerBuildContext { get; set; }
public string? ContainerRegistry { get; set; }

public List<string>? ContainerBuildArgs { get; set; }
public string? ContainerRepositoryPrefix { get; set; }

public List<string>? ContainerImageTags { get; set; }
public string? RuntimeIdentifier { get; set; }
public List<string>? ComposeBuilds { get; set; }
public bool PreferDockerfile { get; set; }
}
3 changes: 3 additions & 0 deletions src/Aspirate.Commands/Commands/Generate/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ public GenerateCommand() : base("generate", "Builds, pushes containers, generate
AddOption(SkipBuildOption.Instance);
AddOption(SkipFinalKustomizeGenerationOption.Instance);
AddOption(ContainerBuilderOption.Instance);
AddOption(ContainerBuildContextOption.Instance);
AddOption(ContainerImageTagOption.Instance);
AddOption(ContainerBuildArgsOption.Instance);
AddOption(PreferDockerfileOption.Instance);
AddOption(ContainerRegistryOption.Instance);
AddOption(ContainerRepositoryPrefixOption.Instance);
AddOption(ImagePullPolicyOption.Instance);
Expand Down
3 changes: 3 additions & 0 deletions src/Aspirate.Commands/Commands/Generate/GenerateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ public sealed class GenerateOptions : BaseCommandOptions,
public bool? SkipBuild { get; set; }
public bool? SkipFinalKustomizeGeneration { get; set; }
public string? ContainerBuilder { get; set; }
public List<string>? ContainerBuildArgs { get; set; }
public string? ContainerBuildContext { get; set; }
public string? ContainerRegistry { get; set; }
public string? ContainerRepositoryPrefix { get; set; }
public List<string>? ContainerImageTags { get; set; }
public string? ImagePullPolicy { get; set; }
public string? OutputFormat { get; set; }
public string? RuntimeIdentifier { get; set; }
public List<string>? ComposeBuilds { get; set; }
public bool PreferDockerfile { get; set; }
public string? PrivateRegistryUrl { get; set; }
public string? PrivateRegistryUsername { get; set; }
public string? PrivateRegistryPassword { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions src/Aspirate.Commands/Commands/Init/InitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public InitCommand() : base("init", "Initializes aspirate settings within your A
{
AddOption(ProjectPathOption.Instance);
AddOption(ContainerBuilderOption.Instance);
AddOption(ContainerBuildArgsOption.Instance);
AddOption(ContainerBuildContextOption.Instance);
AddOption(ContainerRegistryOption.Instance);
AddOption(ContainerRepositoryPrefixOption.Instance);
AddOption(ContainerImageTagOption.Instance);
Expand Down
2 changes: 2 additions & 0 deletions src/Aspirate.Commands/Commands/Init/InitOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ public sealed class InitOptions : BaseCommandOptions, IInitOptions, IContainerOp
public string? ProjectPath { get; set; }

public string? ContainerBuilder { get; set; }
public string? ContainerBuildContext { get; set; }

public string? ContainerRegistry { get; set; }

public string? ContainerRepositoryPrefix { get; set; }

public List<string>? ContainerImageTags { get; set; }
public List<string>? ContainerBuildArgs { get; set; }
public string? TemplatePath { get; set; }
}
3 changes: 3 additions & 0 deletions src/Aspirate.Commands/Commands/Run/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ public RunCommand() : base("run", "Builds, pushes containers, and runs the curre
AddOption(AspireManifestOption.Instance);
AddOption(SkipBuildOption.Instance);
AddOption(ContainerBuilderOption.Instance);
AddOption(ContainerBuildContextOption.Instance);
AddOption(ContainerImageTagOption.Instance);
AddOption(ContainerBuildArgsOption.Instance);
AddOption(PreferDockerfileOption.Instance);
AddOption(ContainerRegistryOption.Instance);
AddOption(ContainerRepositoryPrefixOption.Instance);
AddOption(ImagePullPolicyOption.Instance);
Expand Down
2 changes: 2 additions & 0 deletions src/Aspirate.Commands/Commands/Run/RunOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public sealed class RunOptions : BaseCommandOptions,
public string? Namespace { get; set; }
public bool? SkipBuild { get; set; }
public string? ContainerBuilder { get; set; }
public List<string>? ContainerBuildArgs { get; set; }
public string? ContainerBuildContext { get; set; }
public string? ContainerRegistry { get; set; }
public string? ContainerRepositoryPrefix { get; set; }
public List<string>? ContainerImageTags { get; set; }
Expand Down
20 changes: 20 additions & 0 deletions src/Aspirate.Commands/Options/ContainerBuildArgsOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Aspirate.Commands.Options;

public sealed class ContainerBuildArgsOption : BaseOption<List<string>?>
{
private static readonly string[] _aliases =
[
"-cba",
"--container-build-arg"
];

private ContainerBuildArgsOption() : base(_aliases, "ASPIRATE_CONTAINER_BUILD_ARGS", null)
{
Name = nameof(IContainerOptions.ContainerBuildArgs);
Description = "The Container Build Arguments to use for all containers. In \"key\"=\"value\" format. Can include multiple times.";
Arity = ArgumentArity.ZeroOrMore;
IsRequired = false;
}

public static ContainerBuildArgsOption Instance { get; } = new();
}
20 changes: 20 additions & 0 deletions src/Aspirate.Commands/Options/ContainerBuildContextOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Aspirate.Commands.Options;

public sealed class ContainerBuildContextOption : BaseOption<string?>
{
private static readonly string[] _aliases =
[
"-cbc",
"--container-build-context"
];

private ContainerBuildContextOption() : base(_aliases, "ASPIRATE_CONTAINER_BUILD_CONTEXT", null)
{
Name = nameof(IContainerOptions.ContainerBuildContext);
Description = "The Container Build Context to use when Dockerfile is used to build projects";
Arity = ArgumentArity.ExactlyOne;
IsRequired = false;
}

public static ContainerBuildContextOption Instance { get; } = new();
}
16 changes: 16 additions & 0 deletions src/Aspirate.Commands/Options/PreferDockerfileOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Aspirate.Commands.Options;

public sealed class PreferDockerfileOption : BaseOption<bool?>
{
private static readonly string[] _aliases = ["--prefer-dockerfile"];

private PreferDockerfileOption() : base(_aliases, "ASPIRATE_PREFER_DOCKERFILE", null)
{
Name = nameof(IBuildOptions.PreferDockerfile);
Description = "Instructs to use Dockerfile when available to build project images";
Arity = ArgumentArity.ZeroOrOne;
IsRequired = false;
}

public static PreferDockerfileOption Instance { get; } = new();
}
28 changes: 26 additions & 2 deletions src/Aspirate.Processors/Resources/Project/ProjectProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics;

namespace Aspirate.Processors.Resources.Project;

/// <summary>
Expand Down Expand Up @@ -68,7 +70,7 @@ private KubernetesDeploymentData PopulateKubernetesDeploymentData(BaseKubernetes
.SetWithPrivateRegistry(options.WithPrivateRegistry.GetValueOrDefault())
.Validate();

public async Task BuildAndPushProjectContainer(KeyValuePair<string, Resource> resource, ContainerOptions options, bool nonInteractive, string? runtimeIdentifier)
public async Task BuildAndPushProjectContainer(KeyValuePair<string, Resource> resource, ContainerOptions options, bool nonInteractive, string? runtimeIdentifier, bool preferDockerfile)
{
var project = resource.Value as ProjectResource;

Expand All @@ -77,7 +79,29 @@ public async Task BuildAndPushProjectContainer(KeyValuePair<string, Resource> re
throw new InvalidOperationException($"Container details for project {resource.Key} not found.");
}

await containerCompositionService.BuildAndPushContainerForProject(project, containerDetails, options, nonInteractive, runtimeIdentifier);
var dockerfileFile = !string.IsNullOrEmpty(containerDetails.DockerfileFile) ? containerDetails.DockerfileFile : Path.Combine(Path.GetDirectoryName(project.Path), "Dockerfile");

if (preferDockerfile && File.Exists(dockerfileFile))
{
_console.MarkupLine($"[bold yellow]Using custom Dockerfile to build project {resource.Key}.[/]");

var dockerfileResource = new DockerfileResource()
{
Path = dockerfileFile,
Context = !string.IsNullOrEmpty(containerDetails.DockerfileContext) ? containerDetails.DockerfileContext : options.BuildContext,
Annotations = project.Annotations,
Bindings = project.Bindings,
Env = project.Env,
Name = project.Name,
BuildArgs = options.BuildArgs
};

await containerCompositionService.BuildAndPushContainerForDockerfile(dockerfileResource, options, nonInteractive);
}
else
{
await containerCompositionService.BuildAndPushContainerForProject(project, containerDetails, options, nonInteractive, runtimeIdentifier);
}

_console.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done: [/] Building and Pushing container for project [blue]{resource.Key}[/]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ public async Task<MsBuildContainerProperties> GetContainerDetails(
ContainerBuilderLiterals.ContainerRegistry,
ContainerBuilderLiterals.ContainerRepository,
ContainerBuilderLiterals.ContainerImageName,
ContainerBuilderLiterals.ContainerImageTag);
ContainerBuilderLiterals.ContainerImageTag,
ContainerBuilderLiterals.DockerfileFile,
ContainerBuilderLiterals.DockerfileContext);

var msBuildProperties = JsonSerializer.Deserialize<MsBuildProperties<MsBuildContainerProperties>>(containerPropertiesJson ?? "{}");

Expand Down
2 changes: 2 additions & 0 deletions src/Aspirate.Shared/Inputs/ContainerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ namespace Aspirate.Shared.Inputs;
public class ContainerOptions
{
public string ContainerBuilder { get; set; } = default!;
public Dictionary<string, string>? BuildArgs { get; set; }
public string? BuildContext { get; set; }
public string ImageName { get; set; } = default!;
public string Registry { get; set; } = default!;
public string? Prefix { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public interface IBuildOptions
{
string? RuntimeIdentifier { get; set; }
List<string>? ComposeBuilds { get; set; }
bool PreferDockerfile { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ namespace Aspirate.Shared.Interfaces.Commands.Contracts;
public interface IContainerOptions
{
string? ContainerBuilder { get; set; }

string? ContainerBuildContext { get; set; }
string? ContainerRegistry { get; set; }
string? ContainerRepositoryPrefix { get; set; }
List<string>? ContainerImageTags { get; set; }
List<string>? ContainerBuildArgs { get; set; }
}
2 changes: 2 additions & 0 deletions src/Aspirate.Shared/Literals/ContainerBuilderLiterals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ public static class ContainerBuilderLiterals
public const string ContainerRepository = "ContainerRepository";
public const string ContainerImageName = "ContainerImageName";
public const string ContainerImageTag = "ContainerImageTag";
public const string DockerfileFile = "DockerfileFile";
public const string DockerfileContext = "DockerfileContext";
}
2 changes: 2 additions & 0 deletions src/Aspirate.Shared/Literals/MsBuildPropertiesLiterals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public static class MsBuildPropertiesLiterals
public const string ContainerImageTagArgument = "ContainerImageTag";
public const string ContainerImageTagArguments = "ContainerImageTags";
public const string ErrorOnDuplicatePublishOutputFilesArgument = "ErrorOnDuplicatePublishOutputFiles";
public const string DockerfileFileArgument = "DockerfileFile";
public const string DockerfileContextArgument = "DockerfileContext";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public class AspirateContainerSettings
public string? RepositoryPrefix { get; set; }
public List<string>? Tags { get; set; }
public string? Builder { get; set; }
public List<string>? BuildArgs { get; set; }
public string? Context { get; set; }
}
11 changes: 11 additions & 0 deletions src/Aspirate.Shared/Models/Aspirate/AspirateState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class AspirateState :
[JsonPropertyName("namespace")]
public string? Namespace { get; set; }

[RestorableStateProperty]
[JsonPropertyName("containerBuildContext")]
public string? ContainerBuildContext { get; set; }

[RestorableStateProperty]
[JsonPropertyName("containerRegistry")]
public string? ContainerRegistry { get; set; }
Expand All @@ -45,6 +49,10 @@ public class AspirateState :
[JsonPropertyName("containerImageTags")]
public List<string>? ContainerImageTags { get; set; } = ["latest"];

[RestorableStateProperty]
[JsonPropertyName("containerBuildArgs")]
public List<string>? ContainerBuildArgs { get; set; }

[RestorableStateProperty]
[JsonPropertyName("runtimeIdentifier")]
public string? RuntimeIdentifier { get; set; }
Expand Down Expand Up @@ -128,6 +136,9 @@ public class AspirateState :
[JsonIgnore]
public bool? SkipBuild { get; set; }

[JsonIgnore]
public bool PreferDockerfile { get; set; }

[JsonIgnore]
public string? AspireManifest { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public sealed class MsBuildContainerProperties : BaseMsBuildProperties
[JsonPropertyName(MsBuildPropertiesLiterals.ContainerImageTagArgument)]
public string? ContainerImageTag { get; set; }

[JsonPropertyName(MsBuildPropertiesLiterals.DockerfileFileArgument)]
public string? DockerfileFile { get; set; }

[JsonPropertyName(MsBuildPropertiesLiterals.DockerfileContextArgument)]
public string? DockerfileContext { get; set; }

[JsonIgnore]
public string? FullContainerImage { get; set; }
}

0 comments on commit 0235213

Please sign in to comment.