Skip to content

Commit

Permalink
Image refactoring and fixes to ExposedPorts and Labels logic (d…
Browse files Browse the repository at this point in the history
  • Loading branch information
vlada-shubina authored Feb 15, 2023
1 parent b0bfa16 commit ac3974d
Show file tree
Hide file tree
Showing 16 changed files with 793 additions and 340 deletions.
40 changes: 23 additions & 17 deletions Microsoft.NET.Build.Containers.IntegrationTests/EndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,27 @@ public async Task ApiEndToEndWithRegistryPushAndPull()

Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));

Image? x = await registry.GetImageManifest(
ImageBuilder imageBuilder = await registry.GetImageManifest(
DockerRegistryManager.BaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);

Assert.NotNull(x);
Assert.NotNull(imageBuilder);

Layer l = Layer.FromDirectory(publishDirectory, "/app");

x.AddLayer(l);
imageBuilder.AddLayer(l);

x.SetEntrypoint(new[] { "/app/MinimalTestApp" });
imageBuilder.SetEntryPoint(new[] { "/app/MinimalTestApp" });

BuiltImage builtImage = imageBuilder.Build();

// Push the image back to the local registry
var sourceReference = new ImageReference(registry, DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag);
var destinationReference = new ImageReference(registry, NewImageName(), "latest");

await registry.Push(x, sourceReference, destinationReference, Console.WriteLine).ConfigureAwait(false);
await registry.Push(builtImage, sourceReference, destinationReference, Console.WriteLine).ConfigureAwait(false);

// pull it back locally
new BasicCommand(_testOutput, "docker", "pull", $"{DockerRegistryManager.LocalRegistry}/{NewImageName()}:latest")
Expand All @@ -79,24 +81,26 @@ public async Task ApiEndToEndWithLocalLoad()

Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.LocalRegistry));

Image? x = await registry.GetImageManifest(
ImageBuilder imageBuilder = await registry.GetImageManifest(
DockerRegistryManager.BaseImage,
DockerRegistryManager.Net6ImageTag,
"linux-x64",
ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);
Assert.NotNull(x);
Assert.NotNull(imageBuilder);

Layer l = Layer.FromDirectory(publishDirectory, "/app");

x.AddLayer(l);
imageBuilder.AddLayer(l);

imageBuilder.SetEntryPoint(new[] { "/app/MinimalTestApp" });

x.SetEntrypoint(new[] { "/app/MinimalTestApp" });
BuiltImage builtImage = imageBuilder.Build();

// Load the image into the local Docker daemon
var sourceReference = new ImageReference(registry, DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag);
var destinationReference = new ImageReference(registry, NewImageName(), "latest");

await new LocalDocker(Console.WriteLine).Load(x, sourceReference, destinationReference).ConfigureAwait(false);
await new LocalDocker(Console.WriteLine).Load(builtImage, sourceReference, destinationReference).ConfigureAwait(false);

// Run the image
new BasicCommand(_testOutput, "docker", "run", "--rm", "--tty", $"{NewImageName()}:latest")
Expand Down Expand Up @@ -289,21 +293,23 @@ public async Task CanPackageForAllSupportedContainerRIDs(string rid, bool isRIDS
// Build the image
Registry registry = new Registry(ContainerHelpers.TryExpandRegistryToUri(DockerRegistryManager.BaseImageSource));

Image? x = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag, rid, ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);
Assert.NotNull(x);
ImageBuilder? imageBuilder = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag, rid, ToolsetUtils.GetRuntimeGraphFilePath()).ConfigureAwait(false);
Assert.NotNull(imageBuilder);

Layer l = Layer.FromDirectory(publishDirectory, "/app");

x.AddLayer(l);
x.WorkingDirectory = workingDir;
imageBuilder.AddLayer(l);
imageBuilder.SetWorkingDirectory(workingDir);

string[] entryPoint = DecideEntrypoint(rid, isRIDSpecific, "MinimalTestApp", workingDir);
imageBuilder.SetEntryPoint(entryPoint);

var entryPoint = DecideEntrypoint(rid, isRIDSpecific, "MinimalTestApp", workingDir);
x.SetEntrypoint(entryPoint);
BuiltImage builtImage = imageBuilder.Build();

// Load the image into the local Docker daemon
var sourceReference = new ImageReference(registry, DockerRegistryManager.BaseImage, DockerRegistryManager.Net7ImageTag);
var destinationReference = new ImageReference(registry, NewImageName(), rid);
await new LocalDocker(Console.WriteLine).Load(x, sourceReference, destinationReference).ConfigureAwait(false);
await new LocalDocker(Console.WriteLine).Load(builtImage, sourceReference, destinationReference).ConfigureAwait(false);

// Run the image
new BasicCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public async Task GetFromRegistry()

// Don't need rid graph for local registry image pulls - since we're only pushing single image manifests (not manifest lists)
// as part of our setup, we could put literally anything in here. The file at the passed-in path would only get read when parsing manifests lists.
Image? downloadedImage = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64", ridgraphfile).ConfigureAwait(false);
ImageBuilder? downloadedImage = await registry.GetImageManifest(DockerRegistryManager.BaseImage, DockerRegistryManager.Net6ImageTag, "linux-x64", ridgraphfile).ConfigureAwait(false);

Assert.NotNull(downloadedImage);
}
Expand Down
294 changes: 294 additions & 0 deletions Microsoft.NET.Build.Containers.UnitTests/ImageBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Nodes;
using Microsoft.NET.Build.Containers;
using Xunit;

namespace Test.Microsoft.NET.Build.Containers;

public class ImageBuilderTests
{
[Fact]
public void CanAddLabelsToImage()
{
string simpleImageConfig =
"""
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"ASPNETCORE_URLS=http://+:80",
"DOTNET_RUNNING_IN_CONTAINER=true",
"DOTNET_VERSION=7.0.2",
"ASPNET_VERSION=7.0.2"
],
"Cmd": ["bash"],
"Image": "sha256:d772d27ebeec80393349a4770dc37f977be2c776a01c88b624d43f93fa369d69",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"created": "2023-02-04T08:14:52.000901321Z",
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:bd2fe8b74db65d82ea10db97368d35b92998d4ea0e7e7dc819481fe4a68f64cf",
"sha256:94100d1041b650c6f7d7848c550cd98c25d0bdc193d30692e5ea5474d7b3b085",
"sha256:53c2a75a33c8f971b4b5036d34764373e134f91ee01d8053b4c3573c42e1cf5d",
"sha256:49a61320e585180286535a2545be5722b09e40ad44c7c190b20ec96c9e42e4a3",
"sha256:8a379cce2ac272aa71aa029a7bbba85c852ba81711d9f90afaefd3bf5036dc48"
]
}
}
""";

JsonNode? node = JsonNode.Parse(simpleImageConfig);
Assert.NotNull(node);

ImageConfig baseConfig = new ImageConfig(node);

baseConfig.AddLabel("testLabel1", "v1");
baseConfig.AddLabel("testLabel2", "v2");

string readyImage = baseConfig.BuildConfig();

JsonNode? result = JsonNode.Parse(readyImage);

var resultLabels = result?["config"]?["Labels"] as JsonObject;
Assert.NotNull(resultLabels);

Assert.Equal(2, resultLabels.Count);
Assert.Equal("v1", resultLabels["testLabel1"]?.ToString());
Assert.Equal("v2", resultLabels["testLabel2"]?.ToString());
}

[Fact]
public void CanPreserveExistingLabels()
{
string simpleImageConfig =
"""
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"ASPNETCORE_URLS=http://+:80",
"DOTNET_RUNNING_IN_CONTAINER=true",
"DOTNET_VERSION=7.0.2",
"ASPNET_VERSION=7.0.2"
],
"Cmd": ["bash"],
"Image": "sha256:d772d27ebeec80393349a4770dc37f977be2c776a01c88b624d43f93fa369d69",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels":
{
"existing" : "e1",
"existing2" : "e2"
}
},
"created": "2023-02-04T08:14:52.000901321Z",
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:bd2fe8b74db65d82ea10db97368d35b92998d4ea0e7e7dc819481fe4a68f64cf",
"sha256:94100d1041b650c6f7d7848c550cd98c25d0bdc193d30692e5ea5474d7b3b085",
"sha256:53c2a75a33c8f971b4b5036d34764373e134f91ee01d8053b4c3573c42e1cf5d",
"sha256:49a61320e585180286535a2545be5722b09e40ad44c7c190b20ec96c9e42e4a3",
"sha256:8a379cce2ac272aa71aa029a7bbba85c852ba81711d9f90afaefd3bf5036dc48"
]
}
}
""";

JsonNode? node = JsonNode.Parse(simpleImageConfig);
Assert.NotNull(node);

ImageConfig baseConfig = new ImageConfig(node);

baseConfig.AddLabel("testLabel1", "v1");
baseConfig.AddLabel("existing2", "v2");

string readyImage = baseConfig.BuildConfig();

JsonNode? result = JsonNode.Parse(readyImage);

var resultLabels = result?["config"]?["Labels"] as JsonObject;
Assert.NotNull(resultLabels);

Assert.Equal(3, resultLabels.Count);
Assert.Equal("v1", resultLabels["testLabel1"]?.ToString());
Assert.Equal("v2", resultLabels["existing2"]?.ToString());
Assert.Equal("e1", resultLabels["existing"]?.ToString());
}

[Fact]
public void CanAddPortsToImage()
{
string simpleImageConfig =
"""
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"ASPNETCORE_URLS=http://+:80",
"DOTNET_RUNNING_IN_CONTAINER=true",
"DOTNET_VERSION=7.0.2",
"ASPNET_VERSION=7.0.2"
],
"Cmd": ["bash"],
"Image": "sha256:d772d27ebeec80393349a4770dc37f977be2c776a01c88b624d43f93fa369d69",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"created": "2023-02-04T08:14:52.000901321Z",
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:bd2fe8b74db65d82ea10db97368d35b92998d4ea0e7e7dc819481fe4a68f64cf",
"sha256:94100d1041b650c6f7d7848c550cd98c25d0bdc193d30692e5ea5474d7b3b085",
"sha256:53c2a75a33c8f971b4b5036d34764373e134f91ee01d8053b4c3573c42e1cf5d",
"sha256:49a61320e585180286535a2545be5722b09e40ad44c7c190b20ec96c9e42e4a3",
"sha256:8a379cce2ac272aa71aa029a7bbba85c852ba81711d9f90afaefd3bf5036dc48"
]
}
}
""";

JsonNode? node = JsonNode.Parse(simpleImageConfig);
Assert.NotNull(node);

ImageConfig baseConfig = new ImageConfig(node);

baseConfig.ExposePort(6000, PortType.tcp);
baseConfig.ExposePort(6010, PortType.udp);

string readyImage = baseConfig.BuildConfig();

JsonNode? result = JsonNode.Parse(readyImage);

var resultPorts = result?["config"]?["ExposedPorts"] as JsonObject;
Assert.NotNull(resultPorts);

Assert.Equal(2, resultPorts.Count);
Assert.NotNull(resultPorts["6000/tcp"] as JsonObject);
Assert.NotNull( resultPorts["6010/udp"] as JsonObject);
}

[Fact]
public void CanPreserveExistingPorts()
{
string simpleImageConfig =
"""
{
"architecture": "amd64",
"config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"ASPNETCORE_URLS=http://+:80",
"DOTNET_RUNNING_IN_CONTAINER=true",
"DOTNET_VERSION=7.0.2",
"ASPNET_VERSION=7.0.2"
],
"Cmd": ["bash"],
"Image": "sha256:d772d27ebeec80393349a4770dc37f977be2c776a01c88b624d43f93fa369d69",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null,
"ExposedPorts":
{
"6100/tcp": {},
"6200": {}
}
},
"created": "2023-02-04T08:14:52.000901321Z",
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:bd2fe8b74db65d82ea10db97368d35b92998d4ea0e7e7dc819481fe4a68f64cf",
"sha256:94100d1041b650c6f7d7848c550cd98c25d0bdc193d30692e5ea5474d7b3b085",
"sha256:53c2a75a33c8f971b4b5036d34764373e134f91ee01d8053b4c3573c42e1cf5d",
"sha256:49a61320e585180286535a2545be5722b09e40ad44c7c190b20ec96c9e42e4a3",
"sha256:8a379cce2ac272aa71aa029a7bbba85c852ba81711d9f90afaefd3bf5036dc48"
]
}
}
""";

JsonNode? node = JsonNode.Parse(simpleImageConfig);
Assert.NotNull(node);

ImageConfig baseConfig = new ImageConfig(node);

baseConfig.ExposePort(6000, PortType.tcp);
baseConfig.ExposePort(6010, PortType.udp);
baseConfig.ExposePort(6100, PortType.udp);
baseConfig.ExposePort(6200, PortType.tcp);

string readyImage = baseConfig.BuildConfig();

JsonNode? result = JsonNode.Parse(readyImage);

var resultPorts = result?["config"]?["ExposedPorts"] as JsonObject;
Assert.NotNull(resultPorts);

Assert.Equal(5, resultPorts.Count);
Assert.NotNull(resultPorts["6000/tcp"] as JsonObject);
Assert.NotNull(resultPorts["6010/udp"] as JsonObject);
Assert.NotNull(resultPorts["6100/udp"] as JsonObject);
Assert.NotNull(resultPorts["6100/tcp"] as JsonObject);
Assert.NotNull(resultPorts["6200/tcp"] as JsonObject);
}
}
Loading

0 comments on commit ac3974d

Please sign in to comment.