Skip to content

Commit 40999fd

Browse files
authored
container: Add application layer to the correct end of the layer stack (#51)
Motivation ---------- containertool currently adds the app layer to the beginning of the layer stack array in the manifest. This results in the app layer being the first to be unpacked, with the others stacked on top. We can show this by adding a plain text file as the executable. If we stack another layer on top with a file of the same name, it should replace the underlying one but it does not: echo first > bar swift run containertool --repository localhost:5555/bar bar podman run --pull=always -it --rm --entrypoint=cat localhost:5555/bar:latest bar # prints: first echo second > bar swift run containertool --repository localhost:5555/bar bar --from localhost:5555/bar:latest podman run --pull=always -it --rm --entrypoint=cat localhost:5555/bar:latest bar # prints: first # should print: second Currently containertool is only used to add the application binary to the application layer. This bug will only cause a problem if the base layer adds a binary at the same path, because this will override the application. This bug probably arose because the specification for the rootfs.diff_ids field of the image configuration defines the layers as being "in order from first to last", which could be read ambiguously: https://github.com/opencontainers/image-spec/blob/main/config.md?plain=1#L220-L222 The specification for the manifest.layers field is much more explicit about the ordering: https://github.com/opencontainers/image-spec/blob/fbb4662eb53b80bd38f7597406cf1211317768f0/manifest.md?plain=1#L70-L71 Modifications ------------- Append the application layer to layer stacks in the manifest and configuration blobs, instead of prepending. Result ------ This with this change, the second build and container run in the example above prints "second" as expected. Test Plan --------- This PR adds a new integration test which uses `containertool` to build two layers and check that they override each other correctly. All existing tests continue to pass. Fixes #57
1 parent dc8f7df commit 40999fd

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Interop tests
2+
3+
on:
4+
workflow_call:
5+
# inputs:
6+
# example:
7+
# required: true
8+
# type: string
9+
10+
jobs:
11+
layering-test:
12+
name: Layering test
13+
runs-on: ubuntu-latest
14+
services:
15+
registry:
16+
image: registry:2
17+
ports:
18+
- 5000:5000
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
with:
23+
persist-credentials: false
24+
25+
- name: Mark the workspace as safe
26+
# https://github.com/actions/checkout/issues/766
27+
run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
28+
29+
# First layer: payload does not have to be an executable, it just has to have known contents
30+
- name: Build first layer
31+
run: |
32+
echo first > payload
33+
swift run containertool --repository localhost:5000/layering_test payload
34+
docker run --pull=always --rm --entrypoint=cat localhost:5000/layering_test payload | grep first
35+
36+
# Second layer: payload does not have to be an executable, it just has to have known contents. It should replace the first layer.
37+
- name: Build another layer, which should override 'payload' from the first layer
38+
run: |
39+
echo second > payload
40+
swift run containertool --repository localhost:5000/layering_test payload --from localhost:5000/layering_test:latest
41+
docker run --pull=always --rm --entrypoint=cat localhost:5000/layering_test payload | grep second

.github/workflows/pull_request.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
license_header_check_project_name: "SwiftContainerPlugin"
1515
shell_check_container_image: "swift:6.0-noble"
1616

17+
# Unit tests for functions and modules
1718
unit-tests:
1819
name: Unit tests
1920
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
@@ -24,6 +25,7 @@ jobs:
2425
linux_nightly_6_1_arguments_override: "--skip SmokeTests"
2526
linux_nightly_main_arguments_override: "--skip SmokeTests"
2627

28+
# Test functions and modules against an separate registry
2729
integration-tests:
2830
name: Integration tests
2931
runs-on: ubuntu-latest
@@ -51,6 +53,12 @@ jobs:
5153
run: |
5254
swift test
5355
56+
# Test that outputs can be handled properly by other systems
57+
interop-tests:
58+
name: Interop tests
59+
uses: ./.github/workflows/interop_tests.yml
60+
61+
# Full build-package-deploy-run cycles
5462
endtoend-tests:
5563
name: End to end tests
5664
uses: ./.github/workflows/endtoend_tests.yml

Sources/containertool/containertool.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,10 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
161161
config: inherited_config,
162162
rootfs: .init(
163163
_type: "layers",
164-
diff_ids: [
165-
// The diff_id is the digest of the _uncompressed_ layer archive.
166-
// It is used by the runtime, which might not store the layers in
167-
// the compressed form in which it received them from the registry.
168-
digest(of: tardiff)
169-
] + baseimage_config.rootfs.diff_ids
164+
// The diff_id is the digest of the _uncompressed_ layer archive.
165+
// It is used by the runtime, which might not store the layers in
166+
// the compressed form in which it received them from the registry.
167+
diff_ids: baseimage_config.rootfs.diff_ids + [digest(of: tardiff)]
170168
),
171169
history: [.init(created: timestamp, created_by: "containertool")]
172170
)
@@ -184,7 +182,7 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
184182
schemaVersion: 2,
185183
mediaType: "application/vnd.oci.image.manifest.v1+json",
186184
config: config_blob,
187-
layers: [application_layer] + baseimage_manifest.layers
185+
layers: baseimage_manifest.layers + [application_layer]
188186
)
189187

190188
// MARK: Upload base image

0 commit comments

Comments
 (0)