Code Monkey home page Code Monkey logo

sdk-container-builds's Introduction

.NET SDK Container Building Tools

This project consists of APIs and MSBuild Tasks for generating and testing an OCI Container from a .NET project.

A basic start with tooling

  • for existing web project (the package is part of `Microsoft.NET.SDK.Web):
dotnet publish --os linux --arch x64 -c Release /p:PublishProfile=DefaultContainer
  • for existing non-web project:
dotnet add package Microsoft.NET.Build.Containers
dotnet publish --os linux --arch x64 -c Release /t:PublishContainer

You can learn more about the project from the project Documentation.

Prerequisites

.NET 7 SDK or later

Contributing

This repo only contains documentation and issues for the project.

The source code was moved to dotnet/sdk repo. For easiness, please use containers.slnf filter in case the intention is only to build and debug containers source code.

If you would like to contribute by creating a pull request, please refer to dotnet/sdk contributing guide. Ideally, prior starting the effort find a corresponding issue in this repo and let us and others know in a comment. If you plan to address the problem that is not reflected in any issue, please create one. Consider helping us triaging the pull request by adding 'Area-Containers' repo.

Development documentation is available here

License

This project is licensed with the MIT license.

.NET Foundation

sdk-container-builds is a .NET Foundation project.

Related Projects

sdk-container-builds's People

Contributors

baronfel avatar benvillalobos avatar danegsta avatar danielku15 avatar dependabot[bot] avatar eerhardt avatar flcdrg avatar ievangelist avatar kusc-leica avatar mango-darren avatar michalpavlik avatar normj avatar rainersigwald avatar richlander avatar rick-anderson avatar timheuer avatar vlada-shubina avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sdk-container-builds's Issues

Create a good, simple README to bundle in the generated package

When folks look this up on NuGet.org for the demo period, I don't want them to flail looking for an entrypoint or usage. We should have a minimal README that can show them how to get started. This README would be visible from nuget.org, though not from VS.

Image name normalization isn't broad enough

It seems like no capital letters can be part of an image name. For the project file FOO+XYZ.csproj, we normalize to fOO-XYZ as the image name, which results in this error when pushing to the docker daemon:

Failed to load image to local Docker daemon. stdout: invalid reference format: repository name must be lowercase

Entrypoint no longer being set in alpha47

Somewhere between alpha40 and 47 the Entrypoint stopped being set - likely my entrypoint refactor PR. We need to fix this.

On my host, the properties and items seem to be parsed correctly:

image

But then only the properties seem to be passed through into the CreateNewImage task:

image

Defaults today expose 80/443

The default docker experience today exposes 80/443

EXPOSE 80
EXPOSE 443

Should we also make those the defaults in this experience?

Enable gzip for layers

#9 creates layers with only tar and no compression, which is silly. The blocking issue should be fixed as of .NET 7 preview 6.

Update image modification time

I've been confused a few times about the image modification time that shows in Docker Desktop being "two weeks ago"--because that's when dotnet/runtime last updated, and we're not changing the timestamps. We should do so on any modification to the manifests.

I think this is ok even when we get to deterministic construction, because manifests are small while we'd really want layers to be cacheable.

Support Image History

The history field of the Image Manifest is used as a log of what commands/who made modifications to the image. It's generally added to on a layer-by-layer basis, and if it is maintained correctly, you get nice displays in outputs like docker image history:

➜ docker history container-app-tres:0.0.6
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
229b57e74fb4   7 seconds ago   dotnet publish -p:PublishProfile=DefaultCont…   8.38MB    A dynamically-generated image layer from the .NET SDK
<missing>      10 days ago     /bin/sh -c #(nop) COPY dir:b0ac707752044c30c…   21.3MB
<missing>      10 days ago     /bin/sh -c #(nop)  ENV ASPNET_VERSION=7.0.0-…   0B
<missing>      10 days ago     /bin/sh -c ln -s /usr/share/dotnet/dotnet /u…   24B
<missing>      10 days ago     /bin/sh -c #(nop) COPY dir:99ddbc873c8fc6285…   72.9MB
<missing>      10 days ago     /bin/sh -c #(nop)  ENV DOTNET_VERSION=7.0.0-…   0B
<missing>      10 days ago     /bin/sh -c #(nop)  ENV ASPNETCORE_URLS=http:…   0B
<missing>      10 days ago     /bin/sh -c apt-get update     && apt-get ins…   37MB
<missing>      2 weeks ago     /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>      2 weeks ago     /bin/sh -c #(nop) ADD file:0eae0dca665c7044b…   80.4MB

and the Docker Desktop image history view:
image

A simple addition to the AddLayer function makes this possible:

var history = new JsonObject();
history["created"] = DateTime.UtcNow;
history["author"] = ".NET SDK";
history["created_by"] = "dotnet publish -p:PublishProfile=DefaultContainer";
history["comment"] = "A dynamically-generated image layer from the .NET SDK";
config["history"]!.AsArray().Add(history);

But I wanted to slow down and think about what data we would want to embed in this structure. The spec has the following to say about it:

image

Should we put package/SDK versions in this? Should the created date be the actual now? I know we've been thinking about determinism in this project, but the history property isn't part of digest calculation.

Task: Good defaults for artifact storage

We shouldn't require much ceremony around specifying a directory to store tarballs and so on; it should Just Work.

Ideally it would be shared across projects so multiple projects building an image on the same base wouldn't redownload it a bajillion times. But that conflicts a bit with MSBuild's model around intermediate files, so we'll have to design a solution.

The solution is probably just "take the defaults from #6".

Programmatically change the environment variables and labels of an image

A user of this library should be able to enforce some conventions or baseline configuration on top of a base image. For example,

  • Javier the platform engineer requires that all .NET projects opt into profile-guided optimization for performance
  • Marla the architect wants to enforce a uniform tagging/labeling scheme on images so that she has better traceability of team usage

These tasks should be easy, along the lines of

var baseImage = new ImageReference("mcr.microsoft.com", "dotnet/aspnet", "6.0"); // image from a registry, with a name and a tag
var myImage = new Image("contoso-dotnet-pgo", baseImage); // derive my image from the baseImage

//enable dynamic PGO and set some other variables for maximum speed
myImage.Environment["DOTNET_TieredPGO"] = 1;
myImage.Environment["DOTNET_ReadyToRun"] = 0;
myImage.Environment["DOTNET_TC_QuickJitForLoops"] = 1;

// enforce my labeling convention
myImage.Labels.Add("contoso:business-area", "research-and-development");

myImage.Write("registry.contoso.com", "6.0"); // keep the tags from the upstream because we're tracking them.

Expose ContainerPort via API and in task

yeah - these ContainerPort items will eventually need to be mapped to the container config. this will make naieve docker run commands Just Work. Without it, users would need to add --port EXTERNAL:INTERNAL entries for each port manually at run-time.

Originally posted by @baronfel in #54 (comment)

Determinism for layers

The OCI specs say that layers should preserve timestamps, but I expect to (optionally?) violate that, because we will want to produce bit-for-bit identical output given identical repo builds, just as the Roslyn compiler does. That will require using tar APIs to set metadata on files individually to some known value.

Automation/tooling support for building a local Dockerfile as a 'base image'

One of our escape hatches for users that have a need to customize their base image with RUN commands (or any other functionality that we can't support) is to put just those customizations in a Dockerfile and then build and use that. It would be nice if we had a smooth, documented pathway to making that happen in the easy cases.

We might need to support:

  • invoking a build of a Dockerfile
  • in a given Docker build context,
  • with Build Args and
  • a tagged image name

in a seamless manner.

Bundle standalone executable into a `scratch` image from code

Say I have a native executable (could be a .NET 7 Runtime-specific, self-contained, single-file application, or anything really) that I want to containerize. I want to be able to easily containerize this application via the image manipulation API (this is not set in stone but should be an indication of the kinds of things we want to enable):

var binaryName = "mycoolapp";

var exe = new FileInfo($"/path/to/{binaryName}");
var baseImage = new ImageReference(name: "scratch"); // defining a base image by name only
var image = new Image(binaryName, baseImage) { 
  new Layer(exe) // add layers via the `Add(Layer l)` member for easy construction
};

image.Write("registry.contoso.com", "1.0"); // tagging is an aspect of writing

This would:

  • create the layers and digests
    • defaulting to a /app working directory as the root path for copied files and folder heirarchies
  • set the image base to "scratch", importing layers and metadata from that image when the new image is written
  • set the working directory of the generated image to /app to match the inferred working directory from the layers
  • define a user and group based off of the name of the application
  • set the entrypoint to the executable

Design and implement authentication to registries

Currently we support pushing images to unauthenticated registries and the local Docker daemon. We need to be able to push to authenticated registries of all kinds.

There are two major standards for authenticating against registries that we should support:

  • HTTP Basic Auth
  • HTTP Bearer Token auth

Both of these credentials can be retrieved from a file on the users system, whose location and format is described in detail here. The file contains three relevant structures:

  • an auths mapping of domain name (optionally namespace/user/image sections as well) to credentials
    • these credentials are username:password values in base64 encoding
  • a credHelpers mapping of domain name (optionally namespace/user/image sections as well) to the name of an executable to invoke
    • these executables are invoked with the get command, and then the target url (as defined in the key of the mapping) is written to stdin. stdout will either contain an error message or the json representation of the credential struct
  • a credStore value describing the application to invoke
    • these applications follow the same protocol as the credHelpers above, but use the system keychain generally and apply to all hosts

In all cases, if the username of the credential is <token>, the secret portion of the credential is to be treated as a Bearer token instead of the overall transaction being HTTP Basic Auth.

The above will allow us to seamlessly integrate with existing container applications and flows. In addition, we should also support four new MSBuild properties to allow for arbitrary authentication, though we should document these as a) being insecure, and b) less desired than the more 'standard' auth flows described above. The proposed properties are

  • BaseRegistryUsername
  • BaseRegistryPassword
  • BaseRegistryToken
  • TargetRegistryUsername
  • TargetRegistryPassword
  • TargetRegistryToken

For each triplet of properties (Base vs Target), the Username and Password properties are mutually exclusive to the Token property. This makes it very clear whether Basic auth or Bearer auth will be used.

We should provide a well-known target that third parties can use as a place to perform authentication. This is commonly required by cloud providers, for example, who might use a scheme of long-lived refresh tokens and short-lived access tokens to create a temporary authorization to push to a specific registry. The name of this target (and the property to add dependencies to in order to hook into it) should be documented and remain stable.

We need to have very explicit handling around authentication failures for registry calls - users need a specific error code they can look up and have pre-written docs for.

Users can easily containerize their applications in a CI pipeline

Users should be able to take a standard .NET application and seamlessly containerize it, publishing directly from the .NET CLI. The entire experience should be as easy as

> dotnet new web -n my-awesome-app
# Publish the my-awesome-app:1.2.3 image containing the runtime assets of the application to a local Docker daemon.
# Because this is a web application on .NET 7, the mcr.microsoft.com/dotnet/aspnet:7.0 image was used as the base.
# The ASPNETCORE_URLS variable was set to http://localhost:5000, and the image shows a port exposure of 5000.
> dotnet publish --profile container -p:Version="1.2.3"

Users should be able to rely on sane defaults for the containers generated from this mechanism, but also be able to 'eject' at any time and take over those defaults.

Users should be able to customize key container properties, including but not limited to:

  • base image and tags (optionally specifying digests directly for reproducibility)
  • environment variables
  • container labels
  • exposed ports
  • entrypoint/starting command definitions
  • the resulting image name, version, and tag(s)
  • the destination registry for the image
  • container group and user names
  • working directory
  • and more!

There should be smart defaults for common scenarios - for example, ASP.NET Core applications should specify the default listen urls via the ASPNETCORE_URLS environment variable based on the default values, and the matching ports should be exposed by default. Similarly, we should use bare OS containers with publishing with --self-contained set. More conventions along this line of thinking may be possible.

Users should be able to create and push the generated images for any container registry, which generally fall into two camps: local Docker daemon registries and remote, authenticated-access-required registries like Azure Container Registry, AWS Container Registry, Docker Hub, and many others.

The mechanism that we will use to implement this is a Publish Profile. While classically an ASP.NET-focused concept, the mechanism is general enough for us to use here. A Publish Profile is a named fragment of MSBuild logic (typically just Properties) that identifies a WebPublishMethod and other Properties that are only relevant to that publish mechanism. They are a pluggable mechanism, but most user interaction is via the PublishProfile MSBuild property, which takes a file name (for example PushToACR), extrapolates that to a file at <Project Root>/Properties/PublishProfiles/<Profile Name>.pubxml, and Imports that file. A later mechanism uses the value of the WebPublishMethod property to dynamically load targets bundled in the SDK for the given Publish Method.

As a part of this effort, we will

  • Define a new WebPublishMethod called Container
  • Define a default PublishProfile in the SDK for containerization that sets this property, as well as any other defaults we decide on
  • Provide targets for the Container publish method in the SDK that provide an 'interface' for this project to plug into. If you look at the pre-existing Docker targets, they define a few targets that are expected to be present. If we do the same then we can 'just' supply a tasks/targets package from this repository for iteration purposes, before needing to fully integrate into the SDK
  • Implement these targets here. I think we will need two targets:
    • One target (something like "ComputeContainerImageProperties") that represents the point at which all of the container-relevant metadata has been inferred. This is an external integration point for IDE tools that want to do Container-y things. We should sync with the VS Web Tools and Container Tools teams to make sure this is usable for their needs. This should be design-time friendly.
    • One target (something like "CreateAndPublishContainerImage") that uses the metadata from the previous target to generate and publish the container image to the desired registry.

Protect Users from themselves WRT ContainerEntryPoint

it'll be important to document that the Entrypoint needs to be a fully-rooted path. I could see users doing something like

<ContainerEntrypoint>$(AppName)</ContainerEntrypoint>

and attempting to package up an executable at what is effectively /$(AppName), which won't exist.

Maybe there's some validation we can do here to save them: either checking that a fully-rooted path was provided, or attempting to 'root' a relative or un-rooted path against the ContainerWorkingDirectory.

Originally posted by @baronfel in #38 (comment)

Provide a way to force pulling manifests and layers even if they are already cached

docker build has a --pull flag that forces reacquisition of manifests and layers - we should enable this behavior as well. The content store is fine for the default case but sometimes you really need a clean analogue.

My initial thoughts are twofold:

  • we should support a property that tells the CreateNewImage task to ignore the content store
  • we should add a new target specifically for clearing the content store

and document both of these.

Infer container image properties from Project metadata

Users should be able to easily set the key aspects of image configuration from their project file. There should be a clear integration point at which those values are 'fixed' and can no longer be changed, and there should be reasonable inference for these properties where it makes sense. Where relevant, we should rely on properties that are already conventionally available - packaging information from NuGet and source control information from SourceLink are good examples of these. That lets us make use of documentation and examples from those projects to lessen the number of new concepts here.

Image Metadata Required* Element Type Element Name Data Type Inference algorithm Example
Base Image Name Property ContainerBaseImageName string Based on the ProjectCapabilities of the project - if AspNetCore is present, prefer the dotnet/aspnet base image, otherwise prefer the dotnet/runtime image mcr.microsoft.com/dotnet/aspnet
Base Image Tag Property ContainerBaseImageTag string Based on the TargetFrameworkVersion of the project - net6.0 maps to the 6.0 tag, for example 6.0
Base Image Property ContainerBaseImage string If this is specified, it completely overrides ContainerBaseImageName and ContainerBaseImage. Otherwise, it is the result of concatenating them together, separated by :. mcr.microsoft.com/dotnet/aspnet:6.0
Image Name Property ContainerImageName string The value of the $(AssemblyName) property, lowercased so that it matches image name requirements weatherapp
Image Tag Property ContainerImageTag string The value of the Version property. This one is a bit loose, would folks prefer multiple tags here? 1.0.0
Working Directory Property ContainerWorkingDirectory string Defaults to /app. This directory will be the 'root' of the project's deployed files. /app
Entrypoint Item? ContainerEntrypoint string[] If the project is self-contained, the entrypoint defaults to the name of the generated apphost. Otherwise, for a framework-dependent project, the entrypoint defaults to dotnet <path to project's output.dll> dotnet weatherapp.dll. The path to the apphost or output dll will change based on the ContainerWorkingDirectory
Cmd Item? ContainerCommand string[] No inference. If specified, these arguments will be supplied to the container's entrypoint -
Ports Item ContainerPort <ContainerPort Include="<port number>" Type="tcp, udp" /> If the project has the AspNetCore capability, port entries for TCP 5000 and TCP 5001 will be created <ContainerPort Include="5000" Type="tcp"/><ContainerPort Include="5001" Type="tcp"/>
Environment Variables Item ContainerEnvironmentVariable <ContainerEnvironmentVariable Include="ASPNETCORE_URLS" Value="http://localhost:5000" /> If the project has the AspNetCore capability, the ASPNETCORE_URLS variable will be set to http://localhost:5000;https://localhost:5001
Labels Item ContainerLabel string key, string value We may set some of the well known labels, see below. <ContainerLabel Include="org.opencontainers.image.created" Value="2022-06-07T12:10:12Z" />
User Property ContainerUser string No default
Group Property ContainerGroup string No default, if specified the User must be specified as well

NOTE
Required in this case means required to have a working container image, not that the user is required to provide a value for the item. We will do inference to fill in all required items, erroring on validation if necessary.

Labels

There are some well-known labels that we should consider setting by default:

Annotation Key Derived from
org.opencontainers.image.created $([System.DateTime]::UtcNow.ToString("o"))
org.opencontainers.image.authors $(Authors)
org.opencontainers.image.url $(RepositoryUrl) if present.
org.opencontainers.image.documentation Since $(PackageLicenseUrl) is deprecated, we may just have to default to $(RepositoryUrl). Users should be able to override this easily though.
org.opencontainers.image.source $(RepositoryUrl) if present.
org.opencontainers.image.version $(Version) if present. Note that there's not really a consistent place to hook into where we can be assured of a version being set, so we'll want to be sometime directly before Build, but as late as possible otherwise.
org.opencontainers.image.vendor There's no $(PackageOwners) or $(Owners) to bootstrap off of, but maybe we should make one?
org.opencontainers.image.licenses $(PackageLicenseExpression)
org.opencontainers.image.ref.name $(RepositoryBranch) or $(RepositoryCommit). There's not a baked-in for git repo tags in SourceLink, which would be ideal. Commit is probably the best one.
org.opencontainers.image.title $(Title)
org.opencontainers.image.description $(Description)
org.opencontainers.image.base.digest The resolved digest of $(ContainerBaseImage)
org.opencontainers.image.base.name $(ContainerBaseImage)

Example project file

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net7.0</TargetFramework>
        <OutputType>Exe</OutputType>
        <Version>1.2.3</Version>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Dotnet.ReproducibleBuilds"  Version="1.1.1" PrivateAssets="all" />
    </ItemGroup>

    <ItemGroup>
        <ContainerPort Include="1234" Type="udp" />
        <ContainerEnvironmentVariable Include="LOG_LEVEL" Value="trace" />
        <ContainerLabel Include="org.contoso.businessunit" Value="C+AI" />
    </ItemGroup>
</Project>

Work Items

Basic container image scenario

From the basic containerization docs:

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

We'll be handling the build and publish parts outside of Docker so the relevant part is

FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

To get this we'll need

  • Registry lookup from tag
  • Create new image from base image
    - [x] Set WORKDIR
    - [x] #3
    - [x] Set ENTRYPOINT
  • Save image + assets
  • Make local Docker aware of just-created image (maybe?)

Publish to local Docker

While we want things to work if you don't have Docker installed, as an inner dev loop scenario folks will surely want "make it super easy to run the thing I just built", so we should have a "publish to local Docker" option.

The only way I currently see to do this is via docker load, which takes as input a tarball in the format produced by docker export, which doesn't appear to be documented but is basically hash-named objects in a tarball.

Jib has this and mentions that it requires installed Docker, so I suspect they're doing the same thing.

Questions:

  • What happens if the tarball doesn't have the the base layer tarballs inside it? Do we have to download them so we can produce the tarball?
  • Is there any other way to do this?
  • Should we expose this tarball as an artifact, or create it transiently in memory and stream it to docker load?

Whiteout file behavior in layer addition

Consider a trivial dockerfile:

FROM mcr.microsoft.com/dotnet/runtime:6.0

COPY TestFile.txt /app/

The layer Docker generates applies an opaque whiteout for the whole app/ directory:

tar -tf "S:\play\dockercopything\36898d648167f1ac26bc899aa1586cbebb25376d996882f60d8d21b95d0d2ef2\layer.tar"
app/
app/.wh..wh..opq
app/TestFile.txt

So we should probably do that too.

Design multi-manifest publishing

It's possible for containers to be specified in a 'manifest list' - a set of container image manifests that represent the same application on different underlying OS/hardware configurations.

Fundamentally this would be something like a multitargeted build. For some selection of OS/OSVersions and Architectures we'd need to orchestrate

  • building the app for that os/version/architecture
  • publishing a container image for that os/version/architecture

then, once all of those were done, we'd need to

  • create a new 'manifest list',
  • add each of the created images to the list,
  • ensure each one was 'annotated' with the correct os/platform/etc annotations, and
  • push the manifest list to the registry

There are a couple hurdles we'd need to cover:

  • if we would like to perform this by default for projects that specify multiple RIDs, then we may need a concept of a cross-RID/cross-TFM publish. This doesn't exist right now, and in fact is explicitly stopped by the cross-targeting targets in the .NET SDK.
  • if we modeled this as a different, standlone target, then we wouldn't need to tie it to 'publish' necessarily, but we might lose the benefit of associating with 'publish' as a concept
  • I'm unsure how much users would use this - is it worth pioneering a potentially-new publishing concept?
    • This would bring us parity with Jib and Ko - maybe parity is enough of a motivator

Implement conventional labels on top of existing label infrastructure

Labels

There are some well-known labels that we should consider setting by default:

Annotation Key Derived from
org.opencontainers.image.created $([System.DateTime]::UtcNow.ToString("o"))
org.opencontainers.image.description $(Description)
org.opencontainers.image.authors $(Authors)
org.opencontainers.image.url $(PackageProjectUrl) if present.
org.opencontainers.image.documentation Since $(PackageLicenseUrl) is deprecated, we may just have to default to $(RepositoryUrl). Users should be able to override this easily though.
org.opencontainers.image.version $(Version) if present. Note that there's not really a consistent place to hook into where we can be assured of a version being set, so we'll want to be sometime directly before Build, but as late as possible otherwise.
org.opencontainers.image.vendor There's no $(PackageOwners) or $(Owners) to bootstrap off of, but maybe we should make one?
org.opencontainers.image.licenses $(PackageLicenseExpression)
org.opencontainers.image.title $(Title)
org.opencontainers.image.base.digest The resolved digest of $(ContainerBaseImage). We don't calculate this yet so can't do it.
org.opencontainers.image.base.name $(ContainerBaseImage)
org.opencontainers.image.source $(PrivateRepositoryUrl) if present and $(PublishRepositoryUrl) is true.
org.opencontainers.image.revision $(RepositoryCommit) if present and $(PublishRepositoryUrl) is true.

Create `Layer.FromFiles`

My use case is

// Turn the build output (as items) into an array of filepaths
string[] filePaths = Files.Select((f) => f.ItemSpec).ToArray();

Layer newLayer = Layer.FromDirectory(Path.GetDirectoryName(filePaths[0]), WorkingDirectory);

Support pivoting on OS platform and Architecture for image selection

Right now we explicitly support Linux/x64 container images - we should instead take into account the OS and Architecture the user provides and pick the correct 'base image' from the manifest list (if there is one on the base image).

We'll need to investigate what level of impact this has on picking the correct base image, but this will also have an impact on

  • WorkingDirectory
  • EntryPoint

Overlaps with #117

Explicit support for Windows containers

When building a container for Windows we'll likely need:

  • different WorkingDirectory
  • different Entrypoints (though this may be handled with the better WorkingDirectory)
  • potentially different layer tarball shape (include registry hives and files differently, don't include drive letter root?)

Horrible error experience when publishing to local Docker but the daemon isn't running

Ask me how I know!

❯ dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer -bl
MSBuild version 17.4.0-preview-22368-02+c8492483a for .NET
C:\Program Files\dotnet\sdk\7.0.100-preview.7.22377.5\MSBuild.dll -bl -distributedlogger:Microsoft.DotNet.Tools.MSBuild.MSBuildLogger,C:\Program Files\dotnet\sdk\7.0.100-preview.7.22377.5\dotnet.dll*Microsoft.DotNet.Tools.MSBuild.MSBuildForwardingLogger,C:\Program Files\dotnet\sdk\7.0.100-preview.7.22377.5\dotnet.dll -maxcpucount -property:_IsPublishing=true -property:RuntimeIdentifier=linux-x64 -property:SelfContained=false -p:PublishProfile=DefaultContainer -restore -target:Publish -verbosity:m .\containerized.csproj
  Determining projects to restore...
  Restored S:\play\containerized\containerized.csproj (in 1.09 sec).
C:\Program Files\dotnet\sdk\7.0.100-preview.7.22377.5\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInf
erence.targets(219,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-
policy [S:\play\containerized\containerized.csproj]
  containerized -> S:\play\containerized\bin\Debug\net7.0\linux-x64\containerized.dll
  containerized -> S:\play\containerized\bin\Debug\net7.0\linux-x64\publish\
error during connect: This error may indicate that the docker daemon is not running.: Post "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/images/load?quiet=1": open //./pipe/docker_engine: The system cannot find the file specified.
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018: The "CreateNewImage" task failed unexpectedly. [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018: System.AggregateException: One or more errors occurred. (The pipe has been ended.) [S:\play\c
ontainerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:  ---> System.IO.IOException: The pipe has been ended. [S:\play\containerized\containerized.cs
proj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.IO.RandomAccess.WriteAtOffset(SafeFileHandle handle, ReadOnlySpan`1 buffer, Int6
4 fileOffset) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.Win32.SafeHandles.SafeFileHandle.ThreadPoolValueTaskSource.ExecuteInternal()
[S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018: --- End of stack trace from previous location --- [S:\play\containerized\containerized.csproj
]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.Win32.SafeHandles.SafeFileHandle.ThreadPoolValueTaskSource.GetResult(Int16 to
ken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.Win32.SafeHandles.SafeFileHandle.ThreadPoolValueTaskSource.System.Threading.T
asks.Sources.IValueTaskSource.GetResult(Int16 token) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.IO.Strategies.BufferedFileStreamStrategy.WriteToNonSeekableAsync(ReadOnlyMemory`
1 source, CancellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.IO.Strategies.FileStreamHelpers.AsyncModeCopyToAsync(SafeFileHandle handle, Bool
ean canSeek, Int64 filePosition, Stream destination, Int32 bufferSize, CancellationToken cancellationToken) [S:\play\co
ntainerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.IO.Strategies.AsyncWindowsFileStreamStrategy.AsyncModeCopyToAsync(Stream destina
tion, Int32 bufferSize, CancellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.IO.Strategies.BufferedFileStreamStrategy.CopyToAsyncCore(Stream destination, Int
32 bufferSize, CancellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Formats.Tar.TarHeader.WriteDataAsync(Stream archiveStream, Stream dataStream, In
t64 actualLength, CancellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Formats.Tar.TarHeader.WriteAsGnuInternalAsync(Stream archiveStream, Memory`1 buf
fer, CancellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Formats.Tar.TarHeader.WriteAsGnuAsync(Stream archiveStream, Memory`1 buffer, Can
cellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Formats.Tar.TarWriter.WriteEntryAsyncInternal(TarEntry entry, CancellationToken
cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Formats.Tar.TarWriter.ReadFileFromDiskAndWriteToArchiveStreamAsEntryAsync(String
 fullPath, String entryName, CancellationToken cancellationToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.NET.Build.Containers.LocalDocker.WriteImageToStream(Image x, String name, Str
ing tag, Stream imageStream) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.NET.Build.Containers.LocalDocker.Load(Image x, String name, String tag, Strin
g baseName) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    --- End of inner exception stack trace --- [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) [
S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellat
ionToken) [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at System.Threading.Tasks.Task.Wait() [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.NET.Build.Containers.Tasks.CreateNewImage.Execute() [S:\play\containerized\co
ntainerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Ex
ecute() [S:\play\containerized\containerized.csproj]
C:\Users\raines\.nuget\packages\microsoft.net.build.containers\0.1.0-alpha.47\build\Microsoft.NET.Build.Containers.targ
ets(66,9): error MSB4018:    at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExec
utionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteT
ask) [S:\play\containerized\containerized.csproj]

.NET Framework task implementation

We want the new functionality to be usable from within Visual Studio, so we need .NET Framework-compatible tasks. But the tar API is new to .NET 7.

  • Shell out to a .NET 7 CLI app?
  • Something else?

Offline support

Right now we pull manifests and the configuration blob for a given image on every operation. These structures should also be stored in the local content store, with a similar 'force refresh' override for this behavior as described in #114.

Users inside Visual Studio have an integrated publish experience

VS Users should have an integrated container publishing experience, on par with the existing experiences around onboarding a project to containers. Currently VS scaffolds a Dockerfile, configures publishing to a local docker registry or a remote registry (with or without authentication) and executes the publish.

Users should have a similar experience when using this new capability. Wizards should set the new project properties as appropriate (for example after configuring a push registry the PublishContainerRegistry property could be set), and the 'publish' action in VS should execute the task created in this repository.

Consider renaming ContainerImageName to ContainerRepository

The more general nomenclature for the middle part of a fully-qualified Image name (the non-registry, non-tag portion) is Repository. We should consider changing our name to align with that, so users don't have garbled terminology.

Some deeper discussion here.

Infer/Assign relevant RIDs for the user

Right now, for the most efficient framework-dependent containers, a user has to enter a command like

dotnet publish -c Release --os linux --arch x64 -p:PublishProfile=DefaultContainer

manually specifying the OS and Arch (or, alternatively the entire RID via -r). In cases where we know something about the destination container (through inference?) is it possible to perform the build/publish cycle with a RID taken from that image's metadata?

Ideally, I'd like for the command dotnet publish -c Release -p:PublishProfile=DefaultContainer to have the same information as the command above.

Create layer with a folder

API operation for within

COPY --from=build-env /app/out .

Probably something like

public class Layer
{
    public static Layer FromDirectory(string path, string pathInContainer) {}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.