Compare commits
221 Commits
Author | SHA1 | Date |
---|---|---|
|
507bfee191 | |
|
38596c8b22 | |
|
6157151749 | |
|
248d28a764 | |
|
ff9323859a | |
|
2da141b283 | |
|
d3a009f139 | |
|
fb86ede0a2 | |
|
2894a94c12 | |
|
27b707f152 | |
|
f450c33b75 | |
|
e6429167ae | |
|
6c1728eae2 | |
|
d9d2f0b1b4 | |
|
a3e753580e | |
|
a7b01be272 | |
|
2eb82b6c61 | |
|
bc05100159 | |
|
14a59a2b76 | |
|
0b068a559d | |
|
bae71e646d | |
|
337f5a134e | |
|
1ac8bb4708 | |
|
b4bcc305ed | |
|
445fb7b140 | |
|
75640408c5 | |
|
824af52753 | |
|
5c256f9ea1 | |
|
ece3f19a66 | |
|
71a1d7bcaa | |
|
02cfb57857 | |
|
a59d0ac61a | |
|
2baeb05d7c | |
|
0b89b6bb00 | |
|
07cc69cb30 | |
|
671b7fe99a | |
|
42b1c2eb9d | |
|
8e777577fd | |
|
b66e7fa843 | |
|
c731c259a9 | |
|
53ee7d84d2 | |
|
b31cbf7a01 | |
|
c555e0228c | |
|
59890cbd87 | |
|
7f8422b33f | |
|
90e0caaf46 | |
|
e832586924 | |
|
b25edf5694 | |
|
6fb2b45a0e | |
|
9f3eab454f | |
|
eb6b492937 | |
|
b6ceafb667 | |
|
c1dcfa4ad3 | |
|
53e69a3800 | |
|
f7a92b1c3d | |
|
dc7971fb43 | |
|
599681f519 | |
|
15b2cc3a4e | |
|
c13417d74a | |
|
7d768be794 | |
|
debc5f6241 | |
|
d43f1d537d | |
|
781f98b75d | |
|
96ae9256ba | |
|
b3a87c3938 | |
|
a7f4976a9d | |
|
0a5cb1dd4f | |
|
1345f246a8 | |
|
792e7f9cae | |
|
5e5e96e743 | |
|
9fa9071745 | |
|
d0d1c2d804 | |
|
fd4477a84a | |
|
fe8d6afd79 | |
|
1791615612 | |
|
421872fee8 | |
|
228be771f1 | |
|
554005ccc9 | |
|
08b026a1c0 | |
|
4fa13e2342 | |
|
599aa39d89 | |
|
875ae4c9da | |
|
040bdead82 | |
|
62477fbe5b | |
|
410b0f290b | |
|
102476051b | |
|
df145ce245 | |
|
9c56cda790 | |
|
90ec23f0f3 | |
|
d232567a66 | |
|
df3df359a1 | |
|
c24ee86b25 | |
|
43543d97e7 | |
|
35eea81fb1 | |
|
299b2968da | |
|
2f8c0c0914 | |
|
787bba68fb | |
|
ee8c5c1d97 | |
|
1784864c45 | |
|
e08d320fef | |
|
d53e06bf0c | |
|
4c0dd00232 | |
|
3d44bd1197 | |
|
cfb5a16711 | |
|
5827adbe6c | |
|
87a6ab93ae | |
|
d828f4f351 | |
|
50adb44db9 | |
|
b2f3b6cd0d | |
|
d516485683 | |
|
4c39ab5a7a | |
|
190a71390d | |
|
84894d9aae | |
|
36f9236855 | |
|
c46f1f4c17 | |
|
27c25728fb | |
|
b60fbeaad1 | |
|
c5805c5bed | |
|
c459113113 | |
|
f073052058 | |
|
4bde79d58a | |
|
5be808b41c | |
|
30857ef14c | |
|
98aa61891b | |
|
7ad308c342 | |
|
e2db1e1e01 | |
|
f3dc0eee52 | |
|
21e112424d | |
|
f38310bb9e | |
|
b074e5b04f | |
|
81c6a301c2 | |
|
0eee948d0c | |
|
9df1527422 | |
|
401800a2f3 | |
|
fee517ad2f | |
|
3086d71238 | |
|
221e3e0091 | |
|
a4957197c3 | |
|
d937248ac5 | |
|
e5db88c1e6 | |
|
04f836d7f9 | |
|
3fa39407f1 | |
|
32077c5065 | |
|
7eab2ceb20 | |
|
a27427fd29 | |
|
e9ac446742 | |
|
b8c4b3d852 | |
|
5bfcf9f243 | |
|
d247c5049b | |
|
7fc539bd71 | |
|
9bd346eb1b | |
|
5118b48fe3 | |
|
c71cfb65c3 | |
|
1c9e0d33c8 | |
|
5fcbccc4c1 | |
|
1e28020823 | |
|
aee274f18f | |
|
02b16a30c6 | |
|
8cafefbb56 | |
|
76b786b5ae | |
|
3ac14a9029 | |
|
dcbf14bc4e | |
|
87a4206dcc | |
|
694c405909 | |
|
4d415b4ad3 | |
|
41e1a56453 | |
|
b0307c0898 | |
|
1d88823a28 | |
|
01c83c7302 | |
|
15914f1acb | |
|
40bdc2bcd1 | |
|
b5fa00b0f2 | |
|
3b891956bc | |
|
2a2834dd1c | |
|
c9f1cf6b14 | |
|
15cd2e0b97 | |
|
24eaa88bef | |
|
4d68bfc5fe | |
|
f396316397 | |
|
1226b898b2 | |
|
68c4acfd42 | |
|
6466b731fd | |
|
7c6f380496 | |
|
166ea55fd8 | |
|
11054f7019 | |
|
901769d8ef | |
|
82cdd4056a | |
|
f207d83e9e | |
|
721a193e05 | |
|
3c8411b339 | |
|
42367649a4 | |
|
0365d39441 | |
|
8bd7a7dc99 | |
|
a91b1bb35b | |
|
2c26839d13 | |
|
721e285b0d | |
|
848062ece5 | |
|
1c74a704e3 | |
|
93c72b5bfb | |
|
1cf746580d | |
|
e0367ae325 | |
|
66a553a1b6 | |
|
b7a46d6d20 | |
|
cbd4ef2360 | |
|
d166c3b0d8 | |
|
c0e32debae | |
|
cd1a24243e | |
|
772bb09ba5 | |
|
3914f637a8 | |
|
3c333bb0da | |
|
9807c4d151 | |
|
25f320f39a | |
|
d28b664b37 | |
|
d4cfdd94c5 | |
|
3388ef6a22 | |
|
d9cf528510 | |
|
ff0e8ec807 | |
|
368d2e27c2 | |
|
0c2e76bb7c | |
|
8586da2ae3 | |
|
9cb28b4a31 |
|
@ -38,7 +38,7 @@ jobs:
|
|||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
|
@ -61,10 +61,12 @@ jobs:
|
|||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
secrets: |
|
||||
github_token=${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
@ -70,6 +70,8 @@ jobs:
|
|||
local goarm="${3:-}"
|
||||
local result
|
||||
|
||||
GOOS="$goos" GOARCH="$goarch" GOARM="$goarm" go build ./examples/...
|
||||
|
||||
github::timer::begin
|
||||
|
||||
GOOS="$goos" GOARCH="$goarch" GOARM="$goarm" make binaries \
|
||||
|
@ -97,3 +99,12 @@ jobs:
|
|||
build linux s390x
|
||||
|
||||
[ ! "$failure" ] || exit 1
|
||||
|
||||
- if: ${{ env.GO_VERSION != '' }}
|
||||
name: "Run: make binaries with custom BUILDTAGS"
|
||||
run: |
|
||||
set -eux
|
||||
# no_ipfs: make sure it does not incur any IPFS-related dependency
|
||||
go mod vendor
|
||||
rm -rf vendor/github.com/ipfs vendor/github.com/multiformats
|
||||
BUILDTAGS=no_ipfs make binaries
|
||||
|
|
|
@ -39,6 +39,8 @@ jobs:
|
|||
uses: crazy-max/ghaction-github-runtime@3cb05d89e1f492524af3d41a1c98c83bc3025124 # v3.1.0
|
||||
|
||||
- name: "Run: build dependencies for the integration test environment image"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Cache is sharded per-architecture
|
||||
arch=${{ env.RUNNER_ARCH == 'ARM64' && 'arm64' || 'amd64' }}
|
||||
|
@ -49,6 +51,7 @@ jobs:
|
|||
args=(--build-arg CONTAINERD_VERSION=${{ inputs.containerd-version }})
|
||||
fi
|
||||
docker buildx build \
|
||||
--secret id=github_token,env=GITHUB_TOKEN \
|
||||
--cache-to type=gha,compression=zstd,mode=max,scope=test-integration-dependencies-"$arch" \
|
||||
--cache-from type=gha,scope=test-integration-dependencies-"$arch" \
|
||||
--target build-dependencies "${args[@]}" .
|
||||
|
|
|
@ -81,11 +81,15 @@ jobs:
|
|||
docker run --privileged --rm tonistiigi/binfmt --install linux/arm/v7
|
||||
- if: ${{ inputs.canary }}
|
||||
name: "Init (canary): prepare updated test image"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
. ./hack/build-integration-canary.sh
|
||||
canary::build::integration
|
||||
- if: ${{ ! inputs.canary }}
|
||||
name: "Init: prepare test image"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
buildargs=()
|
||||
# If the runner is old, use old ubuntu inside the container as well
|
||||
|
@ -104,6 +108,7 @@ jobs:
|
|||
arch=${{ env.RUNNER_ARCH == 'ARM64' && 'arm64' || 'amd64' }}
|
||||
docker buildx create --name with-gha --use
|
||||
docker buildx build \
|
||||
--secret id=github_token,env=GITHUB_TOKEN \
|
||||
--output=type=docker \
|
||||
--cache-from type=gha,scope=test-integration-dependencies-"$arch" \
|
||||
-t "$target" --target "$target" \
|
||||
|
|
|
@ -31,7 +31,7 @@ jobs:
|
|||
fetch-depth: 1
|
||||
|
||||
- name: "Init: lima"
|
||||
uses: lima-vm/lima-actions/setup@be564a1408f84557d067b099a475652288074b2e # v1.0.0
|
||||
uses: lima-vm/lima-actions/setup@03b96d61959e83b2c737e44162c3088e81de0886 # v1.0.1
|
||||
id: lima-actions-setup
|
||||
|
||||
- name: "Init: Cache"
|
||||
|
@ -79,6 +79,8 @@ jobs:
|
|||
uses: crazy-max/ghaction-github-runtime@3cb05d89e1f492524af3d41a1c98c83bc3025124 # v3.1.0
|
||||
|
||||
- name: "Init: prepare integration tests"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -eux
|
||||
|
||||
|
@ -88,6 +90,7 @@ jobs:
|
|||
[ "$TARGET" = "rootless" ] && TARGET=test-integration-rootless || TARGET=test-integration
|
||||
docker buildx create --name with-gha --use
|
||||
docker buildx build \
|
||||
--secret id=github_token,env=GITHUB_TOKEN \
|
||||
--output=type=docker \
|
||||
--cache-from type=gha,scope=test-integration-dependencies-amd64 \
|
||||
-t test-integration --target "${TARGET}" \
|
||||
|
|
|
@ -33,6 +33,8 @@ jobs:
|
|||
go-version: "1.24"
|
||||
check-latest: true
|
||||
- name: "Compile binaries"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: make artifacts
|
||||
- name: "SHA256SUMS"
|
||||
run: |
|
||||
|
@ -54,7 +56,7 @@ jobs:
|
|||
Release manager: [ADD YOUR NAME HERE] (@[ADD YOUR GITHUB ID HERE])
|
||||
EOF
|
||||
- name: "Generate artifact attestation"
|
||||
uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
|
||||
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
subject-path: _output/*
|
||||
|
|
|
@ -102,7 +102,7 @@ jobs:
|
|||
canary: true
|
||||
|
||||
with:
|
||||
timeout: 45
|
||||
timeout: 60
|
||||
runner: ${{ matrix.runner }}
|
||||
target: ${{ matrix.target }}
|
||||
binary: ${{ matrix.binary && matrix.binary || 'nerdctl' }}
|
||||
|
@ -141,9 +141,9 @@ jobs:
|
|||
go-version: 1.24
|
||||
windows-cni-version: v0.3.1
|
||||
docker-version: 5:28.0.4-1~ubuntu.24.04~noble
|
||||
containerd-version: 2.1.0
|
||||
containerd-version: 2.1.3
|
||||
# Note: these as for amd64
|
||||
containerd-sha: 0e5359e957b66b679be807563a543c7416e305e3aafcf56bad90ef87a917014d
|
||||
containerd-sha: 436cc160c33b37ec25b89fb5c72fc879ab2b3416df5d7af240c3e9c2f4065d3c
|
||||
containerd-service-sha: 1941362cbaa89dd591b99c32b050d82c583d3cd2e5fa63085d7017457ec5fca8
|
||||
linux-cni-version: v1.7.1
|
||||
linux-cni-sha: 1a28a0506bfe5bcdc981caf1a49eeab7e72da8321f1119b7be85f22621013098
|
||||
|
|
|
@ -58,16 +58,20 @@ jobs:
|
|||
brew install yamllint shellcheck
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
- if: ${{ env.GO_VERSION != '' && env.RUNNER_OS == 'Linux' && matrix.goos == '' }}
|
||||
- if: ${{ env.GO_VERSION != '' && matrix.goos == '' }}
|
||||
name: "lint"
|
||||
env:
|
||||
NO_COLOR: true
|
||||
run: |
|
||||
echo "::group:: lint"
|
||||
cd mod/tigron
|
||||
export LINT_COMMIT_RANGE="$(jq -r '.after + "..HEAD"' ${GITHUB_EVENT_PATH})"
|
||||
make lint
|
||||
echo "::endgroup::"
|
||||
if [ "$RUNNER_OS" == Linux ]; then
|
||||
echo "::group:: lint"
|
||||
cd mod/tigron
|
||||
export LINT_COMMIT_RANGE="$(jq -r '.after + "..HEAD"' ${GITHUB_EVENT_PATH})"
|
||||
make lint
|
||||
echo "::endgroup::"
|
||||
else
|
||||
echo "Lint is disabled on $RUNNER_OS"
|
||||
fi
|
||||
- if: ${{ env.GO_VERSION != '' }}
|
||||
name: "test-unit"
|
||||
run: |
|
||||
|
|
|
@ -31,6 +31,7 @@ linters:
|
|||
- revive
|
||||
# Gocritic
|
||||
- gocritic
|
||||
- forbidigo
|
||||
|
||||
# 3. We used to use these, but have now removed them
|
||||
|
||||
|
@ -41,6 +42,15 @@ linters:
|
|||
# - nakedret
|
||||
|
||||
settings:
|
||||
forbidigo:
|
||||
forbid:
|
||||
# FIXME: there are still calls to os.WriteFile in tests under `cmd`
|
||||
- pattern: ^os\.WriteFile.*$
|
||||
pkg: github.com/containerd/nerdctl/v2/pkg
|
||||
msg: os.WriteFile is neither atomic nor durable - use nerdctl filesystem.WriteFile instead
|
||||
- pattern: ^os\.ReadFile.*$
|
||||
pkg: github.com/containerd/nerdctl/v2/pkg
|
||||
msg: use filesystem.ReadFile instead of os.ReadFile
|
||||
staticcheck:
|
||||
checks:
|
||||
# Below is the default set
|
||||
|
@ -53,9 +63,6 @@ linters:
|
|||
- "-ST1022"
|
||||
|
||||
##### TODO: fix and enable these
|
||||
# 4 occurrences.
|
||||
# Use fmt.Fprintf(x, ...) instead of x.Write(fmt.Sprintf(...)) https://staticcheck.dev/docs/checks#QF1012
|
||||
- "-QF1012"
|
||||
# 6 occurrences.
|
||||
# Apply De Morgan’s law https://staticcheck.dev/docs/checks#QF1001
|
||||
- "-QF1001"
|
||||
|
@ -114,7 +121,7 @@ linters:
|
|||
arguments: [7]
|
||||
- name: function-length
|
||||
# 155 occurrences (at default 0, 75). Really long functions should really be broken up in most cases.
|
||||
arguments: [0, 450]
|
||||
arguments: [0, 500]
|
||||
- name: cyclomatic
|
||||
# 204 occurrences (at default 10)
|
||||
arguments: [100]
|
||||
|
@ -122,7 +129,7 @@ linters:
|
|||
# 222 occurrences. Could indicate failure to handle broken conditions.
|
||||
disabled: true
|
||||
- name: cognitive-complexity
|
||||
arguments: [197]
|
||||
arguments: [205]
|
||||
# 441 occurrences (at default 7). We should try to lower it (involves significant refactoring).
|
||||
|
||||
##### P2: nice to have.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Building nerdctl
|
||||
|
||||
To build nerdctl, use `make`:
|
||||
|
||||
```bash
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Alternatively, nerdctl can be also built with `go build ./cmd/nerdctl`.
|
||||
However, this is not recommended as it does not populate the version string (`nerdctl -v`).
|
||||
|
||||
## Customization
|
||||
|
||||
To specify build tags, set the `BUILDTAGS` variable as follows:
|
||||
|
||||
```bash
|
||||
BUILDTAGS=no_ipfs make
|
||||
```
|
||||
|
||||
The following build tags are supported:
|
||||
* `no_ipfs` (since v2.1.3): Disable IPFS
|
105
Dockerfile
105
Dockerfile
|
@ -17,28 +17,28 @@
|
|||
|
||||
# Basic deps
|
||||
# @BINARY: the binary checksums are verified via Dockerfile.d/SHA256SUMS.d/<COMPONENT>-<VERSION>
|
||||
ARG CONTAINERD_VERSION=v2.1.0@061792f0ecf3684fb30a3a0eb006799b8c6638a7
|
||||
ARG CONTAINERD_VERSION=v2.1.3@c787fb98911740dd3ff2d0e45ce88cdf01410486
|
||||
ARG RUNC_VERSION=v1.3.0@4ca628d1d4c974f92d24daccb901aa078aad748e
|
||||
ARG CNI_PLUGINS_VERSION=v1.7.1@BINARY
|
||||
|
||||
# Extra deps: Build
|
||||
ARG BUILDKIT_VERSION=v0.21.1@BINARY
|
||||
ARG BUILDKIT_VERSION=v0.23.2@BINARY
|
||||
# Extra deps: Lazy-pulling
|
||||
ARG STARGZ_SNAPSHOTTER_VERSION=v0.16.3@BINARY
|
||||
# Extra deps: Encryption
|
||||
ARG IMGCRYPT_VERSION=v2.0.1@c377ec98ff79ec9205eabf555ebd2ea784738c6c
|
||||
# Extra deps: Rootless
|
||||
ARG ROOTLESSKIT_VERSION=v2.3.5@BINARY
|
||||
ARG SLIRP4NETNS_VERSION=v1.3.2@BINARY
|
||||
ARG SLIRP4NETNS_VERSION=v1.3.3@BINARY
|
||||
# Extra deps: bypass4netns
|
||||
ARG BYPASS4NETNS_VERSION=v0.4.2@aa04bd3dcc48c6dae6d7327ba219bda8fe2a4634
|
||||
# Extra deps: FUSE-OverlayFS
|
||||
ARG FUSE_OVERLAYFS_VERSION=v1.15@BINARY
|
||||
ARG CONTAINERD_FUSE_OVERLAYFS_VERSION=v2.1.5@BINARY
|
||||
ARG CONTAINERD_FUSE_OVERLAYFS_VERSION=v2.1.6@BINARY
|
||||
# Extra deps: Init
|
||||
ARG TINI_VERSION=v0.19.0@BINARY
|
||||
# Extra deps: Debug
|
||||
ARG BUILDG_VERSION=v0.5.2@BINARY
|
||||
ARG BUILDG_VERSION=v0.5.3@BINARY
|
||||
# Extra deps: gomodjail
|
||||
ARG GOMODJAIL_VERSION=v0.1.2@0a86b34442a491fa8f5e4565e9c846fce310239c
|
||||
|
||||
|
@ -47,19 +47,22 @@ ARG GOMODJAIL_VERSION=v0.1.2@0a86b34442a491fa8f5e4565e9c846fce310239c
|
|||
ARG GO_VERSION=1.24
|
||||
ARG UBUNTU_VERSION=24.04
|
||||
ARG CONTAINERIZED_SYSTEMD_VERSION=v0.1.1
|
||||
ARG GOTESTSUM_VERSION=v1.12.2
|
||||
ARG NYDUS_VERSION=v2.3.1
|
||||
ARG SOCI_SNAPSHOTTER_VERSION=0.9.0
|
||||
ARG KUBO_VERSION=v0.34.1
|
||||
ARG GOTESTSUM_VERSION=v1.12.3
|
||||
ARG NYDUS_VERSION=v2.3.2
|
||||
ARG SOCI_SNAPSHOTTER_VERSION=0.11.1
|
||||
ARG KUBO_VERSION=v0.35.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.6.1@sha256:923441d7c25f1e2eb5789f82d987693c47b8ed987c4ab3b075d6ed2b5d6779a3 AS xx
|
||||
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bookworm AS build-base-debian
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-bookworm AS build-base
|
||||
COPY --from=xx / /
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update -qq && apt-get install -qq --no-install-recommends \
|
||||
make \
|
||||
git \
|
||||
jq \
|
||||
curl \
|
||||
dpkg-dev
|
||||
ARG TARGETARCH
|
||||
# libbtrfs: for containerd
|
||||
|
@ -73,11 +76,12 @@ RUN xx-apt-get update -qq && xx-apt-get install -qq --no-install-recommends \
|
|||
pkg-config
|
||||
RUN git config --global advice.detachedHead false
|
||||
ADD hack/git-checkout-tag-with-hash.sh /usr/local/bin/
|
||||
ADD hack/scripts/lib.sh /usr/local/bin/http::helper
|
||||
|
||||
FROM build-base-debian AS build-containerd
|
||||
FROM build-base AS build-containerd
|
||||
ARG TARGETARCH
|
||||
ARG CONTAINERD_VERSION
|
||||
RUN git clone --quiet --depth 1 --branch "${CONTAINERD_VERSION%@*}" https://github.com/containerd/containerd.git /go/src/github.com/containerd/containerd
|
||||
RUN git clone --quiet --depth 1 --branch "${CONTAINERD_VERSION%%@*}" https://github.com/containerd/containerd.git /go/src/github.com/containerd/containerd
|
||||
WORKDIR /go/src/github.com/containerd/containerd
|
||||
RUN git-checkout-tag-with-hash.sh ${CONTAINERD_VERSION} && \
|
||||
mkdir -p /out /out/$TARGETARCH && \
|
||||
|
@ -85,10 +89,10 @@ RUN git-checkout-tag-with-hash.sh ${CONTAINERD_VERSION} && \
|
|||
RUN GO=xx-go make STATIC=1 && \
|
||||
cp -a bin/containerd bin/containerd-shim-runc-v2 bin/ctr /out/$TARGETARCH
|
||||
|
||||
FROM build-base-debian AS build-runc
|
||||
FROM build-base AS build-runc
|
||||
ARG RUNC_VERSION
|
||||
ARG TARGETARCH
|
||||
RUN git clone --quiet --depth 1 --branch "${RUNC_VERSION%@*}" https://github.com/opencontainers/runc.git /go/src/github.com/opencontainers/runc
|
||||
RUN git clone --quiet --depth 1 --branch "${RUNC_VERSION%%@*}" https://github.com/opencontainers/runc.git /go/src/github.com/opencontainers/runc
|
||||
WORKDIR /go/src/github.com/opencontainers/runc
|
||||
RUN git-checkout-tag-with-hash.sh ${RUNC_VERSION} && \
|
||||
mkdir -p /out
|
||||
|
@ -96,10 +100,10 @@ ENV CGO_ENABLED=1
|
|||
RUN GO=xx-go CC=$(xx-info)-gcc STRIP=$(xx-info)-strip make static && \
|
||||
xx-verify --static runc && cp -v -a runc /out/runc.${TARGETARCH}
|
||||
|
||||
FROM build-base-debian AS build-bypass4netns
|
||||
FROM build-base AS build-bypass4netns
|
||||
ARG BYPASS4NETNS_VERSION
|
||||
ARG TARGETARCH
|
||||
RUN git clone --quiet --depth 1 --branch "${BYPASS4NETNS_VERSION%@*}" https://github.com/rootless-containers/bypass4netns.git /go/src/github.com/rootless-containers/bypass4netns
|
||||
RUN git clone --quiet --depth 1 --branch "${BYPASS4NETNS_VERSION%%@*}" https://github.com/rootless-containers/bypass4netns.git /go/src/github.com/rootless-containers/bypass4netns
|
||||
WORKDIR /go/src/github.com/rootless-containers/bypass4netns
|
||||
RUN git-checkout-tag-with-hash.sh ${BYPASS4NETNS_VERSION} && \
|
||||
mkdir -p /out/${TARGETARCH}
|
||||
|
@ -107,20 +111,20 @@ ENV CGO_ENABLED=1
|
|||
RUN GO=xx-go make static && \
|
||||
xx-verify --static bypass4netns && cp -a bypass4netns bypass4netnsd /out/${TARGETARCH}
|
||||
|
||||
FROM build-base-debian AS build-gomodjail
|
||||
FROM build-base AS build-gomodjail
|
||||
ARG GOMODJAIL_VERSION
|
||||
ARG TARGETARCH
|
||||
RUN git clone --quiet --depth 1 --branch "${GOMODJAIL_VERSION%@*}" https://github.com/AkihiroSuda/gomodjail.git /go/src/github.com/AkihiroSuda/gomodjail
|
||||
RUN git clone --quiet --depth 1 --branch "${GOMODJAIL_VERSION%%@*}" https://github.com/AkihiroSuda/gomodjail.git /go/src/github.com/AkihiroSuda/gomodjail
|
||||
WORKDIR /go/src/github.com/AkihiroSuda/gomodjail
|
||||
RUN git-checkout-tag-with-hash.sh ${GOMODJAIL_VERSION} && \
|
||||
mkdir -p /out/${TARGETARCH}
|
||||
RUN GO=xx-go make STATIC=1 && \
|
||||
xx-verify --static _output/bin/gomodjail && cp -a _output/bin/gomodjail /out/${TARGETARCH}
|
||||
|
||||
FROM build-base-debian AS build-kubo
|
||||
FROM build-base AS build-kubo
|
||||
ARG KUBO_VERSION
|
||||
ARG TARGETARCH
|
||||
RUN git clone --quiet --depth 1 --branch "${KUBO_VERSION%@*}" https://github.com/ipfs/kubo.git /go/src/github.com/ipfs/kubo
|
||||
RUN git clone --quiet --depth 1 --branch "${KUBO_VERSION%%@*}" https://github.com/ipfs/kubo.git /go/src/github.com/ipfs/kubo
|
||||
WORKDIR /go/src/github.com/ipfs/kubo
|
||||
RUN git-checkout-tag-with-hash.sh ${KUBO_VERSION} && \
|
||||
mkdir -p /out/${TARGETARCH}
|
||||
|
@ -129,11 +133,6 @@ RUN xx-go --wrap && \
|
|||
make build && \
|
||||
xx-verify --static cmd/ipfs/ipfs && cp -a cmd/ipfs/ipfs /out/${TARGETARCH}
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS build-base
|
||||
RUN apk add --no-cache make git curl
|
||||
RUN git config --global advice.detachedHead false
|
||||
ADD hack/git-checkout-tag-with-hash.sh /usr/local/bin/
|
||||
|
||||
FROM build-base AS build-minimal
|
||||
RUN BINDIR=/out/bin make binaries install
|
||||
# We do not set CMD to `go test` here, because it requires systemd
|
||||
|
@ -148,12 +147,12 @@ RUN mkdir -p /out/share/doc/nerdctl-full && touch /out/share/doc/nerdctl-full/RE
|
|||
ARG CONTAINERD_VERSION
|
||||
COPY --from=build-containerd /out/${TARGETARCH:-amd64}/* /out/bin/
|
||||
COPY --from=build-containerd /out/containerd.service /out/lib/systemd/system/containerd.service
|
||||
RUN echo "- containerd: ${CONTAINERD_VERSION/@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
RUN echo "- containerd: ${CONTAINERD_VERSION%%@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG RUNC_VERSION
|
||||
COPY --from=build-runc /out/runc.${TARGETARCH:-amd64} /out/bin/runc
|
||||
RUN echo "- runc: ${RUNC_VERSION/@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
RUN echo "- runc: ${RUNC_VERSION%%@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG CNI_PLUGINS_VERSION
|
||||
RUN CNI_PLUGINS_VERSION=${CNI_PLUGINS_VERSION/@BINARY}; \
|
||||
RUN CNI_PLUGINS_VERSION=${CNI_PLUGINS_VERSION%%@*}; \
|
||||
fname="cni-plugins-${TARGETOS:-linux}-${TARGETARCH:-amd64}-${CNI_PLUGINS_VERSION}.tgz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/cni-plugins-${CNI_PLUGINS_VERSION}" | sha256sum -c && \
|
||||
|
@ -162,7 +161,7 @@ RUN CNI_PLUGINS_VERSION=${CNI_PLUGINS_VERSION/@BINARY}; \
|
|||
rm -f "${fname}" && \
|
||||
echo "- CNI plugins: ${CNI_PLUGINS_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG BUILDKIT_VERSION
|
||||
RUN BUILDKIT_VERSION=${BUILDKIT_VERSION/@BINARY}; \
|
||||
RUN BUILDKIT_VERSION=${BUILDKIT_VERSION%%@*}; \
|
||||
fname="buildkit-${BUILDKIT_VERSION}.${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/moby/buildkit/releases/download/${BUILDKIT_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/buildkit-${BUILDKIT_VERSION}" | sha256sum -c && \
|
||||
|
@ -177,10 +176,11 @@ RUN cd /out/lib/systemd/system && \
|
|||
echo "" >> buildkit.service && \
|
||||
echo "# This file was converted from containerd.service, with \`sed -E '${sedcomm}'\`" >> buildkit.service
|
||||
ARG STARGZ_SNAPSHOTTER_VERSION
|
||||
RUN STARGZ_SNAPSHOTTER_VERSION=${STARGZ_SNAPSHOTTER_VERSION/@BINARY}; \
|
||||
RUN --mount=type=secret,id=github_token,env=GITHUB_TOKEN \
|
||||
STARGZ_SNAPSHOTTER_VERSION=${STARGZ_SNAPSHOTTER_VERSION%%@*}; \
|
||||
fname="stargz-snapshotter-${STARGZ_SNAPSHOTTER_VERSION}-${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/containerd/stargz-snapshotter/releases/download/${STARGZ_SNAPSHOTTER_VERSION}/${fname}" && \
|
||||
curl -o "stargz-snapshotter.service" -fsSL --proto '=https' --tlsv1.2 "https://raw.githubusercontent.com/containerd/stargz-snapshotter/${STARGZ_SNAPSHOTTER_VERSION}/script/config/etc/systemd/system/stargz-snapshotter.service" && \
|
||||
http::helper github::file containerd/stargz-snapshotter script/config/etc/systemd/system/stargz-snapshotter.service "${STARGZ_SNAPSHOTTER_VERSION}" > "stargz-snapshotter.service" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/stargz-snapshotter-${STARGZ_SNAPSHOTTER_VERSION}" | sha256sum -c - && \
|
||||
grep "stargz-snapshotter.service" "/SHA256SUMS.d/stargz-snapshotter-${STARGZ_SNAPSHOTTER_VERSION}" | sha256sum -c - && \
|
||||
tar xzf "${fname}" -C /out/bin && \
|
||||
|
@ -188,13 +188,13 @@ RUN STARGZ_SNAPSHOTTER_VERSION=${STARGZ_SNAPSHOTTER_VERSION/@BINARY}; \
|
|||
mv stargz-snapshotter.service /out/lib/systemd/system/stargz-snapshotter.service && \
|
||||
echo "- Stargz Snapshotter: ${STARGZ_SNAPSHOTTER_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG IMGCRYPT_VERSION
|
||||
RUN git clone --quiet --depth 1 --branch "${IMGCRYPT_VERSION%@*}" https://github.com/containerd/imgcrypt.git /go/src/github.com/containerd/imgcrypt && \
|
||||
RUN git clone --quiet --depth 1 --branch "${IMGCRYPT_VERSION%%@*}" https://github.com/containerd/imgcrypt.git /go/src/github.com/containerd/imgcrypt && \
|
||||
cd /go/src/github.com/containerd/imgcrypt && \
|
||||
git-checkout-tag-with-hash.sh "${IMGCRYPT_VERSION}" && \
|
||||
CGO_ENABLED=0 make && DESTDIR=/out make install && \
|
||||
echo "- imgcrypt: ${IMGCRYPT_VERSION/@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
echo "- imgcrypt: ${IMGCRYPT_VERSION%%@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG SLIRP4NETNS_VERSION
|
||||
RUN SLIRP4NETNS_VERSION=${SLIRP4NETNS_VERSION/@BINARY}; \
|
||||
RUN SLIRP4NETNS_VERSION=${SLIRP4NETNS_VERSION%%@*}; \
|
||||
fname="slirp4netns-$(cat /target_uname_m)" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/rootless-containers/slirp4netns/releases/download/${SLIRP4NETNS_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/slirp4netns-${SLIRP4NETNS_VERSION}" | sha256sum -c && \
|
||||
|
@ -203,9 +203,9 @@ RUN SLIRP4NETNS_VERSION=${SLIRP4NETNS_VERSION/@BINARY}; \
|
|||
echo "- slirp4netns: ${SLIRP4NETNS_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG BYPASS4NETNS_VERSION
|
||||
COPY --from=build-bypass4netns /out/${TARGETARCH:-amd64}/* /out/bin/
|
||||
RUN echo "- bypass4netns: ${BYPASS4NETNS_VERSION/@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
RUN echo "- bypass4netns: ${BYPASS4NETNS_VERSION%%@*}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG FUSE_OVERLAYFS_VERSION
|
||||
RUN FUSE_OVERLAYFS_VERSION=${FUSE_OVERLAYFS_VERSION/@BINARY}; \
|
||||
RUN FUSE_OVERLAYFS_VERSION=${FUSE_OVERLAYFS_VERSION%%@*}; \
|
||||
fname="fuse-overlayfs-$(cat /target_uname_m)" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/containers/fuse-overlayfs/releases/download/${FUSE_OVERLAYFS_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/fuse-overlayfs-${FUSE_OVERLAYFS_VERSION}" | sha256sum -c && \
|
||||
|
@ -213,22 +213,24 @@ RUN FUSE_OVERLAYFS_VERSION=${FUSE_OVERLAYFS_VERSION/@BINARY}; \
|
|||
chmod +x /out/bin/fuse-overlayfs && \
|
||||
echo "- fuse-overlayfs: ${FUSE_OVERLAYFS_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG CONTAINERD_FUSE_OVERLAYFS_VERSION
|
||||
RUN CONTAINERD_FUSE_OVERLAYFS_VERSION=${CONTAINERD_FUSE_OVERLAYFS_VERSION/@BINARY}; \
|
||||
fname="containerd-fuse-overlayfs-${CONTAINERD_FUSE_OVERLAYFS_VERSION/v}-${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
|
||||
RUN CONTAINERD_FUSE_OVERLAYFS_VERSION=${CONTAINERD_FUSE_OVERLAYFS_VERSION%%@*}; \
|
||||
fname="containerd-fuse-overlayfs-${CONTAINERD_FUSE_OVERLAYFS_VERSION##*v}-${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/containerd/fuse-overlayfs-snapshotter/releases/download/${CONTAINERD_FUSE_OVERLAYFS_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/containerd-fuse-overlayfs-${CONTAINERD_FUSE_OVERLAYFS_VERSION}" | sha256sum -c && \
|
||||
tar xzf "${fname}" -C /out/bin && \
|
||||
rm -f "${fname}" && \
|
||||
echo "- containerd-fuse-overlayfs: ${CONTAINERD_FUSE_OVERLAYFS_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG TINI_VERSION
|
||||
RUN TINI_VERSION=${TINI_VERSION/@BINARY}; \
|
||||
RUN TINI_VERSION=${TINI_VERSION%%@*}; \
|
||||
fname="tini-static-${TARGETARCH:-amd64}" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/tini-${TINI_VERSION}" | sha256sum -c && \
|
||||
cp -a "${fname}" /out/bin/tini && chmod +x /out/bin/tini && \
|
||||
echo "- Tini: ${TINI_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG BUILDG_VERSION
|
||||
RUN BUILDG_VERSION=${BUILDG_VERSION/@BINARY}; \
|
||||
# FIXME: this is a mildly-confusing approach. Buildkit will perform some "smart" replacement at build time and output
|
||||
# confusing debugging information, eg: BUILDG_VERSION will appear as if the original ARG value was used.
|
||||
RUN BUILDG_VERSION=${BUILDG_VERSION%%@*}; \
|
||||
fname="buildg-${BUILDG_VERSION}-${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/ktock/buildg/releases/download/${BUILDG_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/buildg-${BUILDG_VERSION}" | sha256sum -c && \
|
||||
|
@ -236,7 +238,7 @@ RUN BUILDG_VERSION=${BUILDG_VERSION/@BINARY}; \
|
|||
rm -f "${fname}" && \
|
||||
echo "- buildg: ${BUILDG_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG ROOTLESSKIT_VERSION
|
||||
RUN ROOTLESSKIT_VERSION=${ROOTLESSKIT_VERSION/@BINARY}; \
|
||||
RUN ROOTLESSKIT_VERSION=${ROOTLESSKIT_VERSION%%@*}; \
|
||||
fname="rootlesskit-$(cat /target_uname_m).tar.gz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/rootless-containers/rootlesskit/releases/download/${ROOTLESSKIT_VERSION}/${fname}" && \
|
||||
grep "${fname}" "/SHA256SUMS.d/rootlesskit-${ROOTLESSKIT_VERSION}" | sha256sum -c && \
|
||||
|
@ -246,13 +248,17 @@ RUN ROOTLESSKIT_VERSION=${ROOTLESSKIT_VERSION/@BINARY}; \
|
|||
ARG GOMODJAIL_VERSION
|
||||
COPY --from=build-gomodjail /out/${TARGETARCH:-amd64}/* /out/bin/
|
||||
RUN echo "- gomodjail: ${GOMODJAIL_VERSION}" >> /out/share/doc/nerdctl-full/README.md
|
||||
ARG CONTAINERIZED_SYSTEMD_VERSION
|
||||
RUN --mount=type=secret,id=github_token,env=GITHUB_TOKEN \
|
||||
http::helper github::file AkihiroSuda/containerized-systemd docker-entrypoint.sh "${CONTAINERIZED_SYSTEMD_VERSION}" > /docker-entrypoint.sh && \
|
||||
chmod +x /docker-entrypoint.sh
|
||||
|
||||
RUN echo "" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "## License" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/slirp4netns: [GNU GENERAL PUBLIC LICENSE, Version 2](https://github.com/rootless-containers/slirp4netns/blob/${SLIRP4NETNS_VERSION/@*}/COPYING)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/fuse-overlayfs: [GNU GENERAL PUBLIC LICENSE, Version 2](https://github.com/containers/fuse-overlayfs/blob/${FUSE_OVERLAYFS_VERSION/@*}/COPYING)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/slirp4netns: [GNU GENERAL PUBLIC LICENSE, Version 2](https://github.com/rootless-containers/slirp4netns/blob/${SLIRP4NETNS_VERSION%%@*}/COPYING)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/fuse-overlayfs: [GNU GENERAL PUBLIC LICENSE, Version 2](https://github.com/containers/fuse-overlayfs/blob/${FUSE_OVERLAYFS_VERSION%%@*}/COPYING)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/{runc,bypass4netns,bypass4netnsd}: Apache License 2.0, statically linked with libseccomp ([LGPL 2.1](https://github.com/seccomp/libseccomp/blob/main/LICENSE), source code available at https://github.com/seccomp/libseccomp/)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/tini: [MIT License](https://github.com/krallin/tini/blob/${TINI_VERSION/@*}/LICENSE)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- bin/tini: [MIT License](https://github.com/krallin/tini/blob/${TINI_VERSION%%@*}/LICENSE)" >> /out/share/doc/nerdctl-full/README.md && \
|
||||
echo "- Other files: [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)" >> /out/share/doc/nerdctl-full/README.md
|
||||
|
||||
FROM build-dependencies AS build-full
|
||||
|
@ -282,9 +288,7 @@ RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \
|
|||
iproute2 iptables \
|
||||
dbus dbus-user-session systemd systemd-sysv \
|
||||
fuse3
|
||||
ARG CONTAINERIZED_SYSTEMD_VERSION
|
||||
RUN curl -o /docker-entrypoint.sh -fsSL --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/AkihiroSuda/containerized-systemd/${CONTAINERIZED_SYSTEMD_VERSION}/docker-entrypoint.sh && \
|
||||
chmod +x /docker-entrypoint.sh
|
||||
COPY --from=build-full /docker-entrypoint.sh /docker-entrypoint.sh
|
||||
COPY --from=out-full / /usr/local/
|
||||
RUN perl -pi -e 's/multi-user.target/docker-entrypoint.target/g' /usr/local/lib/systemd/system/*.service && \
|
||||
systemctl enable containerd buildkit stargz-snapshotter && \
|
||||
|
@ -310,7 +314,7 @@ RUN apt-get update -qq && apt-get install -qq --no-install-recommends \
|
|||
git \
|
||||
make
|
||||
# We wouldn't need this if Docker Hub could have "golang:${GO_VERSION}-ubuntu"
|
||||
COPY --from=build-base-debian /usr/local/go /usr/local/go
|
||||
COPY --from=build-base /usr/local/go /usr/local/go
|
||||
ARG TARGETARCH
|
||||
ENV PATH=/usr/local/go/bin:$PATH
|
||||
ARG GOTESTSUM_VERSION
|
||||
|
@ -325,7 +329,10 @@ COPY --from=ghcr.io/sigstore/cosign/cosign:v2.2.3@sha256:8fc9cad121611e8479f65f7
|
|||
ARG SOCI_SNAPSHOTTER_VERSION
|
||||
RUN fname="soci-snapshotter-${SOCI_SNAPSHOTTER_VERSION}-${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
|
||||
curl -o "${fname}" -fsSL --proto '=https' --tlsv1.2 "https://github.com/awslabs/soci-snapshotter/releases/download/v${SOCI_SNAPSHOTTER_VERSION}/${fname}" && \
|
||||
tar -C /usr/local/bin -xvf "${fname}" soci soci-snapshotter-grpc
|
||||
tar -C /usr/local/bin -xvf "${fname}" soci soci-snapshotter-grpc && \
|
||||
mkdir -p /etc/soci-snapshotter-grpc && \
|
||||
touch /etc/soci-snapshotter-grpc/config.toml && \
|
||||
echo "\n[pull_modes]\n [pull_modes.soci_v1]\n enable = true" >> /etc/soci-snapshotter-grpc/config.toml
|
||||
# enable offline ipfs for integration test
|
||||
COPY --from=build-kubo /out/${TARGETARCH:-amd64}/* /usr/local/bin/
|
||||
COPY ./Dockerfile.d/test-integration-etc_containerd-stargz-grpc_config.toml /etc/containerd-stargz-grpc/config.toml
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
70371949ac56d118e55306091640e63537069a538a97c151eb7475c07cb5a8a4 buildg-v0.5.2-linux-amd64.tar.gz
|
||||
9c44a5f8ecc3035998a07e1c564338205700cf5287c723e8ccba1da2815168cc buildg-v0.5.2-linux-arm64.tar.gz
|
|
@ -0,0 +1,4 @@
|
|||
cf4c40c58ca795eeb6e75e2c6a0e5bb3a6a9c0623d51bc3b85163e5d483eeade buildg-full-v0.5.3-linux-amd64.tar.gz
|
||||
47c479f2e5150c9c76294fa93a03ad20e5928f4315bf52ca8432bfb6707d4276 buildg-full-v0.5.3-linux-arm64.tar.gz
|
||||
c289a454ae8673ff99acf56dec9ba97274c20d2015e80f7ac3b8eb8e4f77888f buildg-v0.5.3-linux-amd64.tar.gz
|
||||
b2e244250ce7ea5c090388f2025a9c546557861d25bba7b0666aa512f01fa6cd buildg-v0.5.3-linux-arm64.tar.gz
|
|
@ -1,2 +0,0 @@
|
|||
e0d83a631a48f13232fcee71cbd913e6b11dbde0a45985fa1b99af27ab97086e buildkit-v0.21.1.linux-amd64.tar.gz
|
||||
7652a05f2961c386ea6e65c4701daa0e5a899a20c77596cd5f0eca02851dc1f6 buildkit-v0.21.1.linux-arm64.tar.gz
|
|
@ -0,0 +1,2 @@
|
|||
2771c3403e3a1f75a83cde387a05365794d3b900c355e864772a36c3ce541f82 buildkit-v0.23.2.linux-amd64.tar.gz
|
||||
6385ff70b2fb4134b50ac3183eea3a0b06c6f6129173940d73178ae0477368f1 buildkit-v0.23.2.linux-arm64.tar.gz
|
|
@ -1,6 +0,0 @@
|
|||
acc149d60e2fad0cff480852c82f39bdaae2eb6faa265b2028c944ec572014f9 containerd-fuse-overlayfs-2.1.5-linux-amd64.tar.gz
|
||||
2c1c12a99ac16e6ad137c474517d04cc7864d26d9045f50f99a6d6e887b9c425 containerd-fuse-overlayfs-2.1.5-linux-arm-v7.tar.gz
|
||||
17759de9588cda1499877cc9587189eb24731ae41edda201087fd74658ddc127 containerd-fuse-overlayfs-2.1.5-linux-arm64.tar.gz
|
||||
ce0310573fd667a2fa348588b12f1867a1bad5befc79d7d39e6419a7d4687ea8 containerd-fuse-overlayfs-2.1.5-linux-ppc64le.tar.gz
|
||||
e9bbb9835346d8007a6429151eb7c7b23fa1f20b85aa6d20dd3702cb5a4c038a containerd-fuse-overlayfs-2.1.5-linux-riscv64.tar.gz
|
||||
c088a7eee9b75f0a759e52d1ae2c8d69d21265594070f41021a94523d1c7bab1 containerd-fuse-overlayfs-2.1.5-linux-s390x.tar.gz
|
|
@ -0,0 +1,6 @@
|
|||
8a768e4c953251d32b5e5d748d17593f7150834caaba403b483cf83f5856fea3 containerd-fuse-overlayfs-2.1.6-linux-amd64.tar.gz
|
||||
a3af866a12e913cd1d4dda8e41c08345eca928a15ac1d466fdb2b00b013e14ee containerd-fuse-overlayfs-2.1.6-linux-arm-v7.tar.gz
|
||||
417ca0c838e43e446f498b384d73f7caaeb00dc4c1c0fe4b0ecfdd36fd355daa containerd-fuse-overlayfs-2.1.6-linux-arm64.tar.gz
|
||||
5fdebd9fb7b50473318f0410bc3ab46f3388ac8aa586b45c91a314af9ce6569c containerd-fuse-overlayfs-2.1.6-linux-ppc64le.tar.gz
|
||||
7e1a9d2ba68ff31a8dfb53bf6e71b2879063b13c759922c8cff3013893829bca containerd-fuse-overlayfs-2.1.6-linux-riscv64.tar.gz
|
||||
3c022651cdaff666e88996d5d9c7e776bf59419a03d7d718a28aa708036419f9 containerd-fuse-overlayfs-2.1.6-linux-s390x.tar.gz
|
|
@ -1,7 +0,0 @@
|
|||
b4162d27bbbd3683ca8ee57b51a1b270c0054b3a15fcc1830a5d7c10b77ad045 SOURCE_DATE_EPOCH
|
||||
c55117faa5e18345a3ee1515267f056822ff0c1897999ae5422b0114ee48df85 slirp4netns-aarch64
|
||||
f55a6c9e3ec8280e9c3cec083f07dc124e2846ce8139a9281c35013e968d7e95 slirp4netns-armv7l
|
||||
7b388a9cacbd89821f7f7a6457470fcae8f51aa846162521589feb4634ec7586 slirp4netns-ppc64le
|
||||
041f9fe507510de1fbb802933a6add093ff19f941185965295c81f2ba4fc9cec slirp4netns-riscv64
|
||||
aa39cf14414ae53dbff6b79dfdfa55b5ff8ac5250e2261804863cd365b33a818 slirp4netns-s390x
|
||||
4d55a3658ae259e3e74bb75cf058eb05d6e39ad6bbe170ca8e94c2462bea0eb1 slirp4netns-x86_64
|
|
@ -0,0 +1,7 @@
|
|||
d0e6a13342efbedb8b7454629a0e9ce9b7a937c261034c85f46ed81af76307d8 SOURCE_DATE_EPOCH
|
||||
1ca9d2f5f1fb4beb91f354653e5dad35b95c049afb264268d99a96ff2a10d903 slirp4netns-aarch64
|
||||
3e209d1c56fccbe627a038d311b233c15e8d914b30f9b981b5ed78b98e836859 slirp4netns-armv7l
|
||||
4d1003a98103ee170c0fcd4aad8a5e0ba7aa2e70fbca883cbb6a39f40447c8da slirp4netns-ppc64le
|
||||
06a13b398d88120097b20dace966d7dd5e2fbfd284b95a086347808df392200e slirp4netns-riscv64
|
||||
23d4a206edd6d3fc9c86f8b05c0881ff77a607b8d471f20964ad9f9c3f3176b1 slirp4netns-s390x
|
||||
5618887b671a30a2f7548f2bdf7fba98a53981abc80cfd3183cd28b4dc8b2b97 slirp4netns-x86_64
|
|
@ -22,6 +22,7 @@
|
|||
# GitHub ID, Name, Email address, GPG fingerprint
|
||||
"jsturtevant","James Sturtevant","jstur@microsoft.com",""
|
||||
"manugupt1", "Manu Gupta", "manugupt1@gmail.com","FCA9 504A 4118 EA5C F466 CC30 A5C3 A8F4 E7FE 9E10"
|
||||
"Shubhranshu153","Shubharanshu Mahapatra","shubhum@amazon.com",""
|
||||
|
||||
# EMERITUS
|
||||
# See EMERITUS.md
|
||||
|
|
17
Makefile
17
Makefile
|
@ -46,6 +46,9 @@ LINT_COMMIT_RANGE ?= main..HEAD
|
|||
GO_BUILD_LDFLAGS ?= -s -w
|
||||
GO_BUILD_FLAGS ?=
|
||||
|
||||
BUILDTAGS ?=
|
||||
GO_TAGS=$(if $(BUILDTAGS),-tags "$(strip $(BUILDTAGS))",)
|
||||
|
||||
##########################
|
||||
# Helpers
|
||||
##########################
|
||||
|
@ -54,7 +57,7 @@ ifdef VERBOSE
|
|||
VERBOSE_FLAG_LONG := --verbose
|
||||
endif
|
||||
|
||||
export GO_BUILD=CGO_ENABLED=0 GOOS=$(GOOS) $(GO) -C $(MAKEFILE_DIR) build -ldflags "$(GO_BUILD_LDFLAGS) $(VERBOSE_FLAG) -X $(PACKAGE)/pkg/version.Version=$(VERSION) -X $(PACKAGE)/pkg/version.Revision=$(REVISION)"
|
||||
export GO_BUILD=CGO_ENABLED=0 GOOS=$(GOOS) $(GO) -C $(MAKEFILE_DIR) build $(GO_TAGS) -ldflags "$(GO_BUILD_LDFLAGS) $(VERBOSE_FLAG) -X $(PACKAGE)/pkg/version.Version=$(VERSION) -X $(PACKAGE)/pkg/version.Revision=$(REVISION)"
|
||||
|
||||
ifndef NO_COLOR
|
||||
NC := \033[0m
|
||||
|
@ -182,7 +185,7 @@ lint-licenses-all:
|
|||
&& GOOS=linux make lint-licenses \
|
||||
&& GOOS=windows make lint-licenses \
|
||||
&& GOOS=freebsd make lint-licenses \
|
||||
&& GOOS=darwin make lint-go
|
||||
&& GOOS=darwin make lint-licenses
|
||||
$(call footer, $@)
|
||||
|
||||
##########################
|
||||
|
@ -200,7 +203,7 @@ fix-go-all:
|
|||
&& GOOS=linux make fix-go \
|
||||
&& GOOS=windows make fix-go \
|
||||
&& GOOS=freebsd make fix-go \
|
||||
&& GOOS=darwin make lint-go
|
||||
&& GOOS=darwin make fix-go
|
||||
$(call footer, $@)
|
||||
|
||||
fix-mod:
|
||||
|
@ -218,12 +221,14 @@ install-dev-tools:
|
|||
# git-validation: main (2025-02-25)
|
||||
# ltag: main (2025-03-04)
|
||||
# go-licenses: v2.0.0-alpha.1 (2024-06-27)
|
||||
# stubbing go-licenses with dependency upgrade due to non-compatibility with golang 1.25rc1
|
||||
# Issue: https://github.com/google/go-licenses/issues/312
|
||||
@cd $(MAKEFILE_DIR) \
|
||||
&& go install github.com/Shubhranshu153/go-licenses/v2@f8c503d1357dffb6c97ed3b94e912ab294dde24a \
|
||||
&& go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@2b224c2cf4c9f261c22a16af7f8ca6408467f338 \
|
||||
&& go install github.com/vbatts/git-validation@7b60e35b055dd2eab5844202ffffad51d9c93922 \
|
||||
&& go install github.com/containerd/ltag@66e6a514664ee2d11a470735519fa22b1a9eaabd \
|
||||
&& go install github.com/google/go-licenses/v2@d01822334fba5896920a060f762ea7ecdbd086e8 \
|
||||
&& go install gotest.tools/gotestsum@ac6dad9c7d87b969004f7749d1942938526c9716
|
||||
&& go install gotest.tools/gotestsum@0d9599e513d70e5792bb9334869f82f6e8b53d4d
|
||||
@echo "Remember to add \$$HOME/go/bin to your path"
|
||||
$(call footer, $@)
|
||||
|
||||
|
@ -253,7 +258,7 @@ TAR_OWNER0_FLAGS=--owner=0 --group=0
|
|||
TAR_FLATTEN_FLAGS=--transform 's/.*\///g'
|
||||
|
||||
define make_artifact_full_linux
|
||||
$(DOCKER) build --output type=tar,dest=$(CURDIR)/_output/nerdctl-full-$(VERSION_TRIMMED)-linux-$(1).tar --target out-full --platform $(1) --build-arg GO_VERSION -f $(MAKEFILE_DIR)/Dockerfile $(MAKEFILE_DIR)
|
||||
$(DOCKER) build --secret id=github_token,env=GITHUB_TOKEN --output type=tar,dest=$(CURDIR)/_output/nerdctl-full-$(VERSION_TRIMMED)-linux-$(1).tar --target out-full --platform $(1) --build-arg GO_VERSION -f $(MAKEFILE_DIR)/Dockerfile $(MAKEFILE_DIR)
|
||||
gzip -9 $(CURDIR)/_output/nerdctl-full-$(VERSION_TRIMMED)-linux-$(1).tar
|
||||
endef
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ package builder
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -263,13 +262,6 @@ func GetBuildkitHost(cmd *cobra.Command, namespace string) (string, error) {
|
|||
return buildkitHost, nil
|
||||
}
|
||||
|
||||
if buildkitHost := os.Getenv("BUILDKIT_HOST"); buildkitHost != "" {
|
||||
if err := buildkitutil.PingBKDaemon(buildkitHost); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buildkitHost, nil
|
||||
|
||||
}
|
||||
return buildkitutil.GetBuildkitHost(namespace)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -100,14 +101,13 @@ CMD ["echo", "test-nerdctl-build-context-oci-layout"]`
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(
|
||||
t,
|
||||
strings.Contains(
|
||||
helpers.Capture("run", "--rm", data.Identifier("child")),
|
||||
"test-nerdctl-build-context-oci-layout",
|
||||
),
|
||||
info,
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ package builder
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -29,6 +31,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/platformutil"
|
||||
|
@ -110,6 +113,7 @@ CMD ["echo", "nerdctl-build-test-string"]`, testutil.CommonImage)
|
|||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rmi", "-f", data.Identifier("ignored"))
|
||||
helpers.Anyhow("rmi", "-f", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, nil, nil),
|
||||
},
|
||||
|
@ -339,7 +343,7 @@ COPY %s /`, testFileName)
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
// Expecting testFileName to exist inside the output target directory
|
||||
assert.Equal(t, data.Temp().Load(testFileName), testContent, "file content is identical")
|
||||
},
|
||||
|
@ -353,7 +357,7 @@ COPY %s /`, testFileName)
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Equal(t, data.Temp().Load(testFileName), testContent, "file content is identical")
|
||||
},
|
||||
}
|
||||
|
@ -850,8 +854,9 @@ RUN curl -I http://google.com
|
|||
func TestBuildAttestation(t *testing.T) {
|
||||
nerdtest.Setup()
|
||||
|
||||
const testSBOMFileName = "sbom.spdx.json"
|
||||
const testProvenanceFileName = "provenance.json"
|
||||
// Using regex patterns to match SBOM and provenance files with optional platform suffix
|
||||
const testSBOMFilePattern = `sbom\.spdx(?:\.[a-z0-9_]+)?\.json`
|
||||
const testProvenanceFilePattern = `provenance(?:\.[a-z0-9_]+)?\.json`
|
||||
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, testutil.CommonImage)
|
||||
|
||||
|
@ -890,8 +895,18 @@ func TestBuildAttestation(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
data.Temp().Exists("dir-for-bom", testSBOMFileName)
|
||||
Output: func(stdout string, t tig.T) {
|
||||
files, err := os.ReadDir(data.Temp().Path("dir-for-bom"))
|
||||
assert.NilError(t, err, "failed to read directory")
|
||||
|
||||
found := false
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && regexp.MustCompile(testSBOMFilePattern).MatchString(file.Name()) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, found, "no SBOM file matching pattern %s found", testSBOMFilePattern)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -912,8 +927,18 @@ func TestBuildAttestation(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
data.Temp().Exists("dir-for-prov", testProvenanceFileName)
|
||||
Output: func(stdout string, t tig.T) {
|
||||
files, err := os.ReadDir(data.Temp().Path("dir-for-prov"))
|
||||
assert.NilError(t, err, "failed to read directory")
|
||||
|
||||
found := false
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && regexp.MustCompile(testProvenanceFilePattern).MatchString(file.Name()) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, found, "no provenance file matching pattern %s found", testProvenanceFilePattern)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -935,9 +960,29 @@ func TestBuildAttestation(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
data.Temp().Exists("dir-for-attest", testSBOMFileName)
|
||||
data.Temp().Exists("dir-for-attest", testProvenanceFileName)
|
||||
Output: func(stdout string, t tig.T) {
|
||||
// Check if any file in the directory matches the SBOM file pattern
|
||||
files, err := os.ReadDir(data.Temp().Path("dir-for-attest"))
|
||||
assert.NilError(t, err, "failed to read directory")
|
||||
|
||||
sbomFound := false
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && regexp.MustCompile(testSBOMFilePattern).MatchString(file.Name()) {
|
||||
sbomFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, sbomFound, "no SBOM file matching pattern %s found", testSBOMFilePattern)
|
||||
|
||||
// Check if any file in the directory matches the provenance file pattern
|
||||
provenanceFound := false
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && regexp.MustCompile(testProvenanceFilePattern).MatchString(file.Name()) {
|
||||
provenanceFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, provenanceFound, "no provenance file matching pattern %s found", testProvenanceFilePattern)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
@ -152,14 +153,19 @@ CMD ["echo", "nerdctl-builder-debug-test-string"]`, testutil.CommonImage)
|
|||
// FIXME: this test should be rewritten to dynamically retrieve the ids, and use images
|
||||
// available on all platforms
|
||||
oldImage := testutil.BusyboxImage
|
||||
oldImageSha := "7b3ccabffc97de872a30dfd234fd972a66d247c8cfc69b0550f276481852627c"
|
||||
parsedOldImage, err := referenceutil.Parse(oldImage)
|
||||
assert.NilError(helpers.T(), err)
|
||||
oldImageSha := parsedOldImage.Digest.String()
|
||||
|
||||
newImage := testutil.AlpineImage
|
||||
newImageSha := "ec14c7992a97fc11425907e908340c6c3d6ff602f5f13d899e6b7027c9b4133a"
|
||||
parsedNewImage, err := referenceutil.Parse(newImage)
|
||||
assert.NilError(helpers.T(), err)
|
||||
newImageSha := parsedNewImage.Digest.String()
|
||||
|
||||
helpers.Ensure("pull", "--quiet", oldImage)
|
||||
helpers.Ensure("tag", oldImage, newImage)
|
||||
helpers.Ensure("tag", oldImage, parsedNewImage.Domain+"/"+parsedNewImage.Path+":"+parsedNewImage.Tag)
|
||||
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, newImage)
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, parsedNewImage.Domain+"/"+parsedNewImage.Path+":"+parsedNewImage.Tag)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
data.Labels().Set("oldImageSha", oldImageSha)
|
||||
data.Labels().Set("newImageSha", newImageSha)
|
||||
|
|
|
@ -38,6 +38,49 @@ func IPAMDrivers(cmd *cobra.Command, args []string, toComplete string) ([]string
|
|||
return []string{"default", "host-local", "dhcp"}, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func NetworkOptions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
driver, _ := cmd.Flags().GetString("driver")
|
||||
if driver == "" {
|
||||
driver = "bridge"
|
||||
}
|
||||
|
||||
var candidates []string
|
||||
switch driver {
|
||||
case "bridge":
|
||||
candidates = []string{
|
||||
"mtu=",
|
||||
"com.docker.network.driver.mtu=",
|
||||
"ip-masq=",
|
||||
"com.docker.network.bridge.enable_ip_masquerade=",
|
||||
}
|
||||
case "macvlan":
|
||||
candidates = []string{
|
||||
"mtu=",
|
||||
"com.docker.network.driver.mtu=",
|
||||
"mode=bridge",
|
||||
"macvlan_mode=bridge",
|
||||
"parent=",
|
||||
}
|
||||
case "ipvlan":
|
||||
candidates = []string{
|
||||
"mtu=",
|
||||
"com.docker.network.driver.mtu=",
|
||||
"mode=l2",
|
||||
"mode=l3",
|
||||
"ipvlan_mode=l2",
|
||||
"ipvlan_mode=l3",
|
||||
"parent=",
|
||||
}
|
||||
default:
|
||||
candidates = []string{
|
||||
"mtu=",
|
||||
"com.docker.network.driver.mtu=",
|
||||
"parent=",
|
||||
}
|
||||
}
|
||||
return candidates, cobra.ShellCompDirectiveNoSpace
|
||||
}
|
||||
|
||||
func NamespaceNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
globalOptions, err := helpers.ProcessRootCmdFlags(cmd)
|
||||
if err != nil {
|
||||
|
|
|
@ -38,3 +38,25 @@ func NetworkDrivers(cmd *cobra.Command, args []string, toComplete string) ([]str
|
|||
func IPAMDrivers(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"default"}, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func NetworkOptions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
driver, _ := cmd.Flags().GetString("driver")
|
||||
if driver == "" {
|
||||
driver = "nat"
|
||||
}
|
||||
|
||||
var candidates []string
|
||||
switch driver {
|
||||
case "nat":
|
||||
candidates = []string{
|
||||
"mtu=",
|
||||
"com.docker.network.driver.mtu=",
|
||||
}
|
||||
default:
|
||||
candidates = []string{
|
||||
"mtu=",
|
||||
"com.docker.network.driver.mtu=",
|
||||
}
|
||||
}
|
||||
return candidates, cobra.ShellCompDirectiveNoSpace
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
)
|
||||
|
||||
func TestComposeBuild(t *testing.T) {
|
||||
dockerfile := "FROM " + testutil.AlpineImage
|
||||
dockerfile := "FROM " + testutil.CommonImage
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
|
@ -46,15 +46,11 @@ services:
|
|||
svc0:
|
||||
build: .
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
depends_on:
|
||||
- svc1
|
||||
svc1:
|
||||
build: .
|
||||
image: %s
|
||||
ports:
|
||||
- 8081:80
|
||||
`, imageSvc0, imageSvc1)
|
||||
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
|
|
|
@ -24,16 +24,19 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeConfig(t *testing.T) {
|
||||
const dockerComposeYAML = `
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
services:
|
||||
hello:
|
||||
image: alpine:3.13
|
||||
`
|
||||
image: %s
|
||||
`, testutil.CommonImage)
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
|
@ -111,7 +114,7 @@ services:
|
|||
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(t, data.Labels().Get("hash") != stdout, "hash should be different")
|
||||
},
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -31,8 +32,6 @@ import (
|
|||
|
||||
func TestComposeCopy(t *testing.T) {
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -79,7 +78,7 @@ services:
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
copied := data.Temp().Load("test-file2")
|
||||
assert.Equal(t, copied, testFileContent)
|
||||
},
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -32,12 +33,10 @@ import (
|
|||
|
||||
func TestComposeCreate(t *testing.T) {
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
|
@ -66,7 +65,7 @@ services:
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "ps", "svc0", "-a")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout, info string, t *testing.T) {
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout string, t tig.T) {
|
||||
assert.Assert(t,
|
||||
strings.Contains(stdout, "created") || strings.Contains(stdout, "Created"),
|
||||
"stdout should contain `created`")
|
||||
|
@ -87,8 +86,6 @@ services:
|
|||
|
||||
func TestComposeCreateDependency(t *testing.T) {
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -125,7 +122,7 @@ services:
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "ps", "svc0", "-a")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout, info string, t *testing.T) {
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout string, t tig.T) {
|
||||
assert.Assert(t,
|
||||
strings.Contains(stdout, "created") || strings.Contains(stdout, "Created"),
|
||||
"stdout should contain `created`")
|
||||
|
@ -137,7 +134,7 @@ services:
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "ps", "svc1", "-a")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout, info string, t *testing.T) {
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout string, t tig.T) {
|
||||
assert.Assert(t,
|
||||
strings.Contains(stdout, "created") || strings.Contains(stdout, "Created"),
|
||||
"stdout should contain `created`")
|
||||
|
@ -152,12 +149,10 @@ func TestComposeCreatePull(t *testing.T) {
|
|||
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -167,12 +162,12 @@ services:
|
|||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").AssertOK()
|
||||
|
||||
// `compose create --pull never` should fail: no such image
|
||||
base.Cmd("rmi", "-f", testutil.AlpineImage).Run()
|
||||
base.Cmd("rmi", "-f", testutil.CommonImage).Run()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "create", "--pull", "never").AssertFail()
|
||||
// `compose create --pull missing(default)|always` should succeed: image is pulled and container is created
|
||||
base.Cmd("rmi", "-f", testutil.AlpineImage).Run()
|
||||
base.Cmd("rmi", "-f", testutil.CommonImage).Run()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "create").AssertOK()
|
||||
base.Cmd("rmi", "-f", testutil.AlpineImage).Run()
|
||||
base.Cmd("rmi", "-f", testutil.CommonImage).Run()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "create", "--pull", "always").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "svc0", "-a").AssertOutContainsAny("Created", "created")
|
||||
}
|
||||
|
@ -187,7 +182,7 @@ services:
|
|||
image: %s
|
||||
`, imageSvc0)
|
||||
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, testutil.AlpineImage)
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, testutil.CommonImage)
|
||||
|
||||
testutil.RequiresBuild(t)
|
||||
testutil.RegisterBuildCacheCleanup(t)
|
||||
|
|
|
@ -30,20 +30,18 @@ func TestComposeDownRemoveUsedNetwork(t *testing.T) {
|
|||
|
||||
var (
|
||||
dockerComposeYAMLOrphan = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
test:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
dockerComposeYAMLFull = fmt.Sprintf(`
|
||||
%s
|
||||
orphan:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, dockerComposeYAMLOrphan, testutil.AlpineImage)
|
||||
`, dockerComposeYAMLOrphan, testutil.CommonImage)
|
||||
)
|
||||
|
||||
compOrphan := testutil.NewComposeDir(t, dockerComposeYAMLOrphan)
|
||||
|
@ -66,20 +64,18 @@ func TestComposeDownRemoveOrphans(t *testing.T) {
|
|||
|
||||
var (
|
||||
dockerComposeYAMLOrphan = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
test:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
dockerComposeYAMLFull = fmt.Sprintf(`
|
||||
%s
|
||||
orphan:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, dockerComposeYAMLOrphan, testutil.AlpineImage)
|
||||
`, dockerComposeYAMLOrphan, testutil.CommonImage)
|
||||
)
|
||||
|
||||
compOrphan := testutil.NewComposeDir(t, dockerComposeYAMLOrphan)
|
||||
|
|
|
@ -34,8 +34,6 @@ import (
|
|||
|
||||
func TestComposeExec(t *testing.T) {
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -179,8 +177,6 @@ services:
|
|||
func TestComposeExecTTY(t *testing.T) {
|
||||
const expectedOutput = "speed 38400 baud"
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -267,8 +263,6 @@ services:
|
|||
|
||||
func TestComposeExecWithIndex(t *testing.T) {
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -285,6 +279,11 @@ services:
|
|||
data.Labels().Set("projectName", strings.ToLower(filepath.Base(data.Temp().Dir())))
|
||||
|
||||
helpers.Ensure("compose", "-f", yamlPath, "up", "-d", "svc0")
|
||||
|
||||
// Make sure all containers are started so that /etc/hosts is consistent.
|
||||
for _, index := range []string{"1", "2", "3"} {
|
||||
nerdtest.EnsureContainerStarted(helpers, fmt.Sprintf("%s-svc0-%s", data.Labels().Get("projectName"), index))
|
||||
}
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
|
|
|
@ -17,77 +17,26 @@
|
|||
package compose
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeImages(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
WORDPRESS_DB_PASSWORD: examplepass
|
||||
WORDPRESS_DB_NAME: exampledb
|
||||
volumes:
|
||||
- wordpress:/var/www/html
|
||||
db:
|
||||
image: %s
|
||||
environment:
|
||||
MYSQL_DATABASE: exampledb
|
||||
MYSQL_USER: exampleuser
|
||||
MYSQL_PASSWORD: examplepass
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: '1'
|
||||
volumes:
|
||||
- db:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
wordpress:
|
||||
db:
|
||||
`, testutil.WordpressImage, testutil.MariaDBImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
|
||||
|
||||
wordpressImageName := strings.Split(testutil.WordpressImage, ":")[0]
|
||||
dbImageName := strings.Split(testutil.MariaDBImage, ":")[0]
|
||||
|
||||
// check one service image
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images", "db").AssertOutContains(dbImageName)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images", "db").AssertOutNotContains(wordpressImageName)
|
||||
|
||||
// check all service images
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images").AssertOutContains(dbImageName)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images").AssertOutContains(wordpressImageName)
|
||||
}
|
||||
|
||||
func TestComposeImagesJson(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: %s
|
||||
container_name: wordpress
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
@ -111,41 +60,71 @@ volumes:
|
|||
db:
|
||||
`, testutil.WordpressImage, testutil.MariaDBImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
wordpressImageName, _ := referenceutil.Parse(testutil.WordpressImage)
|
||||
dbImageName, _ := referenceutil.Parse(testutil.MariaDBImage)
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
assertHandler := func(svc string, count int, fields ...string) func(stdout string) error {
|
||||
return func(stdout string) error {
|
||||
// 1. check json output can be unmarshalled back to printables.
|
||||
var printables []composeContainerPrintable
|
||||
if err := json.Unmarshal([]byte(stdout), &printables); err != nil {
|
||||
return fmt.Errorf("[service: %s]failed to unmarshal json output from `compose images`: %s", svc, stdout)
|
||||
}
|
||||
// 2. check #printables matches expected count.
|
||||
if len(printables) != count {
|
||||
return fmt.Errorf("[service: %s]unmarshal generates %d printables, expected %d: %s", svc, len(printables), count, stdout)
|
||||
}
|
||||
// 3. check marshalled json string has all expected substrings.
|
||||
for _, field := range fields {
|
||||
if !strings.Contains(stdout, field) {
|
||||
return fmt.Errorf("[service: %s]marshalled json output doesn't have expected string (%s): %s", svc, field, stdout)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
data.Labels().Set("composeYaml", data.Temp().Path("compose.yaml"))
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
}
|
||||
|
||||
// check other formats are not supported
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images", "--format", "yaml").AssertFail()
|
||||
// check all services are up (can be marshalled and unmarshalled)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images", "--format", "json").
|
||||
AssertOutWithFunc(assertHandler("all", 2, `"ContainerName":"wordpress"`, `"ContainerName":"db"`))
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "images", "--format", "json", "wordpress").
|
||||
AssertOutWithFunc(assertHandler("wordpress", 1, `"ContainerName":"wordpress"`))
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "images db",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "images", "db")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.Contains(dbImageName.Name()),
|
||||
expect.DoesNotContain(wordpressImageName.Name()),
|
||||
)),
|
||||
},
|
||||
{
|
||||
Description: "images",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "images")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(dbImageName.Name(), wordpressImageName.Name())),
|
||||
},
|
||||
{
|
||||
Description: "images --format yaml",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "images", "--format", "yaml")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, nil, nil),
|
||||
},
|
||||
{
|
||||
Description: "images --format json",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "images", "--format", "json")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.JSON([]composeContainerPrintable{}, func(printables []composeContainerPrintable, t tig.T) {
|
||||
assert.Equal(t, len(printables), 2)
|
||||
}),
|
||||
expect.Contains(`"ContainerName":"wordpress"`, `"ContainerName":"db"`),
|
||||
)),
|
||||
},
|
||||
{
|
||||
Description: "images --format json wordpress",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "images", "--format", "json", "wordpress")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.JSON([]composeContainerPrintable{}, func(printables []composeContainerPrintable, t tig.T) {
|
||||
assert.Equal(t, len(printables), 1)
|
||||
}),
|
||||
expect.Contains(`"ContainerName":"wordpress"`),
|
||||
)),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -27,14 +27,10 @@ import (
|
|||
func TestComposeKill(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
wordpress:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
|
|
@ -31,8 +31,6 @@ func TestComposePauseAndUnpause(t *testing.T) {
|
|||
}
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
|
|
@ -88,11 +88,18 @@ func portAction(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
dataStore, err := clientutil.DataStore(globalOptions.DataRoot, globalOptions.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
po := composer.PortOptions{
|
||||
ServiceName: args[0],
|
||||
Index: index,
|
||||
Port: port,
|
||||
Protocol: protocol,
|
||||
DataStore: dataStore,
|
||||
Namespace: globalOptions.Namespace,
|
||||
}
|
||||
|
||||
return c.Port(ctx, cmd.OutOrStdout(), po)
|
||||
|
|
|
@ -20,15 +20,17 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposePort(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -55,8 +57,6 @@ func TestComposePortFailure(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -79,3 +79,42 @@ services:
|
|||
base.ComposeCmd("-f", comp.YAMLFullPath(), "port", "--protocol", "udp", "svc0", "10000").AssertFail()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "port", "--protocol", "tcp", "svc0", "10001").AssertFail()
|
||||
}
|
||||
|
||||
// TestComposeMultiplePorts tests whether it is possible to allocate a large
|
||||
// number of ports. (https://github.com/containerd/nerdctl/issues/4027)
|
||||
func TestComposeMultiplePorts(t *testing.T) {
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
ports:
|
||||
- '32000-32060:32000-32060'
|
||||
`, testutil.AlpineImage)
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
compYamlPath := data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
data.Labels().Set("composeYaml", compYamlPath)
|
||||
|
||||
helpers.Ensure("compose", "-f", compYamlPath, "up", "-d")
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v")
|
||||
}
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "Issue #4027 - Allocate a large number of ports.",
|
||||
NoParallel: true,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("composeYaml"), "port", "svc0", "32000")
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.Contains("0.0.0.0:32000")),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@ import (
|
|||
"github.com/containerd/containerd/v2/core/runtime/restart"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/go-cni"
|
||||
"github.com/containerd/log"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/api/types"
|
||||
"github.com/containerd/nerdctl/v2/pkg/clientutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/cmd/compose"
|
||||
"github.com/containerd/nerdctl/v2/pkg/containerutil"
|
||||
|
@ -183,9 +183,9 @@ func psAction(cmd *cobra.Command, args []string) error {
|
|||
var p composeContainerPrintable
|
||||
var err error
|
||||
if format == "json" {
|
||||
p, err = composeContainerPrintableJSON(ctx, container)
|
||||
p, err = composeContainerPrintableJSON(ctx, container, globalOptions)
|
||||
} else {
|
||||
p, err = composeContainerPrintableTab(ctx, container)
|
||||
p, err = composeContainerPrintableTab(ctx, container, globalOptions)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -234,7 +234,7 @@ func psAction(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// composeContainerPrintableTab constructs composeContainerPrintable with fields
|
||||
// only for console output.
|
||||
func composeContainerPrintableTab(ctx context.Context, container containerd.Container) (composeContainerPrintable, error) {
|
||||
func composeContainerPrintableTab(ctx context.Context, container containerd.Container, gOptions types.GlobalCommandOptions) (composeContainerPrintable, error) {
|
||||
info, err := container.Info(ctx, containerd.WithoutRefreshedMetadata)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
|
@ -251,6 +251,18 @@ func composeContainerPrintableTab(ctx context.Context, container containerd.Cont
|
|||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
dataStore, err := clientutil.DataStore(gOptions.DataRoot, gOptions.Address)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
containerLabels, err := container.Labels(ctx)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
ports, err := portutil.LoadPortMappings(dataStore, gOptions.Namespace, info.ID, containerLabels)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
|
||||
return composeContainerPrintable{
|
||||
Name: info.Labels[labels.Name],
|
||||
|
@ -258,13 +270,13 @@ func composeContainerPrintableTab(ctx context.Context, container containerd.Cont
|
|||
Command: formatter.InspectContainerCommandTrunc(spec),
|
||||
Service: info.Labels[labels.ComposeService],
|
||||
State: status,
|
||||
Ports: formatter.FormatPorts(info.Labels),
|
||||
Ports: formatter.FormatPorts(ports),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// composeContainerPrintableJSON constructs composeContainerPrintable with fields
|
||||
// only for json output and compatible docker output.
|
||||
func composeContainerPrintableJSON(ctx context.Context, container containerd.Container) (composeContainerPrintable, error) {
|
||||
func composeContainerPrintableJSON(ctx context.Context, container containerd.Container, gOptions types.GlobalCommandOptions) (composeContainerPrintable, error) {
|
||||
info, err := container.Info(ctx, containerd.WithoutRefreshedMetadata)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
|
@ -294,6 +306,18 @@ func composeContainerPrintableJSON(ctx context.Context, container containerd.Con
|
|||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
dataStore, err := clientutil.DataStore(gOptions.DataRoot, gOptions.Address)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
containerLabels, err := container.Labels(ctx)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
portMappings, err := portutil.LoadPortMappings(dataStore, gOptions.Namespace, info.ID, containerLabels)
|
||||
if err != nil {
|
||||
return composeContainerPrintable{}, err
|
||||
}
|
||||
|
||||
return composeContainerPrintable{
|
||||
ID: container.ID(),
|
||||
|
@ -305,7 +329,7 @@ func composeContainerPrintableJSON(ctx context.Context, container containerd.Con
|
|||
State: state,
|
||||
Health: "",
|
||||
ExitCode: exitCode,
|
||||
Publishers: formatPublishers(info.Labels),
|
||||
Publishers: formatPublishers(portMappings),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -321,7 +345,7 @@ type PortPublisher struct {
|
|||
|
||||
// formatPublishers parses and returns docker-compatible []PortPublisher from
|
||||
// label map. If an error happens, an empty slice is returned.
|
||||
func formatPublishers(labelMap map[string]string) []PortPublisher {
|
||||
func formatPublishers(portMappings []cni.PortMapping) []PortPublisher {
|
||||
mapper := func(pm cni.PortMapping) PortPublisher {
|
||||
return PortPublisher{
|
||||
URL: pm.HostIP,
|
||||
|
@ -332,12 +356,8 @@ func formatPublishers(labelMap map[string]string) []PortPublisher {
|
|||
}
|
||||
|
||||
var dockerPorts []PortPublisher
|
||||
if portMappings, err := portutil.ParsePortsLabel(labelMap); err == nil {
|
||||
for _, p := range portMappings {
|
||||
dockerPorts = append(dockerPorts, mapper(p))
|
||||
}
|
||||
} else {
|
||||
log.L.Error(err.Error())
|
||||
for _, p := range portMappings {
|
||||
dockerPorts = append(dockerPorts, mapper(p))
|
||||
}
|
||||
return dockerPorts
|
||||
}
|
||||
|
|
|
@ -32,14 +32,10 @@ import (
|
|||
func TestComposePs(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: %s
|
||||
container_name: wordpress_container
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
@ -64,7 +60,7 @@ services:
|
|||
volumes:
|
||||
wordpress:
|
||||
db:
|
||||
`, testutil.WordpressImage, testutil.MariaDBImage, testutil.AlpineImage)
|
||||
`, testutil.WordpressImage, testutil.MariaDBImage, testutil.CommonImage)
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
|
@ -100,9 +96,9 @@ volumes:
|
|||
time.Sleep(3 * time.Second)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "wordpress").AssertOutWithFunc(assertHandler("wordpress_container", testutil.WordpressImage))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "db").AssertOutWithFunc(assertHandler("db_container", testutil.MariaDBImage))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps").AssertOutNotContains(testutil.AlpineImage)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "alpine", "-a").AssertOutWithFunc(assertHandler("alpine_container", testutil.AlpineImage))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "-a", "--filter", "status=exited").AssertOutWithFunc(assertHandler("alpine_container", testutil.AlpineImage))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps").AssertOutNotContains(testutil.CommonImage)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "alpine", "-a").AssertOutWithFunc(assertHandler("alpine_container", testutil.CommonImage))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "-a", "--filter", "status=exited").AssertOutWithFunc(assertHandler("alpine_container", testutil.CommonImage))
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "--services", "-a").AssertOutContainsAll("wordpress\n", "db\n", "alpine\n")
|
||||
}
|
||||
|
||||
|
@ -112,8 +108,6 @@ func TestComposePsJSON(t *testing.T) {
|
|||
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: %s
|
||||
|
|
|
@ -26,14 +26,10 @@ import (
|
|||
func TestComposePullWithService(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
wordpress:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
|
|
@ -26,13 +26,9 @@ import (
|
|||
func TestComposeRestart(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
|
|
@ -18,23 +18,23 @@ package compose
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeRemove(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
wordpress:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
@ -58,27 +58,71 @@ volumes:
|
|||
db:
|
||||
`, testutil.WordpressImage, testutil.MariaDBImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
// no stopped containers
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "rm", "-f").AssertOK()
|
||||
time.Sleep(3 * time.Second)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "wordpress").AssertOutContainsAny("Up", "running")
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "db").AssertOutContainsAny("Up", "running")
|
||||
// remove one stopped service
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "stop", "wordpress").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "rm", "-f", "wordpress").AssertOK()
|
||||
time.Sleep(3 * time.Second)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "wordpress").AssertOutNotContains("wordpress")
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "db").AssertOutContainsAny("Up", "running")
|
||||
// remove all services with `--stop`
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "rm", "-f", "-s").AssertOK()
|
||||
time.Sleep(3 * time.Second)
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "db").AssertOutNotContains("db")
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
data.Labels().Set("yamlPath", data.Temp().Path("compose.yaml"))
|
||||
}
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "All services are still up",
|
||||
NoParallel: true,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "rm", "-f")
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, t tig.T) {
|
||||
wp := helpers.Capture("compose", "-f", data.Labels().Get("yamlPath"), "ps", "wordpress")
|
||||
db := helpers.Capture("compose", "-f", data.Labels().Get("yamlPath"), "ps", "db")
|
||||
comp := expect.Match(regexp.MustCompile("Up|running"))
|
||||
comp(wp, t)
|
||||
comp(db, t)
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Remove stopped service",
|
||||
NoParallel: true,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
helpers.Ensure("compose", "-f", data.Labels().Get("yamlPath"), "stop", "wordpress")
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "rm", "-f")
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, t tig.T) {
|
||||
wp := helpers.Capture("compose", "-f", data.Labels().Get("yamlPath"), "ps", "wordpress")
|
||||
db := helpers.Capture("compose", "-f", data.Labels().Get("yamlPath"), "ps", "db")
|
||||
expect.DoesNotContain("wordpress")(wp, t)
|
||||
expect.Match(regexp.MustCompile("Up|running"))(db, t)
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Remove all services with stop",
|
||||
NoParallel: true,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "rm", "-f", "-s")
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, t tig.T) {
|
||||
db := helpers.Capture("compose", "-f", data.Labels().Get("yamlPath"), "ps", "db")
|
||||
expect.DoesNotContain("db")(db, t)
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -40,13 +40,12 @@ func TestComposeRun(t *testing.T) {
|
|||
const expectedOutput = "speed 38400 baud"
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
entrypoint:
|
||||
- stty
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
|
@ -120,7 +119,6 @@ func TestComposeRunWithServicePorts(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
web:
|
||||
image: %s
|
||||
|
@ -144,7 +142,7 @@ services:
|
|||
}()
|
||||
|
||||
checkNginx := func() error {
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 10, false)
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 5, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -182,7 +180,6 @@ func TestComposeRunWithPublish(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
web:
|
||||
image: %s
|
||||
|
@ -204,7 +201,7 @@ services:
|
|||
}()
|
||||
|
||||
checkNginx := func() error {
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 10, false)
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 5, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -242,7 +239,6 @@ func TestComposeRunWithEnv(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
|
@ -250,7 +246,7 @@ services:
|
|||
- sh
|
||||
- -c
|
||||
- "echo $$FOO"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -274,14 +270,13 @@ func TestComposeRunWithUser(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
entrypoint:
|
||||
- id
|
||||
- -u
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -303,7 +298,6 @@ func TestComposeRunWithLabel(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
|
@ -312,7 +306,7 @@ services:
|
|||
- "dummy log"
|
||||
labels:
|
||||
- "foo=bar"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -341,13 +335,12 @@ func TestComposeRunWithArgs(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
entrypoint:
|
||||
- echo
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -371,13 +364,12 @@ func TestComposeRunWithEntrypoint(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
entrypoint:
|
||||
- stty # should be changed
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -399,13 +391,12 @@ func TestComposeRunWithVolume(t *testing.T) {
|
|||
containerName := testutil.Identifier(t)
|
||||
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
services:
|
||||
alpine:
|
||||
image: %s
|
||||
entrypoint:
|
||||
- stty # no meaning, just put any command
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -489,7 +480,7 @@ services:
|
|||
`, imageSvc0, keyPair.PublicKey, keyPair.PrivateKey,
|
||||
imageSvc1, keyPair.PrivateKey, imageSvc2)
|
||||
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, testutil.AlpineImage)
|
||||
dockerfile := fmt.Sprintf(`FROM %s`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
|
|
@ -18,16 +18,19 @@ package compose
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeStart(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -37,50 +40,68 @@ services:
|
|||
command: "sleep infinity"
|
||||
`, testutil.CommonImage, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").AssertOK()
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
// calling `compose start` after all services up has no effect.
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "start").AssertOK()
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "start")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "stop", "--timeout", "1", "svc0")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "kill", "svc1")
|
||||
}
|
||||
|
||||
// `compose start`` can start a stopped/killed service container
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "stop", "--timeout", "1", "svc0").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "kill", "svc1").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "start").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "svc0").AssertOutContainsAny("Up", "running")
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "svc1").AssertOutContainsAny("Up", "running")
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Temp().Path("compose.yaml"), "start")
|
||||
}
|
||||
|
||||
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: nil,
|
||||
Output: func(stdout string, t tig.T) {
|
||||
svc0 := helpers.Capture("compose", "-f", data.Temp().Path("compose.yaml"), "ps", "svc0")
|
||||
svc1 := helpers.Capture("compose", "-f", data.Temp().Path("compose.yaml"), "ps", "svc1")
|
||||
comp := expect.Match(regexp.MustCompile("Up|running"))
|
||||
comp(svc0, t)
|
||||
comp(svc1, t)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestComposeStartFailWhenServicePause(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
switch base.Info().CgroupDriver {
|
||||
case "none", "":
|
||||
t.Skip("requires cgroup (for pausing)")
|
||||
}
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").AssertOK()
|
||||
testCase.Require = nerdtest.CGroup
|
||||
|
||||
// `compose start` cannot start a paused service container
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "pause", "svc0").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "start").AssertFail()
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "pause", "svc0")
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Temp().Path("compose.yaml"), "start")
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(expect.ExitCodeGenericFail, nil, nil)
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -18,22 +18,22 @@ package compose
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeStop(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
wordpress:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
|
@ -57,21 +57,50 @@ volumes:
|
|||
db:
|
||||
`, testutil.WordpressImage, testutil.MariaDBImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
data.Labels().Set("yamlPath", data.Temp().Path("compose.yaml"))
|
||||
}
|
||||
|
||||
// stop should (only) stop the given service.
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "stop", "db").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "db", "-a").AssertOutContainsAny("Exit", "exited")
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "wordpress").AssertOutContainsAny("Up", "running")
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
// `--timeout` arg should work properly.
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "stop", "--timeout", "5", "wordpress").AssertOK()
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "ps", "wordpress", "-a").AssertOutContainsAny("Exit", "exited")
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "stop db",
|
||||
NoParallel: true,
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("compose", "-f", data.Labels().Get("yamlPath"), "stop", "db")
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "ps", "db", "-a")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Match(regexp.MustCompile("Exit|exited"))),
|
||||
},
|
||||
{
|
||||
Description: "wordpress is still running",
|
||||
NoParallel: true,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "ps", "wordpress")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Match(regexp.MustCompile("Up|running"))),
|
||||
},
|
||||
{
|
||||
Description: "stop wordpress",
|
||||
NoParallel: true,
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("compose", "-f", data.Labels().Get("yamlPath"), "stop", "--timeout", "5", "wordpress")
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "ps", "wordpress", "-a")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Match(regexp.MustCompile("Exit|exited"))),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -20,20 +20,16 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/infoutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeTop(t *testing.T) {
|
||||
if rootlessutil.IsRootless() && infoutil.CgroupsVersion() == "1" {
|
||||
t.Skip("test skipped for rootless containers on cgroup v1")
|
||||
}
|
||||
|
||||
base := testutil.NewBase(t)
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -42,15 +38,36 @@ services:
|
|||
image: %s
|
||||
`, testutil.CommonImage, testutil.NginxAlpineImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
projectName := comp.ProjectName()
|
||||
t.Logf("projectName=%q", projectName)
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").AssertOK()
|
||||
testCase.Require = require.All(nerdtest.CgroupsAccessible)
|
||||
|
||||
// a running container should have the process command in output
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "top", "svc0").AssertOutContains("sleep infinity")
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "top", "svc1").AssertOutContains("nginx")
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
helpers.Ensure("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
data.Labels().Set("yamlPath", data.Temp().Path("compose.yaml"))
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "svc0 contains sleep infinity",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "top", "svc0")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Contains("sleep infinity")),
|
||||
},
|
||||
{
|
||||
Description: "svc1 contains sleep nginx",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Labels().Get("yamlPath"), "top", "svc1")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Contains("nginx")),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -29,19 +29,20 @@ import (
|
|||
"gotest.tools/v3/icmd"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/composer/serviceparser"
|
||||
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nettestutil"
|
||||
)
|
||||
|
||||
func TestComposeUp(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
helpers.ComposeUp(t, base, fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
wordpress:
|
||||
|
@ -102,7 +103,7 @@ COPY index.html /usr/share/nginx/html/index.html
|
|||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d", "--build").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down", "-v").Run()
|
||||
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 50, false)
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 5, false)
|
||||
assert.NilError(t, err)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
assert.NilError(t, err)
|
||||
|
@ -117,8 +118,6 @@ func TestComposeUpNetWithStaticIP(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
staticIP := "172.20.0.12"
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -155,8 +154,6 @@ func TestComposeUpMultiNet(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc0:
|
||||
image: %s
|
||||
|
@ -204,8 +201,6 @@ func TestComposeUpOsEnvVar(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
const containerName = "nginxAlpine"
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc1:
|
||||
image: %s
|
||||
|
@ -237,8 +232,6 @@ func TestComposeUpDotEnvFile(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = `
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc3:
|
||||
image: ghcr.io/stargz-containers/nginx:$TAG
|
||||
|
@ -260,8 +253,6 @@ func TestComposeUpEnvFileNotFoundError(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = `
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
svc4:
|
||||
image: ghcr.io/stargz-containers/nginx:$TAG
|
||||
|
@ -284,13 +275,11 @@ func TestComposeUpWithScale(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
test:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -307,8 +296,6 @@ func TestComposeIPAMConfig(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
foo:
|
||||
image: %s
|
||||
|
@ -319,7 +306,7 @@ networks:
|
|||
ipam:
|
||||
config:
|
||||
- subnet: 10.1.100.0/24
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -337,20 +324,18 @@ func TestComposeUpRemoveOrphans(t *testing.T) {
|
|||
|
||||
var (
|
||||
dockerComposeYAMLOrphan = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
test:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
dockerComposeYAMLFull = fmt.Sprintf(`
|
||||
%s
|
||||
orphan:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, dockerComposeYAMLOrphan, testutil.AlpineImage)
|
||||
`, dockerComposeYAMLOrphan, testutil.CommonImage)
|
||||
)
|
||||
|
||||
compOrphan := testutil.NewComposeDir(t, dockerComposeYAMLOrphan)
|
||||
|
@ -375,13 +360,11 @@ func TestComposeUpIdempotent(t *testing.T) {
|
|||
base := testutil.NewBase(t)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
test:
|
||||
image: %s
|
||||
command: "sleep infinity"
|
||||
`, testutil.AlpineImage)
|
||||
`, testutil.CommonImage)
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
@ -395,11 +378,10 @@ services:
|
|||
}
|
||||
|
||||
func TestComposeUpWithExternalNetwork(t *testing.T) {
|
||||
containerName1 := testutil.Identifier(t) + "-1"
|
||||
containerName2 := testutil.Identifier(t) + "-2"
|
||||
networkName := testutil.Identifier(t) + "-network"
|
||||
var dockerComposeYaml1 = fmt.Sprintf(`
|
||||
version: "3"
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
var dockerComposeYaml1 = fmt.Sprintf(`
|
||||
services:
|
||||
%s:
|
||||
image: %s
|
||||
|
@ -411,9 +393,8 @@ services:
|
|||
networks:
|
||||
%s:
|
||||
external: true
|
||||
`, containerName1, testutil.NginxAlpineImage, containerName1, networkName, networkName)
|
||||
var dockerComposeYaml2 = fmt.Sprintf(`
|
||||
version: "3"
|
||||
`, data.Identifier("con-1"), testutil.NginxAlpineImage, data.Identifier("con-1"), data.Identifier("network"), data.Identifier("network"))
|
||||
var dockerComposeYaml2 = fmt.Sprintf(`
|
||||
services:
|
||||
%s:
|
||||
image: %s
|
||||
|
@ -425,26 +406,34 @@ services:
|
|||
networks:
|
||||
%s:
|
||||
external: true
|
||||
`, containerName2, testutil.NginxAlpineImage, containerName2, networkName, networkName)
|
||||
comp1 := testutil.NewComposeDir(t, dockerComposeYaml1)
|
||||
defer comp1.CleanUp()
|
||||
comp2 := testutil.NewComposeDir(t, dockerComposeYaml2)
|
||||
defer comp2.CleanUp()
|
||||
base := testutil.NewBase(t)
|
||||
// Create the test network
|
||||
base.Cmd("network", "create", networkName).AssertOK()
|
||||
defer base.Cmd("network", "rm", networkName).Run()
|
||||
// Run the first compose
|
||||
base.ComposeCmd("-f", comp1.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp1.YAMLFullPath(), "down", "-v").Run()
|
||||
// Run the second compose
|
||||
base.ComposeCmd("-f", comp2.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp2.YAMLFullPath(), "down", "-v").Run()
|
||||
// Down the second compose
|
||||
base.ComposeCmd("-f", comp2.YAMLFullPath(), "down", "-v").AssertOK()
|
||||
// Run the second compose again
|
||||
base.ComposeCmd("-f", comp2.YAMLFullPath(), "up", "-d").AssertOK()
|
||||
base.Cmd("exec", containerName1, "wget", "-qO-", "http://"+containerName2).AssertOutContains(testutil.NginxAlpineIndexHTMLSnippet)
|
||||
`, data.Identifier("con-2"), testutil.NginxAlpineImage, data.Identifier("con-2"), data.Identifier("network"), data.Identifier("network"))
|
||||
tmp := data.Temp()
|
||||
|
||||
tmp.Save(dockerComposeYaml1, "project-1", "compose.yaml")
|
||||
tmp.Save(dockerComposeYaml2, "project-2", "compose.yaml")
|
||||
|
||||
helpers.Ensure("network", "create", data.Identifier("network"))
|
||||
helpers.Ensure("compose", "-f", tmp.Path("project-1", "compose.yaml"), "up", "-d")
|
||||
helpers.Ensure("compose", "-f", tmp.Path("project-2", "compose.yaml"), "up", "-d")
|
||||
helpers.Ensure("compose", "-f", tmp.Path("project-2", "compose.yaml"), "down", "-v")
|
||||
helpers.Ensure("compose", "-f", tmp.Path("project-2", "compose.yaml"), "up", "-d")
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier("con-2"))
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
helpers.Ensure("exec", data.Identifier("con-1"), "cat", "/etc/hosts")
|
||||
return helpers.Command("exec", data.Identifier("con-1"), "wget", "-qO-", "http://"+data.Identifier("con-2"))
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(0, nil, expect.Contains(testutil.NginxAlpineIndexHTMLSnippet))
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("project-1", "compose.yaml"), "down", "-v")
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("project-2", "compose.yaml"), "down", "-v")
|
||||
helpers.Anyhow("network", "rm", data.Identifier("network"))
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestComposeUpWithBypass4netns(t *testing.T) {
|
||||
|
@ -457,8 +446,6 @@ func TestComposeUpWithBypass4netns(t *testing.T) {
|
|||
testutil.RequireSystemService(t, "bypass4netnsd")
|
||||
base := testutil.NewBase(t)
|
||||
helpers.ComposeUp(t, base, fmt.Sprintf(`
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
wordpress:
|
||||
|
@ -557,8 +544,6 @@ func TestComposeUpAbortOnContainerExit(t *testing.T) {
|
|||
services:
|
||||
%s:
|
||||
image: %s
|
||||
ports:
|
||||
- 8080:80
|
||||
%s:
|
||||
image: %s
|
||||
entrypoint: /bin/sh -c "exit 1"
|
||||
|
|
|
@ -17,14 +17,15 @@
|
|||
package compose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/icmd"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -32,56 +33,73 @@ import (
|
|||
|
||||
// https://github.com/containerd/nerdctl/issues/1942
|
||||
func TestComposeUpDetailedError(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("FIXME: test does not work on Windows yet (runtime \"io.containerd.runc.v2\" binary not installed \"containerd-shim-runc-v2.exe\": file does not exist)")
|
||||
}
|
||||
base := testutil.NewBase(t)
|
||||
dockerComposeYAML := fmt.Sprintf(`
|
||||
services:
|
||||
foo:
|
||||
image: %s
|
||||
runtime: invalid
|
||||
`, testutil.CommonImage)
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
|
||||
c := base.ComposeCmd("-f", comp.YAMLFullPath(), "up", "-d")
|
||||
expected := icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: `exec: \"invalid\": executable file not found in $PATH`,
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// "FIXME: test does not work on Windows yet (runtime \"io.containerd.runc.v2\" binary not installed \"containerd-shim-runc-v2.exe\": file does not exist)
|
||||
testCase.Require = require.Not(require.Windows)
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
}
|
||||
// Docker expected err is different
|
||||
if nerdtest.IsDocker() {
|
||||
expected.Err = `unknown or invalid runtime name: invalid`
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Temp().Path("compose.yaml"), "up", "-d")
|
||||
}
|
||||
c.Assert(expected)
|
||||
|
||||
testCase.Expected = test.Expects(
|
||||
1,
|
||||
[]error{errors.New(`invalid runtime name`)},
|
||||
nil,
|
||||
)
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
// https://github.com/containerd/nerdctl/issues/1652
|
||||
func TestComposeUpBindCreateHostPath(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip(`FIXME: no support for Windows path: (error: "volume target must be an absolute path, got \"/mnt\")`)
|
||||
}
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
base := testutil.NewBase(t)
|
||||
// `FIXME: no support for Windows path: (error: "volume target must be an absolute path, got \"/mnt\")`
|
||||
testCase.Require = require.Not(require.Windows)
|
||||
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
var dockerComposeYAML = fmt.Sprintf(`
|
||||
services:
|
||||
test:
|
||||
image: %s
|
||||
command: sh -euxc "echo hi >/mnt/test"
|
||||
volumes:
|
||||
# ./foo should be automatically created
|
||||
- ./foo:/mnt
|
||||
`, testutil.CommonImage)
|
||||
# tempdir/foo should be automatically created
|
||||
- %s:/mnt
|
||||
`, testutil.CommonImage, data.Temp().Path("foo"))
|
||||
|
||||
comp := testutil.NewComposeDir(t, dockerComposeYAML)
|
||||
defer comp.CleanUp()
|
||||
data.Temp().Save(dockerComposeYAML, "compose.yaml")
|
||||
}
|
||||
|
||||
base.ComposeCmd("-f", comp.YAMLFullPath(), "up").AssertOK()
|
||||
defer base.ComposeCmd("-f", comp.YAMLFullPath(), "down").AssertOK()
|
||||
testFile := filepath.Join(comp.Dir(), "foo", "test")
|
||||
testB, err := os.ReadFile(testFile)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "hi\n", string(testB))
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("compose", "-f", data.Temp().Path("compose.yaml"), "up")
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down")
|
||||
}
|
||||
|
||||
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: nil,
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Equal(t, data.Temp().Load("foo", "test"), "hi\n")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -19,20 +19,29 @@ package compose
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestComposeVersion(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
base.ComposeCmd("version").AssertOutContains("Compose version ")
|
||||
testCase := nerdtest.Setup()
|
||||
testCase.Command = test.Command("compose", "version")
|
||||
testCase.Expected = test.Expects(0, nil, expect.Contains("Compose version "))
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestComposeVersionShort(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
base.ComposeCmd("version", "--short").AssertOK()
|
||||
testCase := nerdtest.Setup()
|
||||
testCase.Command = test.Command("compose", "version", "--short")
|
||||
testCase.Expected = test.Expects(0, nil, nil)
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestComposeVersionJson(t *testing.T) {
|
||||
base := testutil.NewBase(t)
|
||||
base.ComposeCmd("version", "--format", "json").AssertOutContains("{\"version\":\"")
|
||||
testCase := nerdtest.Setup()
|
||||
testCase.Command = test.Command("compose", "version", "--format", "json")
|
||||
testCase.Expected = test.Expects(0, nil, expect.Contains("{\"version\":\""))
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ func Command() *cobra.Command {
|
|||
pruneCommand(),
|
||||
StatsCommand(),
|
||||
AttachCommand(),
|
||||
HealthCheckCommand(),
|
||||
)
|
||||
AddCpCommand(cmd)
|
||||
return cmd
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
containerd "github.com/containerd/containerd/v2/client"
|
||||
|
@ -56,6 +58,7 @@ Caveats:
|
|||
SilenceErrors: true,
|
||||
}
|
||||
cmd.Flags().String("detach-keys", consoleutil.DefaultDetachKeys, "Override the default detach keys")
|
||||
cmd.Flags().Bool("no-stdin", false, "Do not attach STDIN")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -68,9 +71,18 @@ func attachOptions(cmd *cobra.Command) (types.ContainerAttachOptions, error) {
|
|||
if err != nil {
|
||||
return types.ContainerAttachOptions{}, err
|
||||
}
|
||||
noStdin, err := cmd.Flags().GetBool("no-stdin")
|
||||
if err != nil {
|
||||
return types.ContainerAttachOptions{}, err
|
||||
}
|
||||
|
||||
var stdin io.Reader
|
||||
if !noStdin {
|
||||
stdin = cmd.InOrStdin()
|
||||
}
|
||||
return types.ContainerAttachOptions{
|
||||
GOptions: globalOptions,
|
||||
Stdin: cmd.InOrStdin(),
|
||||
Stdin: stdin,
|
||||
Stdout: cmd.OutOrStdout(),
|
||||
Stderr: cmd.ErrOrStderr(),
|
||||
DetachKeys: detachKeys,
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -64,7 +65,7 @@ func TestAttach(t *testing.T) {
|
|||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{errors.New("read detach keys")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
})
|
||||
|
@ -93,7 +94,7 @@ func TestAttach(t *testing.T) {
|
|||
Errors: []error{errors.New("read detach keys")},
|
||||
Output: expect.All(
|
||||
expect.Contains("markmark"),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
),
|
||||
|
@ -125,7 +126,7 @@ func TestAttachDetachKeys(t *testing.T) {
|
|||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{errors.New("read detach keys")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
})
|
||||
|
@ -153,7 +154,7 @@ func TestAttachDetachKeys(t *testing.T) {
|
|||
Errors: []error{errors.New("read detach keys")},
|
||||
Output: expect.All(
|
||||
expect.Contains("markmark"),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
),
|
||||
|
@ -182,8 +183,8 @@ func TestAttachForAutoRemovedContainer(t *testing.T) {
|
|||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{errors.New("read detach keys")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"), info)
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -202,7 +203,7 @@ func TestAttachForAutoRemovedContainer(t *testing.T) {
|
|||
ExitCode: 42,
|
||||
Output: expect.All(
|
||||
expect.Contains("markmark"),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, !strings.Contains(helpers.Capture("ps", "-a"), data.Identifier()))
|
||||
},
|
||||
),
|
||||
|
@ -211,3 +212,44 @@ func TestAttachForAutoRemovedContainer(t *testing.T) {
|
|||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestAttachNoStdin(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
cmd := helpers.Command("run", "-it", "--detach-keys=ctrl-p,ctrl-q", "--name", data.Identifier(),
|
||||
testutil.CommonImage, "sleep", "5")
|
||||
cmd.WithPseudoTTY()
|
||||
cmd.Feed(bytes.NewReader([]byte{16, 17})) // Ctrl-p, Ctrl-q to detach (https://en.wikipedia.org/wiki/C0_and_C1_control_codes)
|
||||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "{{.State.Running}}", data.Identifier()), "true"))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
cmd := helpers.Command("attach", "--no-stdin", data.Identifier())
|
||||
cmd.WithPseudoTTY()
|
||||
cmd.Feed(strings.NewReader("should-not-appear\n"))
|
||||
cmd.Feed(bytes.NewReader([]byte{16, 17}))
|
||||
return cmd
|
||||
}
|
||||
|
||||
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0, // Since it's a normal exit and not detach.
|
||||
Output: func(stdout string, t tig.T) {
|
||||
logs := helpers.Capture("logs", data.Identifier())
|
||||
assert.Assert(t, !strings.Contains(logs, "should-not-appear"))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
|
||||
|
@ -40,6 +42,15 @@ func CommitCommand() *cobra.Command {
|
|||
cmd.Flags().StringP("message", "m", "", "Commit message")
|
||||
cmd.Flags().StringArrayP("change", "c", nil, "Apply Dockerfile instruction to the created image (supported directives: [CMD, ENTRYPOINT])")
|
||||
cmd.Flags().BoolP("pause", "p", true, "Pause container during commit")
|
||||
cmd.Flags().StringP("compression", "", "gzip", "commit compression algorithm (zstd or gzip)")
|
||||
cmd.Flags().String("format", "docker", "Format of the committed image (docker or oci)")
|
||||
cmd.Flags().Bool("estargz", false, "Convert the committed layer to eStargz for lazy pulling")
|
||||
cmd.Flags().Int("estargz-compression-level", 9, "eStargz compression level (1-9)")
|
||||
cmd.Flags().Int("estargz-chunk-size", 0, "eStargz chunk size")
|
||||
cmd.Flags().Int("estargz-min-chunk-size", 0, "The minimal number of bytes of data must be written in one gzip stream")
|
||||
cmd.Flags().Bool("zstdchunked", false, "Convert the committed layer to zstd:chunked for lazy pulling")
|
||||
cmd.Flags().Int("zstdchunked-compression-level", 3, "zstd:chunked compression level")
|
||||
cmd.Flags().Int("zstdchunked-chunk-size", 0, "zstd:chunked chunk size")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -66,15 +77,78 @@ func commitOptions(cmd *cobra.Command) (types.ContainerCommitOptions, error) {
|
|||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
|
||||
return types.ContainerCommitOptions{
|
||||
Stdout: cmd.OutOrStdout(),
|
||||
GOptions: globalOptions,
|
||||
Author: author,
|
||||
Message: message,
|
||||
Pause: pause,
|
||||
Change: change,
|
||||
}, nil
|
||||
com, err := cmd.Flags().GetString("compression")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
if com != string(types.Zstd) && com != string(types.Gzip) {
|
||||
return types.ContainerCommitOptions{}, errors.New("--compression param only supports zstd or gzip")
|
||||
}
|
||||
|
||||
format, err := cmd.Flags().GetString("format")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
if format != string(types.ImageFormatDocker) && format != string(types.ImageFormatOCI) {
|
||||
return types.ContainerCommitOptions{}, errors.New("--format param only supports docker or oci")
|
||||
}
|
||||
|
||||
estargz, err := cmd.Flags().GetBool("estargz")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
estargzCompressionLevel, err := cmd.Flags().GetInt("estargz-compression-level")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
estargzChunkSize, err := cmd.Flags().GetInt("estargz-chunk-size")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
estargzMinChunkSize, err := cmd.Flags().GetInt("estargz-min-chunk-size")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
|
||||
zstdchunked, err := cmd.Flags().GetBool("zstdchunked")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
zstdchunkedCompressionLevel, err := cmd.Flags().GetInt("zstdchunked-compression-level")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
zstdchunkedChunkSize, err := cmd.Flags().GetInt("zstdchunked-chunk-size")
|
||||
if err != nil {
|
||||
return types.ContainerCommitOptions{}, err
|
||||
}
|
||||
|
||||
// estargz and zstdchunked are mutually exclusive
|
||||
if estargz && zstdchunked {
|
||||
return types.ContainerCommitOptions{}, errors.New("options --estargz and --zstdchunked lead to conflict, only one of them can be used")
|
||||
}
|
||||
|
||||
return types.ContainerCommitOptions{
|
||||
Stdout: cmd.OutOrStdout(),
|
||||
GOptions: globalOptions,
|
||||
Author: author,
|
||||
Message: message,
|
||||
Pause: pause,
|
||||
Change: change,
|
||||
Compression: types.CompressionType(com),
|
||||
Format: types.ImageFormat(format),
|
||||
EstargzOptions: types.EstargzOptions{
|
||||
Estargz: estargz,
|
||||
EstargzCompressionLevel: estargzCompressionLevel,
|
||||
EstargzChunkSize: estargzChunkSize,
|
||||
EstargzMinChunkSize: estargzMinChunkSize,
|
||||
},
|
||||
ZstdChunkedOptions: types.ZstdChunkedOptions{
|
||||
ZstdChunked: zstdchunked,
|
||||
ZstdChunkedCompressionLevel: zstdchunkedCompressionLevel,
|
||||
ZstdChunkedChunkSize: zstdchunkedChunkSize,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func commitAction(cmd *cobra.Command, args []string) error {
|
||||
|
@ -82,7 +156,6 @@ func commitAction(cmd *cobra.Command, args []string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), options.GOptions.Namespace, options.GOptions.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -19,8 +19,10 @@ package container
|
|||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -41,7 +43,7 @@ func TestKubeCommitSave(t *testing.T) {
|
|||
nerdtest.KubeCtlCommand(helpers, "wait", "pod", identifier, "--for=condition=ready", "--timeout=1m").Run(&test.Expected{})
|
||||
nerdtest.KubeCtlCommand(helpers, "exec", identifier, "--", "mkdir", "-p", "/tmp/whatever").Run(&test.Expected{})
|
||||
nerdtest.KubeCtlCommand(helpers, "get", "pods", identifier, "-o", "jsonpath={ .status.containerStatuses[0].containerID }").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
containerID = strings.TrimPrefix(stdout, "containerd://")
|
||||
},
|
||||
})
|
||||
|
@ -53,8 +55,22 @@ func TestKubeCommitSave(t *testing.T) {
|
|||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
helpers.Ensure("commit", data.Labels().Get("containerID"), "testcommitsave")
|
||||
return helpers.Command("save", "testcommitsave")
|
||||
helpers.Ensure("commit", data.Labels().Get("containerID"), data.Identifier("testcommitsave"))
|
||||
// Wait for the image to show up
|
||||
for range 5 {
|
||||
found := false
|
||||
cmd := helpers.Command("images", data.Identifier("testcommitsave"), "--format", "json")
|
||||
cmd.Run(&test.Expected{
|
||||
Output: func(stdout string, t tig.T) {
|
||||
found = strings.TrimSpace(stdout) != ""
|
||||
},
|
||||
})
|
||||
if found {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
return helpers.Command("save", data.Identifier("testcommitsave"))
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(0, nil, nil)
|
||||
|
@ -73,7 +89,7 @@ func TestKubeCommitSave(t *testing.T) {
|
|||
|
||||
cmd = nerdtest.KubeCtlCommand(helpers, "get", "pods", tID, "-o", "jsonpath={ .status.hostIPs[0].ip }")
|
||||
cmd.Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
registryIP = stdout
|
||||
},
|
||||
})
|
||||
|
|
|
@ -19,10 +19,14 @@ package container
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
@ -86,3 +90,52 @@ func TestCommit(t *testing.T) {
|
|||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestZstdCommit(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
testCase.Require = require.All(
|
||||
// FIXME: Docker does not support compression
|
||||
require.Not(nerdtest.Docker),
|
||||
nerdtest.ContainerdVersion("2.0.0"),
|
||||
nerdtest.CGroup,
|
||||
)
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
helpers.Anyhow("rmi", "-f", data.Identifier("image"))
|
||||
}
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
identifier := data.Identifier()
|
||||
helpers.Ensure("run", "-d", "--name", identifier, testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, identifier)
|
||||
helpers.Ensure("exec", identifier, "sh", "-euxc", `echo hello-test-commit > /foo`)
|
||||
helpers.Ensure("commit", identifier, data.Identifier("image"), "--compression=zstd")
|
||||
data.Labels().Set("image", data.Identifier("image"))
|
||||
}
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "verify zstd has been used",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("image", "inspect", "--mode=native", data.Labels().Get("image"))
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.JSON([]native.Image{}, func(images []native.Image, t tig.T) {
|
||||
assert.Equal(t, len(images), 1)
|
||||
assert.Equal(helpers.T(), images[0].Manifest.Layers[len(images[0].Manifest.Layers)-1].MediaType, "application/vnd.docker.image.rootfs.diff.tar.zstd")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "verify the image is working",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("run", "--rm", data.Labels().Get("image"), "sh", "-c", "--", "cat /foo")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals("hello-test-commit\n")),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -258,6 +258,40 @@ func createOptions(cmd *cobra.Command) (types.ContainerCreateOptions, error) {
|
|||
}
|
||||
// #endregion
|
||||
|
||||
// #region for healthcheck flags
|
||||
opt.HealthCmd, err = cmd.Flags().GetString("health-cmd")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
opt.HealthInterval, err = cmd.Flags().GetDuration("health-interval")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
opt.HealthTimeout, err = cmd.Flags().GetDuration("health-timeout")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
opt.HealthRetries, err = cmd.Flags().GetInt("health-retries")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
opt.HealthStartPeriod, err = cmd.Flags().GetDuration("health-start-period")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
opt.HealthStartInterval, err = cmd.Flags().GetDuration("health-start-interval")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
opt.NoHealthcheck, err = cmd.Flags().GetBool("no-healthcheck")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
}
|
||||
if err := helpers.ValidateHealthcheckFlags(opt); err != nil {
|
||||
return opt, err
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region for intel RDT flags
|
||||
opt.RDTClass, err = cmd.Flags().GetString("rdt-class")
|
||||
if err != nil {
|
||||
|
@ -371,7 +405,6 @@ func createOptions(cmd *cobra.Command) (types.ContainerCreateOptions, error) {
|
|||
// #endregion
|
||||
|
||||
// #region for metadata flags
|
||||
opt.NameChanged = cmd.Flags().Changed("name")
|
||||
opt.Name, err = cmd.Flags().GetString("name")
|
||||
if err != nil {
|
||||
return opt, err
|
||||
|
@ -506,7 +539,7 @@ func createAction(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
defer cancel()
|
||||
|
||||
netFlags, err := loadNetworkFlags(cmd)
|
||||
netFlags, err := loadNetworkFlags(cmd, createOpt.GOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load networking flags: %w", err)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -235,7 +236,7 @@ func TestIssue2993(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("is already used by ID")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
containersDirs, err := os.ReadDir(data.Labels().Get(containersPathKey))
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(containersDirs), 1)
|
||||
|
@ -282,7 +283,7 @@ func TestIssue2993(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
containersDirs, err := os.ReadDir(data.Labels().Get(containersPathKey))
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, len(containersDirs), 0)
|
||||
|
@ -363,10 +364,10 @@ func TestUsernsMappingCreateCmd(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
actualHostUID, err := getContainerHostUID(helpers, data.Identifier())
|
||||
assert.NilError(t, err, "Failed to get container host UID")
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"), info)
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"))
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -96,13 +97,13 @@ func TestCreateHyperVContainer(t *testing.T) {
|
|||
helpers.Command("container", "inspect", data.Labels().Get("cID")).
|
||||
Run(&test.Expected{
|
||||
ExitCode: expect.ExitCodeNoCheck,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Container
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
if err != nil || len(dc) == 0 {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, len(dc), 1, "Unexpectedly got multiple results\n"+info)
|
||||
assert.Equal(t, len(dc), 1, "Unexpectedly got multiple results\n")
|
||||
ran = dc[0].State.Status == "exited"
|
||||
},
|
||||
})
|
||||
|
|
|
@ -62,27 +62,27 @@ func execOptions(cmd *cobra.Command) (types.ContainerExecOptions, error) {
|
|||
return types.ContainerExecOptions{}, err
|
||||
}
|
||||
|
||||
flagI, err := cmd.Flags().GetBool("interactive")
|
||||
isInteractive, err := cmd.Flags().GetBool("interactive")
|
||||
if err != nil {
|
||||
return types.ContainerExecOptions{}, err
|
||||
}
|
||||
flagT, err := cmd.Flags().GetBool("tty")
|
||||
isTerminal, err := cmd.Flags().GetBool("tty")
|
||||
if err != nil {
|
||||
return types.ContainerExecOptions{}, err
|
||||
}
|
||||
flagD, err := cmd.Flags().GetBool("detach")
|
||||
isDetach, err := cmd.Flags().GetBool("detach")
|
||||
if err != nil {
|
||||
return types.ContainerExecOptions{}, err
|
||||
}
|
||||
|
||||
if flagI {
|
||||
if flagD {
|
||||
if isInteractive {
|
||||
if isDetach {
|
||||
return types.ContainerExecOptions{}, errors.New("currently flag -i and -d cannot be specified together (FIXME)")
|
||||
}
|
||||
}
|
||||
|
||||
if flagT {
|
||||
if flagD {
|
||||
if isTerminal {
|
||||
if isDetach {
|
||||
return types.ContainerExecOptions{}, errors.New("currently flag -t and -d cannot be specified together (FIXME)")
|
||||
}
|
||||
}
|
||||
|
@ -111,9 +111,9 @@ func execOptions(cmd *cobra.Command) (types.ContainerExecOptions, error) {
|
|||
|
||||
return types.ContainerExecOptions{
|
||||
GOptions: globalOptions,
|
||||
TTY: flagT,
|
||||
Interactive: flagI,
|
||||
Detach: flagD,
|
||||
TTY: isTerminal,
|
||||
Interactive: isInteractive,
|
||||
Detach: isDetach,
|
||||
Workdir: workdir,
|
||||
Env: env,
|
||||
EnvFile: envFile,
|
||||
|
|
|
@ -65,6 +65,9 @@ func TestExecTTY(t *testing.T) {
|
|||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(), testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
|
||||
data.Labels().Set("container_name", data.Identifier())
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
containerd "github.com/containerd/containerd/v2/client"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/completion"
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/clientutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/cmd/container"
|
||||
"github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker"
|
||||
)
|
||||
|
||||
// HealthCheckCommand returns a cobra command for `nerdctl container healthcheck`
|
||||
func HealthCheckCommand() *cobra.Command {
|
||||
var healthCheckCommand = &cobra.Command{
|
||||
Use: "healthcheck [flags] CONTAINER",
|
||||
Short: "Execute the health check command in a container",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: healthCheckAction,
|
||||
ValidArgsFunction: healthCheckShellComplete,
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
}
|
||||
|
||||
return healthCheckCommand
|
||||
}
|
||||
|
||||
func healthCheckAction(cmd *cobra.Command, args []string) error {
|
||||
globalOptions, err := helpers.ProcessRootCmdFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, ctx, cancel, err := clientutil.NewClient(cmd.Context(), globalOptions.Namespace, globalOptions.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
containerID := args[0]
|
||||
walker := &containerwalker.ContainerWalker{
|
||||
Client: client,
|
||||
OnFound: func(ctx context.Context, found containerwalker.Found) error {
|
||||
if found.MatchCount > 1 {
|
||||
return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req)
|
||||
}
|
||||
return container.HealthCheck(ctx, client, found.Container)
|
||||
},
|
||||
}
|
||||
|
||||
n, err := walker.Walk(ctx, containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n == 0 {
|
||||
return fmt.Errorf("no such container %s", containerID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func healthCheckShellComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completion.ContainerNames(cmd, func(status containerd.ProcessStatus) bool {
|
||||
return status == containerd.Running
|
||||
})
|
||||
}
|
|
@ -0,0 +1,604 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/healthcheck"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestContainerHealthCheckBasic(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// Docker CLI does not provide a standalone healthcheck command.
|
||||
testCase.Require = require.Not(nerdtest.Docker)
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "Container does not exist",
|
||||
Command: test.Command("container", "healthcheck", "non-existent"),
|
||||
Expected: test.Expects(1, []error{errors.New("no such container non-existent")}, nil),
|
||||
},
|
||||
{
|
||||
Description: "Missing health check config",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(1, []error{errors.New("container has no health check configured")}, nil),
|
||||
},
|
||||
{
|
||||
Description: "Basic health check success",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "echo healthy",
|
||||
"--health-interval", "45s",
|
||||
"--health-timeout", "30s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state to be present")
|
||||
assert.Equal(t, healthcheck.Healthy, h.Status)
|
||||
assert.Equal(t, 0, h.FailingStreak)
|
||||
assert.Assert(t, len(h.Log) > 0, "expected at least one health check log entry")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health check on stopped container",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "echo healthy",
|
||||
"--health-interval", "3s",
|
||||
testutil.CommonImage, "sleep", "2")
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
helpers.Ensure("stop", data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(1, []error{errors.New("container is not running (status: stopped)")}, nil),
|
||||
},
|
||||
{
|
||||
Description: "Health check without task",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("create", "--name", data.Identifier(),
|
||||
"--health-cmd", "echo healthy",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(1, []error{errors.New("failed to get container task: no running task found")}, nil),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestContainerHealthCheckAdvance(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// Docker CLI does not provide a standalone healthcheck command.
|
||||
testCase.Require = require.Not(nerdtest.Docker)
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "Health check timeout scenario",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "sleep 10",
|
||||
"--health-timeout", "2s",
|
||||
"--health-interval", "1s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.FailingStreak, 1)
|
||||
assert.Assert(t, len(inspect.State.Health.Log) > 0, "expected health log to have entries")
|
||||
last := inspect.State.Health.Log[0]
|
||||
assert.Equal(t, -1, last.ExitCode)
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health check failing streak behavior",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "exit 1",
|
||||
"--health-interval", "1s",
|
||||
"--health-retries", "2",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
// Run healthcheck twice to ensure failing streak
|
||||
for i := 0; i < 2; i++ {
|
||||
helpers.Ensure("container", "healthcheck", data.Identifier())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Unhealthy)
|
||||
assert.Equal(t, h.FailingStreak, 2)
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health check with start period",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "exit 1",
|
||||
"--health-interval", "1s",
|
||||
"--health-start-period", "60s",
|
||||
"--health-retries", "2",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Starting)
|
||||
assert.Equal(t, h.FailingStreak, 0)
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health check with invalid command",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "not-a-real-cmd",
|
||||
"--health-interval", "1s",
|
||||
"--health-retries", "1",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Unhealthy)
|
||||
assert.Equal(t, h.FailingStreak, 1)
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "No healthcheck flag disables health status",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--no-healthcheck", testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
assert.Assert(t, inspect.State.Health == nil, "expected health to be nil with --no-healthcheck")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Healthcheck using CMD-SHELL format",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "echo shell-format", "--health-interval", "1s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(_ string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Healthy)
|
||||
assert.Assert(t, len(h.Log) > 0)
|
||||
assert.Assert(t, strings.Contains(h.Log[0].Output, "shell-format"))
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health check uses container environment variables",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--env", "MYVAR=test-value",
|
||||
"--health-cmd", "echo $MYVAR",
|
||||
"--health-interval", "1s",
|
||||
"--health-timeout", "1s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Healthy)
|
||||
assert.Assert(t, h.FailingStreak == 0)
|
||||
assert.Assert(t, strings.Contains(h.Log[0].Output, "test"), "expected health log output to contain 'test'")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health check respects container WorkingDir",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--workdir", "/tmp",
|
||||
"--health-cmd", "pwd",
|
||||
"--health-interval", "1s",
|
||||
"--health-timeout", "1s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Healthy)
|
||||
assert.Equal(t, h.FailingStreak, 0)
|
||||
assert.Assert(t, strings.Contains(h.Log[0].Output, "/tmp"), "expected health log output to contain '/tmp'")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Healthcheck emits large output repeatedly",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "yes X | head -c 60000",
|
||||
"--health-interval", "1s", "--health-timeout", "2s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
for i := 0; i < 3; i++ {
|
||||
helpers.Ensure("container", "healthcheck", data.Identifier())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(_ string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Healthy)
|
||||
assert.Assert(t, len(h.Log) >= 3, "expected at least 3 health log entries")
|
||||
for _, log := range h.Log {
|
||||
assert.Assert(t, len(log.Output) >= 1024, fmt.Sprintf("each output should be >= 1024 bytes, was: %s", log.Output))
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health log in inspect keeps only the latest 5 entries",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "exit 1",
|
||||
"--health-interval", "1s",
|
||||
"--health-retries", "1",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
for i := 0; i < 7; i++ {
|
||||
helpers.Ensure("container", "healthcheck", data.Identifier())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(_ string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Unhealthy)
|
||||
assert.Assert(t, len(h.Log) <= 5, "expected health log to contain at most 5 entries")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Healthcheck with large output gets truncated in health log",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "yes X | head -c 1048576", // 1MB output
|
||||
"--health-interval", "1s", "--health-timeout", "2s",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("container", "healthcheck", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(_ string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Healthy)
|
||||
assert.Equal(t, h.FailingStreak, 0)
|
||||
assert.Assert(t, len(h.Log) == 1, "expected one log entry")
|
||||
output := h.Log[0].Output
|
||||
assert.Assert(t, strings.HasSuffix(output, "[truncated]"), "expected output to be truncated with '[truncated]'")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Health status transitions from healthy to unhealthy after retries",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
containerName := data.Identifier()
|
||||
helpers.Ensure("run", "-d", "--name", containerName,
|
||||
"--health-cmd", "exit 1",
|
||||
"--health-timeout", "10s",
|
||||
"--health-retries", "3",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
for i := 0; i < 4; i++ {
|
||||
helpers.Ensure("container", "healthcheck", data.Identifier())
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Unhealthy)
|
||||
assert.Assert(t, h.FailingStreak >= 3)
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Failed healthchecks in start-period do not change status",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "ls /foo || exit 1", "--health-retries", "2",
|
||||
"--health-start-period", "30s", // long enough to stay in "starting"
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
// Run healthcheck 3 times (should still be in start period)
|
||||
for i := 0; i < 3; i++ {
|
||||
helpers.Ensure("container", "healthcheck", data.Identifier())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Starting)
|
||||
assert.Equal(t, h.FailingStreak, 0, "failing streak should not increase during start period")
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Successful healthcheck in start-period sets status to healthy",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(),
|
||||
"--health-cmd", "ls || exit 1", "--health-retries", "2",
|
||||
testutil.CommonImage, "sleep", nerdtest.Infinity)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
helpers.Ensure("container", "healthcheck", data.Identifier())
|
||||
time.Sleep(1 * time.Second)
|
||||
return helpers.Command("inspect", data.Identifier())
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
h := inspect.State.Health
|
||||
debug, _ := json.MarshalIndent(h, "", " ")
|
||||
t.Log(string(debug))
|
||||
assert.Assert(t, h != nil, "expected health state")
|
||||
assert.Equal(t, h.Status, healthcheck.Healthy, "expected healthy status even during start-period")
|
||||
assert.Equal(t, h.FailingStreak, 0)
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
|
@ -20,7 +20,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -535,8 +534,7 @@ RUN groupadd -r test && useradd -r -g test test
|
|||
USER test
|
||||
`, testutil.UbuntuImage)
|
||||
|
||||
err := os.WriteFile(filepath.Join(data.Temp().Path(), "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
|
||||
helpers.Ensure("build", "-t", data.Identifier(), data.Temp().Path())
|
||||
helpers.Ensure("create", "--name", data.Identifier(), "--user", "test", data.Identifier())
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/formatter"
|
||||
"github.com/containerd/nerdctl/v2/pkg/strutil"
|
||||
|
@ -304,6 +305,42 @@ func TestContainerListWithFilter(t *testing.T) {
|
|||
return nil
|
||||
})
|
||||
|
||||
// should support regexp
|
||||
base.Cmd("ps", "--filter", "name=.*"+testContainerA.name+".*").AssertOutWithFunc(func(stdout string) error {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
if len(lines) < 2 {
|
||||
return fmt.Errorf("expected at least 2 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
|
||||
err := tab.ParseHeader(lines[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse header: %v", err)
|
||||
}
|
||||
|
||||
containerName, _ := tab.ReadRow(lines[1], "NAMES")
|
||||
assert.Equal(t, containerName, testContainerA.name)
|
||||
return nil
|
||||
})
|
||||
|
||||
// fully anchored regexp
|
||||
base.Cmd("ps", "--filter", "name=^"+testContainerA.name+"$").AssertOutWithFunc(func(stdout string) error {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
if len(lines) < 2 {
|
||||
return fmt.Errorf("expected at least 2 lines, got %d", len(lines))
|
||||
}
|
||||
|
||||
tab := tabutil.NewReader("CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
|
||||
err := tab.ParseHeader(lines[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse header: %v", err)
|
||||
}
|
||||
|
||||
containerName, _ := tab.ReadRow(lines[1], "NAMES")
|
||||
assert.Equal(t, containerName, testContainerA.name)
|
||||
return nil
|
||||
})
|
||||
|
||||
base.Cmd("ps", "-q", "--filter", "name="+testContainerA.name+testContainerA.name).AssertOutWithFunc(func(stdout string) error {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
if len(lines) > 0 {
|
||||
|
@ -652,7 +689,7 @@ func TestContainerListStatusFilter(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(stdout, data.Labels().Get("cID")), "No container found with status created")
|
||||
},
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ package container
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -28,52 +27,97 @@ import (
|
|||
"time"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/icmd"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
)
|
||||
|
||||
func TestLogs(t *testing.T) {
|
||||
t.Parallel()
|
||||
base := testutil.NewBase(t)
|
||||
containerName := testutil.Identifier(t)
|
||||
const expected = `foo
|
||||
bar`
|
||||
bar
|
||||
`
|
||||
|
||||
defer base.Cmd("rm", containerName).Run()
|
||||
base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage,
|
||||
"sh", "-euxc", "echo foo; echo bar").AssertOK()
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
//test since / until flag
|
||||
time.Sleep(3 * time.Second)
|
||||
base.Cmd("logs", "--since", "1s", containerName).AssertOutNotContains(expected)
|
||||
base.Cmd("logs", "--since", "10s", containerName).AssertOutContains(expected)
|
||||
base.Cmd("logs", "--until", "10s", containerName).AssertOutNotContains(expected)
|
||||
base.Cmd("logs", "--until", "1s", containerName).AssertOutContains(expected)
|
||||
if runtime.GOOS == "windows" {
|
||||
testCase.Require = nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/4237")
|
||||
}
|
||||
|
||||
// Ensure follow flag works as expected:
|
||||
base.Cmd("logs", "-f", containerName).AssertOutContains("bar")
|
||||
base.Cmd("logs", "-f", containerName).AssertOutContains("foo")
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
//test timestamps flag
|
||||
base.Cmd("logs", "-t", containerName).AssertOutContains(time.Now().UTC().Format("2006-01-02"))
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "--quiet", "--name", data.Identifier(), testutil.CommonImage, "sh", "-euxc", "echo foo; echo bar;")
|
||||
data.Labels().Set("cID", data.Identifier())
|
||||
}
|
||||
|
||||
//test tail flag
|
||||
base.Cmd("logs", "-n", "all", containerName).AssertOutContains(expected)
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "since 1s",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "--since", "1s", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.DoesNotContain(expected)),
|
||||
},
|
||||
{
|
||||
Description: "since 60s",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "--since", "60s", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals(expected)),
|
||||
},
|
||||
{
|
||||
Description: "until 60s",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "--until", "60s", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.DoesNotContain(expected)),
|
||||
},
|
||||
{
|
||||
Description: "until 1s",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "--until", "1s", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals(expected)),
|
||||
},
|
||||
{
|
||||
Description: "follow",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "-f", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals(expected)),
|
||||
},
|
||||
{
|
||||
Description: "timestamp",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "-t", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Contains(time.Now().UTC().Format("2006-01-02"))),
|
||||
},
|
||||
{
|
||||
Description: "tail flag",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "-n", "all", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals(expected)),
|
||||
},
|
||||
{
|
||||
Description: "tail flag",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "-n", "1", data.Labels().Get("cID"))
|
||||
},
|
||||
// FIXME: why?
|
||||
Expected: test.Expects(0, nil, expect.Match(regexp.MustCompile("^(?:bar\n|)$"))),
|
||||
},
|
||||
}
|
||||
|
||||
base.Cmd("logs", "-n", "1", containerName).AssertOutWithFunc(func(stdout string) error {
|
||||
if !(stdout == "bar\n" || stdout == "") {
|
||||
return fmt.Errorf("expected %q or %q, got %q", "bar", "", stdout)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
base.Cmd("rm", "-f", containerName).AssertOK()
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
// Tests whether `nerdctl logs` properly separates stdout/stderr output
|
||||
|
@ -81,8 +125,13 @@ bar`
|
|||
func TestLogsOutStreamsSeparated(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// Logging seems broken on windows.
|
||||
testCase.Require = nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/4237")
|
||||
}
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(), testutil.CommonImage,
|
||||
helpers.Ensure("run", "--name", data.Identifier(), testutil.CommonImage,
|
||||
"sh", "-euc", "echo stdout1; echo stderr1 >&2; echo stdout2; echo stderr2 >&2")
|
||||
}
|
||||
|
||||
|
@ -91,8 +140,6 @@ func TestLogsOutStreamsSeparated(t *testing.T) {
|
|||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
// Arbitrary, but we need to wait until the logs show up
|
||||
time.Sleep(3 * time.Second)
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
}
|
||||
|
||||
|
@ -105,116 +152,165 @@ func TestLogsOutStreamsSeparated(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLogsWithInheritedFlags(t *testing.T) {
|
||||
// Seen flaky with Docker
|
||||
t.Parallel()
|
||||
base := testutil.NewBase(t)
|
||||
for k, v := range base.Args {
|
||||
if strings.HasPrefix(v, "--namespace=") {
|
||||
base.Args[k] = "-n=" + testutil.Namespace
|
||||
}
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Require = require.Not(nerdtest.Docker)
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("-n="+testutil.Namespace, "run", "--name", data.Identifier(), testutil.CommonImage,
|
||||
"sh", "-euxc", "echo foo; echo bar")
|
||||
}
|
||||
containerName := testutil.Identifier(t)
|
||||
|
||||
defer base.Cmd("rm", containerName).Run()
|
||||
base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage,
|
||||
"sh", "-euxc", "echo foo; echo bar").AssertOK()
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
// It appears this test flakes out with Docker seeing only "foo\n"
|
||||
// Tentatively adding a pause in case this is just slow
|
||||
time.Sleep(time.Second)
|
||||
// test rootCmd alias `-n` already used in logs subcommand
|
||||
base.Cmd("logs", "-n", "1", containerName).AssertOutWithFunc(func(stdout string) error {
|
||||
if !(stdout == "bar\n" || stdout == "") {
|
||||
return fmt.Errorf("expected %q or %q, got %q", "bar", "", stdout)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("-n="+testutil.Namespace, "logs", "-n", "1", data.Identifier())
|
||||
}
|
||||
|
||||
// FIXME: why?
|
||||
testCase.Expected = test.Expects(0, nil, expect.Match(regexp.MustCompile("^(?:bar\n|)$")))
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestLogsOfJournaldDriver(t *testing.T) {
|
||||
testutil.RequireExecutable(t, "journalctl")
|
||||
journalctl, _ := exec.LookPath("journalctl")
|
||||
res := icmd.RunCmd(icmd.Command(journalctl, "-xe"))
|
||||
if res.ExitCode != 0 {
|
||||
t.Skipf("current user is not allowed to access journal logs: %s", res.Combined())
|
||||
const expected = `foo
|
||||
bar
|
||||
`
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Require = require.All(
|
||||
require.Binary("journalctl"),
|
||||
&test.Requirement{
|
||||
Check: func(data test.Data, helpers test.Helpers) (bool, string) {
|
||||
works := false
|
||||
cmd := helpers.Custom("journalctl", "-xe")
|
||||
cmd.Run(&test.Expected{
|
||||
ExitCode: expect.ExitCodeNoCheck,
|
||||
Output: func(stdout string, t tig.T) {
|
||||
if stdout != "" {
|
||||
works = true
|
||||
}
|
||||
},
|
||||
})
|
||||
return works, "Journactl to return data for the current user"
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
base := testutil.NewBase(t)
|
||||
containerName := testutil.Identifier(t)
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "--network", "none", "--log-driver", "journald", "--name", data.Identifier(), testutil.CommonImage,
|
||||
"sh", "-euxc", "echo foo; echo bar")
|
||||
data.Labels().Set("cID", data.Identifier())
|
||||
}
|
||||
|
||||
defer base.Cmd("rm", containerName).Run()
|
||||
base.Cmd("run", "-d", "--network", "none", "--log-driver", "journald", "--name", containerName, testutil.CommonImage,
|
||||
"sh", "-euxc", "echo foo; echo bar").AssertOK()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
base.Cmd("logs", containerName).AssertOutContains("bar")
|
||||
// Run logs twice, make sure that the logs are not removed
|
||||
base.Cmd("logs", containerName).AssertOutContains("foo")
|
||||
|
||||
base.Cmd("logs", "--since", "5s", containerName).AssertOutWithFunc(func(stdout string) error {
|
||||
if !strings.Contains(stdout, "bar") {
|
||||
return fmt.Errorf("expected bar, got %s", stdout)
|
||||
}
|
||||
if !strings.Contains(stdout, "foo") {
|
||||
return fmt.Errorf("expected foo, got %s", stdout)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
base.Cmd("rm", "-f", containerName).AssertOK()
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "logs",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.Equals(expected)),
|
||||
},
|
||||
{
|
||||
Description: "logs --since 60s",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "--since", "60s", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.DoesNotContain("foo", "bar")),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogsWithFailingContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
base := testutil.NewBase(t)
|
||||
containerName := testutil.Identifier(t)
|
||||
defer base.Cmd("rm", containerName).Run()
|
||||
base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage,
|
||||
"sh", "-euxc", "echo foo; echo bar; exit 42; echo baz").AssertOK()
|
||||
time.Sleep(3 * time.Second)
|
||||
// AssertOutContains also asserts that the exit code of the logs command == 0,
|
||||
// even when the container is failing
|
||||
base.Cmd("logs", "-f", containerName).AssertOutContains("bar")
|
||||
base.Cmd("logs", "-f", containerName).AssertOutNotContains("baz")
|
||||
base.Cmd("rm", "-f", containerName).AssertOK()
|
||||
const expected = `foo
|
||||
bar
|
||||
`
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// Logging seems broken on windows.
|
||||
testCase.Require = nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/4237")
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("run", "--name", data.Identifier(), testutil.CommonImage, "sh", "-euxc", "echo foo; echo bar; exit 42; echo baz")
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(0, nil, expect.Equals(expected))
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestLogsWithRunningContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
base := testutil.NewBase(t)
|
||||
containerName := testutil.Identifier(t)
|
||||
defer base.Cmd("rm", "-f", containerName).Run()
|
||||
expected := make([]string, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
expected[i] = fmt.Sprint(i + 1)
|
||||
}
|
||||
|
||||
base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage,
|
||||
"sh", "-euc", "for i in `seq 1 10`; do echo $i; sleep 1; done").AssertOK()
|
||||
base.Cmd("logs", "-f", containerName).AssertOutContainsAll(expected...)
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// Logging seems broken on windows.
|
||||
testCase.Require = nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/4237")
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "--name", data.Identifier(), testutil.CommonImage, "sh", "-euc", "for i in `seq 1 10`; do echo $i; sleep 1; done")
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(0, nil, expect.Contains(expected[0], expected[1:]...))
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestLogsWithoutNewlineOrEOF(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// FIXME: test does not work on Windows yet because containerd doesn't send an exit event appropriately after task exit on Windows")
|
||||
// FIXME: nerdctl behavior does not match docker - test disabled for nerdctl until we fix
|
||||
testCase.Require = require.All(
|
||||
require.Linux,
|
||||
nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/4201"),
|
||||
)
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(), testutil.CommonImage, "printf", "'Hello World!\nThere is no newline'")
|
||||
helpers.Ensure("run", "--name", data.Identifier(), testutil.CommonImage, "printf", "'Hello World!\nThere is no newline'")
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
// FIXME: arbitrary timeouts are by nature a problem.
|
||||
time.Sleep(5 * time.Second)
|
||||
return helpers.Command("logs", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(0, nil, expect.Equals("'Hello World!\nThere is no newline'"))
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
|
@ -222,19 +318,44 @@ func TestLogsAfterRestartingContainer(t *testing.T) {
|
|||
if runtime.GOOS != "linux" {
|
||||
t.Skip("FIXME: test does not work on Windows yet. Restarting a container fails with: failed to create shim task: hcs::CreateComputeSystem <id>: The requested operation for attach namespace failed.: unknown")
|
||||
}
|
||||
t.Parallel()
|
||||
base := testutil.NewBase(t)
|
||||
containerName := testutil.Identifier(t)
|
||||
defer base.Cmd("rm", "-f", containerName).Run()
|
||||
base.Cmd("run", "-d", "--name", containerName, testutil.CommonImage,
|
||||
"printf", "'Hello World!\nThere is no newline'").AssertOK()
|
||||
expected := []string{"Hello World!", "There is no newline"}
|
||||
time.Sleep(3 * time.Second)
|
||||
base.Cmd("logs", "-f", containerName).AssertOutContainsAll(expected...)
|
||||
// restart and check logs again
|
||||
base.Cmd("start", containerName)
|
||||
time.Sleep(3 * time.Second)
|
||||
base.Cmd("logs", "-f", containerName).AssertOutContainsAll(expected...)
|
||||
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "--name", data.Identifier(), testutil.CommonImage,
|
||||
"printf", "'Hello World!\nThere is no newline'")
|
||||
data.Labels().Set("cID", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "logs -f works",
|
||||
NoParallel: true,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "-f", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals("'Hello World!\nThere is no newline'")),
|
||||
},
|
||||
{
|
||||
Description: "logs -f works after restart",
|
||||
NoParallel: true,
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("start", data.Labels().Get("cID"))
|
||||
// FIXME: this is inherently flaky
|
||||
time.Sleep(5 * time.Second)
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", "-f", data.Labels().Get("cID"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Equals("'Hello World!\nThere is no newline''Hello World!\nThere is no newline'")),
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestLogsWithForegroundContainers(t *testing.T) {
|
||||
|
@ -256,10 +377,7 @@ func TestLogsWithForegroundContainers(t *testing.T) {
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.All(
|
||||
expect.Contains("foo", "bar"),
|
||||
expect.DoesNotContain("baz"),
|
||||
)),
|
||||
Expected: test.Expects(0, nil, expect.Equals("foo\nbar\n")),
|
||||
},
|
||||
{
|
||||
Description: "interactive",
|
||||
|
@ -272,10 +390,7 @@ func TestLogsWithForegroundContainers(t *testing.T) {
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.All(
|
||||
expect.Contains("foo", "bar"),
|
||||
expect.DoesNotContain("baz"),
|
||||
)),
|
||||
Expected: test.Expects(0, nil, expect.Equals("foo\nbar\n")),
|
||||
},
|
||||
{
|
||||
Description: "PTY",
|
||||
|
@ -290,10 +405,7 @@ func TestLogsWithForegroundContainers(t *testing.T) {
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.All(
|
||||
expect.Contains("foo", "bar"),
|
||||
expect.DoesNotContain("baz"),
|
||||
)),
|
||||
Expected: test.Expects(0, nil, expect.Equals("foo\nbar\n")),
|
||||
},
|
||||
{
|
||||
Description: "interactivePTY",
|
||||
|
@ -308,69 +420,88 @@ func TestLogsWithForegroundContainers(t *testing.T) {
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.All(
|
||||
expect.Contains("foo", "bar"),
|
||||
expect.DoesNotContain("baz"),
|
||||
)),
|
||||
Expected: test.Expects(0, nil, expect.Equals("foo\nbar\n")),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestTailFollowRotateLogs(t *testing.T) {
|
||||
// FIXME this is flaky by nature... 2 lines is arbitrary, 10000 ms is arbitrary, and both are some sort of educated
|
||||
// guess that things will mostly always kinda work maybe...
|
||||
// Furthermore, parallelizing will put pressure on the daemon which might be even slower in answering, increasing
|
||||
// the risk of transient failure.
|
||||
// This test needs to be rethought entirely
|
||||
// t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("tail log is not supported on Windows")
|
||||
}
|
||||
base := testutil.NewBase(t)
|
||||
containerName := testutil.Identifier(t)
|
||||
|
||||
func TestLogsTailFollowRotate(t *testing.T) {
|
||||
// FIXME this is flaky by nature... the number of lines is arbitrary, the wait is arbitrary,
|
||||
// and both are some sort of educated guess that things will mostly always kinda work maybe...
|
||||
const sampleJSONLog = `{"log":"A\n","stream":"stdout","time":"2024-04-11T12:01:09.800288974Z"}`
|
||||
const linesPerFile = 200
|
||||
|
||||
defer base.Cmd("rm", "-f", containerName).Run()
|
||||
base.Cmd("run", "-d", "--log-driver", "json-file",
|
||||
"--log-opt", fmt.Sprintf("max-size=%d", len(sampleJSONLog)*linesPerFile),
|
||||
"--log-opt", "max-file=10",
|
||||
"--name", containerName, testutil.CommonImage,
|
||||
"sh", "-euc", "while true; do echo A; usleep 100; done").AssertOK()
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
tailLogCmd := base.Cmd("logs", "-f", containerName)
|
||||
tailLogCmd.Timeout = 1000 * time.Millisecond
|
||||
logRun := tailLogCmd.Run()
|
||||
tailLogs := strings.Split(strings.TrimSpace(logRun.Stdout()), "\n")
|
||||
for _, line := range tailLogs {
|
||||
if line != "" {
|
||||
assert.Equal(t, "A", line)
|
||||
}
|
||||
// tail log is not supported on Windows
|
||||
testCase.Require = require.Not(require.Windows)
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--log-driver", "json-file",
|
||||
"--log-opt", fmt.Sprintf("max-size=%d", len(sampleJSONLog)*linesPerFile),
|
||||
"--log-opt", "max-file=10",
|
||||
"--name", data.Identifier(), testutil.CommonImage,
|
||||
"sh", "-euc", "while true; do echo A; usleep 100; done")
|
||||
// FIXME: ... inherently racy...
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
assert.Equal(t, true, len(tailLogs) > linesPerFile, logRun.Stderr())
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
cmd := helpers.Command("logs", "-f", data.Identifier())
|
||||
// FIXME: this is flaky by nature. We assume that the container has started and will output enough in 5 seconds.
|
||||
cmd.WithTimeout(5 * time.Second)
|
||||
return cmd
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(expect.ExitCodeTimeout, nil, func(stdout string, t tig.T) {
|
||||
tailLogs := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
for _, line := range tailLogs {
|
||||
if line != "" {
|
||||
assert.Equal(t, "A", line)
|
||||
}
|
||||
}
|
||||
|
||||
assert.Assert(t, len(tailLogs) > linesPerFile, fmt.Sprintf("expected %d lines or more, found %d", linesPerFile, len(tailLogs)))
|
||||
})
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
func TestNoneLoggerHasNoLogURI(t *testing.T) {
|
||||
|
||||
func TestLogsNoneLoggerHasNoLogURI(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "--name", data.Identifier(), "--log-driver", "none", testutil.CommonImage, "sh", "-euxc", "echo foo")
|
||||
}
|
||||
|
||||
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
}
|
||||
|
||||
testCase.Expected = test.Expects(1, nil, nil)
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestLogsWithDetails(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// FIXME: this is not working on windows. There is some deep issue with windows logs:
|
||||
// https://github.com/containerd/nerdctl/issues/4237
|
||||
if runtime.GOOS == "windows" {
|
||||
testCase.Require = nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/4237")
|
||||
}
|
||||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--log-driver", "json-file",
|
||||
helpers.Ensure("run", "--log-driver", "json-file",
|
||||
"--log-opt", "max-size=10m",
|
||||
"--log-opt", "max-file=3",
|
||||
"--log-opt", "env=ENV",
|
||||
|
@ -401,7 +532,7 @@ func TestLogsFollowNoExtraneousLineFeed(t *testing.T) {
|
|||
|
||||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
// Create a container that outputs a message without a trailing newline
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(), testutil.CommonImage,
|
||||
helpers.Ensure("run", "--name", data.Identifier(), testutil.CommonImage,
|
||||
"sh", "-c", "printf 'Hello without newline'")
|
||||
}
|
||||
|
||||
|
@ -411,8 +542,6 @@ func TestLogsFollowNoExtraneousLineFeed(t *testing.T) {
|
|||
|
||||
testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
// Use logs -f to follow the logs
|
||||
// Arbitrary, but we need to wait until the logs show up
|
||||
time.Sleep(3 * time.Second)
|
||||
return helpers.Command("logs", "-f", data.Identifier())
|
||||
}
|
||||
|
||||
|
@ -425,7 +554,7 @@ func TestLogsFollowNoExtraneousLineFeed(t *testing.T) {
|
|||
func TestLogsWithStartContainer(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// For windows we havent added support for dual logging so not adding the test.
|
||||
// Windows does not support dual logging.
|
||||
testCase.Require = require.Not(require.Windows)
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
|
@ -434,34 +563,28 @@ func TestLogsWithStartContainer(t *testing.T) {
|
|||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
cmd := helpers.Command("run", "-it", "--name", data.Identifier(), testutil.CommonImage)
|
||||
cmd.WithPseudoTTY()
|
||||
cmd.WithFeeder(func() io.Reader {
|
||||
return strings.NewReader("echo foo\nexit\n")
|
||||
})
|
||||
|
||||
cmd.Feed(strings.NewReader("echo foo\nexit\n"))
|
||||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
})
|
||||
|
||||
cmd = helpers.Command("start", "-ia", data.Identifier())
|
||||
cmd.WithPseudoTTY()
|
||||
cmd.Feed(strings.NewReader("echo bar\nexit\n"))
|
||||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
})
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
cmd := helpers.Command("start", "-ia", data.Identifier())
|
||||
cmd.WithPseudoTTY()
|
||||
cmd.WithFeeder(func() io.Reader {
|
||||
return strings.NewReader("echo bar\nexit\n")
|
||||
})
|
||||
cmd.Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
})
|
||||
cmd = helpers.Command("logs", data.Identifier())
|
||||
|
||||
return cmd
|
||||
return helpers.Command("logs", data.Identifier())
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Contains("foo", "bar")),
|
||||
},
|
||||
{
|
||||
// FIXME: is this test safe or could it be racy?
|
||||
Description: "Test logs are captured after stopping and starting a non-interactive container and continue capturing new logs",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier(), testutil.CommonImage, "sh", "-c", "while true; do echo foo; sleep 1; done")
|
||||
|
@ -481,10 +604,10 @@ func TestLogsWithStartContainer(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
finalLogsCount := strings.Count(stdout, "foo")
|
||||
initialFooCount, _ := strconv.Atoi(data.Labels().Get("initialFooCount"))
|
||||
assert.Assert(t, finalLogsCount > initialFooCount, "Expected 'foo' count to increase after restart", info)
|
||||
assert.Assert(t, finalLogsCount > initialFooCount, "Expected 'foo' count to increase after restart")
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/containerd/nerdctl/v2/pkg/clientutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/containerutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker"
|
||||
"github.com/containerd/nerdctl/v2/pkg/portutil"
|
||||
)
|
||||
|
||||
func PortCommand() *cobra.Command {
|
||||
|
@ -81,13 +82,26 @@ func portAction(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
defer cancel()
|
||||
|
||||
dataStore, err := clientutil.DataStore(globalOptions.DataRoot, globalOptions.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
walker := &containerwalker.ContainerWalker{
|
||||
Client: client,
|
||||
OnFound: func(ctx context.Context, found containerwalker.Found) error {
|
||||
if found.MatchCount > 1 {
|
||||
return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req)
|
||||
}
|
||||
return containerutil.PrintHostPort(ctx, cmd.OutOrStdout(), found.Container, argPort, argProto)
|
||||
containerLabels, err := found.Container.Labels(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ports, err := portutil.LoadPortMappings(dataStore, globalOptions.Namespace, found.Container.ID(), containerLabels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return containerutil.PrintHostPort(ctx, cmd.OutOrStdout(), found.Container, argPort, argProto, ports)
|
||||
},
|
||||
}
|
||||
req := args[0]
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/portlock"
|
||||
)
|
||||
|
||||
// iptablesCheckCommand is the shell command to check iptables rules
|
||||
const iptablesCheckCommand = "iptables -t nat -S && iptables -t filter -S && iptables -t mangle -S"
|
||||
|
||||
// testContainerRmIptablesExecutor is a common executor function for testing iptables rules cleanup
|
||||
func testContainerRmIptablesExecutor(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
t := helpers.T()
|
||||
|
||||
// Get the container ID from the label
|
||||
containerID := data.Labels().Get("containerID")
|
||||
|
||||
// Remove the container
|
||||
helpers.Ensure("rm", "-f", containerID)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Create a TestableCommand using helpers.Custom
|
||||
if rootlessutil.IsRootless() {
|
||||
// In rootless mode, we need to enter the rootlesskit network namespace
|
||||
if netns, err := rootlessutil.DetachedNetNS(); err != nil {
|
||||
t.Log(fmt.Sprintf("Failed to get detached network namespace: %v", err))
|
||||
t.FailNow()
|
||||
} else {
|
||||
if netns != "" {
|
||||
// Use containerd-rootless-setuptool.sh to enter the RootlessKit namespace
|
||||
return helpers.Custom("containerd-rootless-setuptool.sh", "nsenter", "--", "nsenter", "--net="+netns, "sh", "-ec", iptablesCheckCommand)
|
||||
}
|
||||
// Enter into :RootlessKit namespace using containerd-rootless-setuptool.sh
|
||||
return helpers.Custom("containerd-rootless-setuptool.sh", "nsenter", "--", "sh", "-ec", iptablesCheckCommand)
|
||||
}
|
||||
}
|
||||
|
||||
// In non-rootless mode, check iptables rules directly on the host
|
||||
return helpers.Custom("sh", "-ec", iptablesCheckCommand)
|
||||
}
|
||||
|
||||
// TestContainerRmIptables tests that iptables rules are cleared after container deletion
|
||||
func TestContainerRmIptables(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
// Require iptables and containerd-rootless-setuptool.sh commands to be available
|
||||
testCase.Require = require.All(
|
||||
require.Binary("iptables"),
|
||||
require.Binary("containerd-rootless-setuptool.sh"),
|
||||
require.Not(require.Windows),
|
||||
require.Not(nerdtest.Docker),
|
||||
)
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "Test iptables rules are cleared after container deletion",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
// Get a free port using portlock
|
||||
port, err := portlock.Acquire(0)
|
||||
if err != nil {
|
||||
helpers.T().Log(fmt.Sprintf("Failed to acquire port: %v", err))
|
||||
helpers.T().FailNow()
|
||||
}
|
||||
data.Labels().Set("port", strconv.Itoa(port))
|
||||
|
||||
// Create a container with port mapping to ensure iptables rules are created
|
||||
containerID := helpers.Capture("run", "-d", "--name", data.Identifier(), "-p", fmt.Sprintf("%d:80", port), testutil.NginxAlpineImage)
|
||||
data.Labels().Set("containerID", containerID)
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
// Make sure container is removed even if test fails
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
|
||||
// Release the acquired port
|
||||
if portStr := data.Labels().Get("port"); portStr != "" {
|
||||
port, _ := strconv.Atoi(portStr)
|
||||
_ = portlock.Release(port)
|
||||
}
|
||||
},
|
||||
Command: testContainerRmIptablesExecutor,
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
// Get the container ID from the label
|
||||
containerID := data.Labels().Get("containerID")
|
||||
return &test.Expected{
|
||||
ExitCode: expect.ExitCodeSuccess,
|
||||
// Verify that the iptables output does not contain the container ID
|
||||
Output: expect.DoesNotContain(containerID),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -153,12 +154,12 @@ func TestRestartWithSignal(t *testing.T) {
|
|||
Output: expect.All(
|
||||
// Check that we saw SIGUSR1 inside the container
|
||||
expect.Contains(nerdtest.SignalCaught),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
// Ensure the container was restarted
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
// Check the new pid is different
|
||||
newpid := strconv.Itoa(nerdtest.InspectContainer(helpers, data.Identifier()).State.Pid)
|
||||
assert.Assert(helpers.T(), newpid != data.Labels().Get("oldpid"), info)
|
||||
assert.Assert(helpers.T(), newpid != data.Labels().Get("oldpid"))
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
@ -234,6 +234,15 @@ func setCreateFlags(cmd *cobra.Command) {
|
|||
// rootfs flags (from Podman)
|
||||
cmd.Flags().Bool("rootfs", false, "The first argument is not an image but the rootfs to the exploded container")
|
||||
|
||||
// Health check flags
|
||||
cmd.Flags().String("health-cmd", "", "Command to run to check health")
|
||||
cmd.Flags().Duration("health-interval", 0, "Time between running the check (default: 30s)")
|
||||
cmd.Flags().Duration("health-timeout", 0, "Maximum time to allow one check to run (default: 30s)")
|
||||
cmd.Flags().Int("health-retries", 0, "Consecutive failures needed to report unhealthy (default: 3)")
|
||||
cmd.Flags().Duration("health-start-period", 0, "Start period for the container to initialize before starting health-retries countdown")
|
||||
cmd.Flags().Duration("health-start-interval", 0, "Time between running the checks during the start period")
|
||||
cmd.Flags().Bool("no-healthcheck", false, "Disable any container-specified HEALTHCHECK")
|
||||
|
||||
// #region env flags
|
||||
// entrypoint needs to be StringArray, not StringSlice, to prevent "FOO=foo1,foo2" from being split to {"FOO=foo1", "foo2"}
|
||||
// entrypoint StringArray is an internal implementation to support `nerdctl compose` entrypoint yaml filed with multiple strings
|
||||
|
@ -367,7 +376,7 @@ func runAction(cmd *cobra.Command, args []string) error {
|
|||
return errors.New("flags -d and -a cannot be specified together")
|
||||
}
|
||||
|
||||
netFlags, err := loadNetworkFlags(cmd)
|
||||
netFlags, err := loadNetworkFlags(cmd, createOpt.GOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load networking flags: %w", err)
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/cmd/container"
|
||||
"github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker"
|
||||
|
@ -134,7 +135,9 @@ func TestRunCgroupV2(t *testing.T) {
|
|||
base.Cmd("exec", testutil.Identifier(t)+"-testUpdate2",
|
||||
"cat", "cpu.max", "memory.max", "memory.swap.max", "memory.low",
|
||||
"pids.max", "cpu.weight", "cpuset.cpus", "cpuset.mems").AssertOutExactly(expected2)
|
||||
|
||||
base.Cmd("run", "--rm", "--security-opt", "writable-cgroups=true", testutil.AlpineImage, "mkdir", "/sys/fs/cgroup/foo").AssertOK()
|
||||
base.Cmd("run", "--rm", "--security-opt", "writable-cgroups=false", testutil.AlpineImage, "mkdir", "/sys/fs/cgroup/foo").AssertFail()
|
||||
base.Cmd("run", "--rm", testutil.AlpineImage, "mkdir", "/sys/fs/cgroup/foo").AssertFail()
|
||||
}
|
||||
|
||||
func TestRunCgroupV1(t *testing.T) {
|
||||
|
@ -176,6 +179,9 @@ func TestRunCgroupV1(t *testing.T) {
|
|||
const expected = "42000\n100000\n0\n44040192\n6291456\n104857600\n0\n42\n2000\n0-1\n"
|
||||
base.Cmd("run", "--rm", "--cpus", "0.42", "--cpuset-mems", "0", "--memory", "42m", "--memory-reservation", "6m", "--memory-swap", "100m", "--memory-swappiness", "0", "--pids-limit", "42", "--cpu-shares", "2000", "--cpuset-cpus", "0-1", testutil.AlpineImage, "cat", quota, period, cpusetMems, memoryLimit, memoryReservation, memorySwap, memorySwappiness, pidsLimit, cpuShare, cpusetCpus).AssertOutExactly(expected)
|
||||
base.Cmd("run", "--rm", "--cpu-quota", "42000", "--cpu-period", "100000", "--cpuset-mems", "0", "--memory", "42m", "--memory-reservation", "6m", "--memory-swap", "100m", "--memory-swappiness", "0", "--pids-limit", "42", "--cpu-shares", "2000", "--cpuset-cpus", "0-1", testutil.AlpineImage, "cat", quota, period, cpusetMems, memoryLimit, memoryReservation, memorySwap, memorySwappiness, pidsLimit, cpuShare, cpusetCpus).AssertOutExactly(expected)
|
||||
base.Cmd("run", "--rm", "--security-opt", "writable-cgroups=true", testutil.AlpineImage, "mkdir", "/sys/fs/cgroup/pids/foo").AssertOK()
|
||||
base.Cmd("run", "--rm", "--security-opt", "writable-cgroups=false", testutil.AlpineImage, "mkdir", "/sys/fs/cgroup/pids/foo").AssertFail()
|
||||
base.Cmd("run", "--rm", testutil.AlpineImage, "mkdir", "/sys/fs/cgroup/pids/foo").AssertFail()
|
||||
}
|
||||
|
||||
// TestIssue3781 tests https://github.com/containerd/nerdctl/issues/3781
|
||||
|
@ -310,7 +316,7 @@ func TestRunDevice(t *testing.T) {
|
|||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("exec", data.Labels().Get("id"), "sh", "-ec", "echo -n \"overwritten-lo1-content\">"+lo[1].Device)
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout string, info string, t *testing.T) {
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, func(stdout string, t tig.T) {
|
||||
lo1Read, err := os.ReadFile(lo[1].Device)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(bytes.Trim(lo1Read, "\x00")), "overwritten-lo1-content")
|
||||
|
@ -523,7 +529,7 @@ func TestRunBlkioSettingCgroupV2(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "{{.HostConfig.BlkioWeight}}", data.Identifier()), "150"))
|
||||
},
|
||||
),
|
||||
|
@ -545,7 +551,7 @@ func TestRunBlkioSettingCgroupV2(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioWeightDevice}}{{.Weight}}{{end}}", data.Identifier())
|
||||
assert.Assert(t, strings.Contains(inspectOut, "100"))
|
||||
},
|
||||
|
@ -574,7 +580,7 @@ func TestRunBlkioSettingCgroupV2(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceReadBps}}{{.Rate}}{{end}}", data.Identifier())
|
||||
assert.Assert(t, strings.Contains(inspectOut, "1048576"))
|
||||
},
|
||||
|
@ -603,7 +609,7 @@ func TestRunBlkioSettingCgroupV2(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceWriteBps}}{{.Rate}}{{end}}", data.Identifier())
|
||||
assert.Assert(t, strings.Contains(inspectOut, "2097152"))
|
||||
},
|
||||
|
@ -632,7 +638,7 @@ func TestRunBlkioSettingCgroupV2(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceReadIOps}}{{.Rate}}{{end}}", data.Identifier())
|
||||
assert.Assert(t, strings.Contains(inspectOut, "1000"))
|
||||
},
|
||||
|
@ -661,7 +667,7 @@ func TestRunBlkioSettingCgroupV2(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
inspectOut := helpers.Capture("inspect", "--format", "{{range .HostConfig.BlkioDeviceWriteIOps}}{{.Rate}}{{end}}", data.Identifier())
|
||||
assert.Assert(t, strings.Contains(inspectOut, "2000"))
|
||||
},
|
||||
|
@ -696,7 +702,7 @@ func TestRunCPURealTimeSettingCgroupV1(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
rtRuntime := helpers.Capture("inspect", "--format", "{{.HostConfig.CPURealtimeRuntime}}", data.Identifier())
|
||||
rtPeriod := helpers.Capture("inspect", "--format", "{{.HostConfig.CPURealtimePeriod}}", data.Identifier())
|
||||
assert.Assert(t, strings.Contains(rtRuntime, "950000"))
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
|
||||
|
@ -548,7 +549,7 @@ func TestRunWithDetachKeys(t *testing.T) {
|
|||
Errors: []error{errors.New("detach keys")},
|
||||
Output: expect.All(
|
||||
expect.Contains("markmark"),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
),
|
||||
|
@ -616,7 +617,7 @@ func TestIssue3568(t *testing.T) {
|
|||
Errors: []error{errors.New("detach keys")},
|
||||
Output: expect.All(
|
||||
expect.Contains("markmark"),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
),
|
||||
|
@ -651,8 +652,8 @@ func TestPortBindingWithCustomHost(t *testing.T) {
|
|||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
resp, err := nettestutil.HTTPGet(address, 30, false)
|
||||
func(stdout string, t tig.T) {
|
||||
resp, err := nettestutil.HTTPGet(address, 5, false)
|
||||
assert.NilError(t, err)
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/containerd/containerd/v2/core/mount"
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
|
||||
|
@ -307,7 +308,7 @@ func TestRunBindMountTmpfs(t *testing.T) {
|
|||
}
|
||||
|
||||
func mountExistsWithOpt(mountPoint, mountOpt string) test.Comparator {
|
||||
return func(stdout, info string, t *testing.T) {
|
||||
return func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
mountOutput := []string{}
|
||||
for _, line := range lines {
|
||||
|
@ -352,6 +353,8 @@ func TestRunBindMountBind(t *testing.T) {
|
|||
"top",
|
||||
)
|
||||
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier("container"))
|
||||
|
||||
// Save host rwDir location and container id for subtests
|
||||
data.Labels().Set("container", data.Identifier("container"))
|
||||
data.Labels().Set("rwDir", rwDir)
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/containerd/nerdctl/v2/pkg/strutil"
|
||||
)
|
||||
|
||||
func loadNetworkFlags(cmd *cobra.Command) (types.NetworkOptions, error) {
|
||||
func loadNetworkFlags(cmd *cobra.Command, globalOpts types.GlobalCommandOptions) (types.NetworkOptions, error) {
|
||||
netOpts := types.NetworkOptions{}
|
||||
|
||||
// --net/--network=<net name> ...
|
||||
|
@ -101,33 +101,58 @@ func loadNetworkFlags(cmd *cobra.Command) (types.NetworkOptions, error) {
|
|||
netOpts.Domainname = domainname
|
||||
|
||||
// --dns=<DNS host> ...
|
||||
dnsSlice, err := cmd.Flags().GetStringSlice("dns")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
// Use command flags if set, otherwise use global config is set
|
||||
var dnsSlice []string
|
||||
if cmd.Flags().Changed("dns") {
|
||||
var err error
|
||||
dnsSlice, err = cmd.Flags().GetStringSlice("dns")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
}
|
||||
} else {
|
||||
dnsSlice = globalOpts.DNS
|
||||
}
|
||||
netOpts.DNSServers = strutil.DedupeStrSlice(dnsSlice)
|
||||
|
||||
// --dns-search=<domain name> ...
|
||||
dnsSearchSlice, err := cmd.Flags().GetStringSlice("dns-search")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
// Use command flags if set, otherwise use global config is set
|
||||
var dnsSearchSlice []string
|
||||
if cmd.Flags().Changed("dns-search") {
|
||||
var err error
|
||||
dnsSearchSlice, err = cmd.Flags().GetStringSlice("dns-search")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
}
|
||||
} else {
|
||||
dnsSearchSlice = globalOpts.DNSSearch
|
||||
}
|
||||
netOpts.DNSSearchDomains = strutil.DedupeStrSlice(dnsSearchSlice)
|
||||
|
||||
// --dns-opt/--dns-option=<resolv.conf line> ...
|
||||
// Use command flags if set, otherwise use global config if set
|
||||
dnsOptions := []string{}
|
||||
|
||||
dnsOptFlags, err := cmd.Flags().GetStringSlice("dns-opt")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
}
|
||||
dnsOptions = append(dnsOptions, dnsOptFlags...)
|
||||
// Check if either dns-opt or dns-option flags were set
|
||||
dnsOptChanged := cmd.Flags().Changed("dns-opt")
|
||||
dnsOptionChanged := cmd.Flags().Changed("dns-option")
|
||||
|
||||
dnsOptionFlags, err := cmd.Flags().GetStringSlice("dns-option")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
if dnsOptChanged || dnsOptionChanged {
|
||||
// Use command flags
|
||||
dnsOptFlags, err := cmd.Flags().GetStringSlice("dns-opt")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
}
|
||||
dnsOptions = append(dnsOptions, dnsOptFlags...)
|
||||
|
||||
dnsOptionFlags, err := cmd.Flags().GetStringSlice("dns-option")
|
||||
if err != nil {
|
||||
return netOpts, err
|
||||
}
|
||||
dnsOptions = append(dnsOptions, dnsOptionFlags...)
|
||||
} else {
|
||||
// Use global config defaults
|
||||
dnsOptions = append(dnsOptions, globalOpts.DNSOpts...)
|
||||
}
|
||||
dnsOptions = append(dnsOptions, dnsOptionFlags...)
|
||||
|
||||
netOpts.DNSResolvConfOptions = strutil.DedupeStrSlice(dnsOptions)
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ func baseTestRunPort(t *testing.T, nginxImage string, nginxIndexHTMLSnippet stri
|
|||
hostPort: "7000-7005",
|
||||
containerPort: "80-85",
|
||||
connectURLPort: 7001,
|
||||
err: "error after 30 attempts",
|
||||
err: "error after 5 attempts",
|
||||
runShouldSuccess: true,
|
||||
},
|
||||
{
|
||||
|
@ -209,7 +209,7 @@ func baseTestRunPort(t *testing.T, nginxImage string, nginxIndexHTMLSnippet stri
|
|||
return
|
||||
}
|
||||
|
||||
resp, err := nettestutil.HTTPGet(connectURL, 30, false)
|
||||
resp, err := nettestutil.HTTPGet(connectURL, 5, false)
|
||||
if tc.err != "" {
|
||||
assert.ErrorContains(t, err, tc.err)
|
||||
return
|
||||
|
|
|
@ -36,10 +36,10 @@ import (
|
|||
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/pkg/netns"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -247,7 +247,7 @@ func TestRunPortWithNoHostPort(t *testing.T) {
|
|||
return
|
||||
}
|
||||
connectURL := fmt.Sprintf("http://%s:%s", "127.0.0.1", paramsMap["portNumber"])
|
||||
resp, err := nettestutil.HTTPGet(connectURL, 30, false)
|
||||
resp, err := nettestutil.HTTPGet(connectURL, 5, false)
|
||||
assert.NilError(t, err)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
assert.NilError(t, err)
|
||||
|
@ -332,7 +332,7 @@ func TestUniqueHostPortAssignement(t *testing.T) {
|
|||
|
||||
// Make HTTP GET request to container 1
|
||||
connectURL1 := fmt.Sprintf("http://%s:%s", "127.0.0.1", port1)
|
||||
resp1, err := nettestutil.HTTPGet(connectURL1, 30, false)
|
||||
resp1, err := nettestutil.HTTPGet(connectURL1, 5, false)
|
||||
assert.NilError(t, err)
|
||||
respBody1, err := io.ReadAll(resp1.Body)
|
||||
assert.NilError(t, err)
|
||||
|
@ -340,7 +340,7 @@ func TestUniqueHostPortAssignement(t *testing.T) {
|
|||
|
||||
// Make HTTP GET request to container 2
|
||||
connectURL2 := fmt.Sprintf("http://%s:%s", "127.0.0.1", port2)
|
||||
resp2, err := nettestutil.HTTPGet(connectURL2, 30, false)
|
||||
resp2, err := nettestutil.HTTPGet(connectURL2, 5, false)
|
||||
assert.NilError(t, err)
|
||||
respBody2, err := io.ReadAll(resp2.Body)
|
||||
assert.NilError(t, err)
|
||||
|
@ -349,29 +349,81 @@ func TestUniqueHostPortAssignement(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHostPortAlreadyInUse(t *testing.T) {
|
||||
testCases := []struct {
|
||||
hostPort string
|
||||
containerPort string
|
||||
}{
|
||||
{
|
||||
hostPort: "5000",
|
||||
containerPort: "80/tcp",
|
||||
},
|
||||
{
|
||||
hostPort: "5000",
|
||||
containerPort: "80/tcp",
|
||||
},
|
||||
{
|
||||
hostPort: "5000",
|
||||
containerPort: "80/udp",
|
||||
},
|
||||
{
|
||||
hostPort: "5000",
|
||||
containerPort: "80/sctp",
|
||||
},
|
||||
}
|
||||
|
||||
tID := testutil.Identifier(t)
|
||||
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
tcName := fmt.Sprintf("%+v", tc)
|
||||
t.Run(tcName, func(t *testing.T) {
|
||||
if strings.Contains(tc.containerPort, "sctp") && rootlessutil.IsRootless() {
|
||||
t.Skip("sctp is not supported in rootless mode")
|
||||
}
|
||||
testContainerName1 := fmt.Sprintf("%s-%d-1", tID, i)
|
||||
testContainerName2 := fmt.Sprintf("%s-%d-2", tID, i)
|
||||
base := testutil.NewBase(t)
|
||||
t.Cleanup(func() {
|
||||
base.Cmd("rm", "-f", testContainerName1, testContainerName2).AssertOK()
|
||||
})
|
||||
pFlag := fmt.Sprintf("%s:%s", tc.hostPort, tc.containerPort)
|
||||
cmd1 := base.Cmd("run", "-d",
|
||||
"--name", testContainerName1, "-p",
|
||||
pFlag,
|
||||
testutil.NginxAlpineImage)
|
||||
|
||||
cmd2 := base.Cmd("run", "-d",
|
||||
"--name", testContainerName2, "-p",
|
||||
pFlag,
|
||||
testutil.NginxAlpineImage)
|
||||
|
||||
cmd1.AssertOK()
|
||||
cmd2.AssertFail()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPort(t *testing.T) {
|
||||
baseTestRunPort(t, testutil.NginxAlpineImage, testutil.NginxAlpineIndexHTMLSnippet, true)
|
||||
}
|
||||
|
||||
func TestRunWithInvalidPortThenCleanUp(t *testing.T) {
|
||||
func TestRunWithManyPortsThenCleanUp(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
// docker does not set label restriction to 4096 bytes
|
||||
testCase.Require = require.Not(nerdtest.Docker)
|
||||
|
||||
testCase.SubTests = []*test.Case{
|
||||
{
|
||||
Description: "Run a container with invalid ports, and then clean up.",
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "--data-root", data.Temp().Path(), "-f", data.Identifier())
|
||||
},
|
||||
Description: "Run a container with many ports, and then clean up.",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("run", "--data-root", data.Temp().Path(), "--rm", "--name", data.Identifier(), "-p", "22200-22299:22200-22299", testutil.CommonImage)
|
||||
return helpers.Command("run", "--data-root", data.Temp().Path(), "--rm", "-p", "22200-22299:22200-22299", testutil.CommonImage)
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errdefs.ErrInvalidArgument},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, t tig.T) {
|
||||
getAddrHash := func(addr string) string {
|
||||
const addrHashLen = 8
|
||||
|
||||
|
@ -518,158 +570,103 @@ func TestSharedNetworkSetup(t *testing.T) {
|
|||
testCase := &test.Case{
|
||||
Require: require.Not(require.Windows),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
data.Labels().Set("containerName1", data.Identifier("-container1"))
|
||||
containerName1 := data.Labels().Get("containerName1")
|
||||
helpers.Ensure("run", "-d", "--name", containerName1,
|
||||
testutil.NginxAlpineImage)
|
||||
data.Labels().Set("container1", data.Identifier("container1"))
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier("container1"),
|
||||
testutil.CommonImage, "sleep", "inf")
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier("container1"))
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier("-container1"))
|
||||
helpers.Anyhow("rm", "-f", data.Identifier("container1"))
|
||||
},
|
||||
SubTests: []*test.Case{
|
||||
{
|
||||
Description: "Test network is shared",
|
||||
NoParallel: true, // The validation involves starting of the main container: container1
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
helpers.Anyhow("rm", "-f", data.Identifier("container2"))
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
containerName2 := data.Identifier()
|
||||
cmd := helpers.Command()
|
||||
cmd.WithArgs("run", "-d", "--name", containerName2,
|
||||
"--network=container:"+data.Labels().Get("containerName1"),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure(
|
||||
"run", "-d", "--name", data.Identifier("container2"),
|
||||
"--network=container:"+data.Labels().Get("container1"),
|
||||
testutil.NginxAlpineImage)
|
||||
return cmd
|
||||
data.Labels().Set("container2", data.Identifier("container2"))
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier("container2"))
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
containerName2 := data.Identifier()
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("exec", containerName2, "wget", "-qO-", "http://127.0.0.1:80"), testutil.NginxAlpineIndexHTMLSnippet), info)
|
||||
helpers.Ensure("restart", data.Labels().Get("containerName1"))
|
||||
helpers.Ensure("stop", "--time=1", containerName2)
|
||||
helpers.Ensure("start", containerName2)
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("exec", containerName2, "wget", "-qO-", "http://127.0.0.1:80"), testutil.NginxAlpineIndexHTMLSnippet), info)
|
||||
SubTests: []*test.Case{
|
||||
{
|
||||
NoParallel: true,
|
||||
Description: "Test network is shared",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("exec", data.Labels().Get("container2"), "wget", "-qO-", "http://127.0.0.1:80")
|
||||
},
|
||||
}
|
||||
Expected: test.Expects(0, nil, expect.Contains(testutil.NginxAlpineIndexHTMLSnippet)),
|
||||
},
|
||||
{
|
||||
NoParallel: true,
|
||||
Description: "Test network is shared after restart",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("restart", data.Labels().Get("container1"))
|
||||
helpers.Ensure("stop", "--time=1", data.Labels().Get("container2"))
|
||||
helpers.Ensure("start", data.Labels().Get("container2"))
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Labels().Get("container2"))
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("exec", data.Labels().Get("container2"), "wget", "-qO-", "http://127.0.0.1:80")
|
||||
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Contains(testutil.NginxAlpineIndexHTMLSnippet)),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Test uts is supported in shared network",
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
containerName2 := data.Identifier()
|
||||
cmd := helpers.Command()
|
||||
cmd.WithArgs("run", "-d", "--name", containerName2, "--uts", "host",
|
||||
"--network=container:"+data.Labels().Get("containerName1"),
|
||||
testutil.AlpineImage)
|
||||
return cmd
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
}
|
||||
return helpers.Command("run", "--rm", "--uts", "host",
|
||||
"--network=container:"+data.Labels().Get("container1"),
|
||||
testutil.CommonImage)
|
||||
},
|
||||
Expected: test.Expects(0, nil, nil),
|
||||
},
|
||||
{
|
||||
Description: "Test dns is not supported",
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
containerName2 := data.Identifier()
|
||||
cmd := helpers.Command()
|
||||
cmd.WithArgs("run", "-d", "--name", containerName2, "--dns", "0.1.2.3",
|
||||
"--network=container:"+data.Labels().Get("containerName1"),
|
||||
testutil.AlpineImage)
|
||||
return cmd
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
if nerdtest.IsDocker() {
|
||||
return &test.Expected{
|
||||
ExitCode: 125,
|
||||
}
|
||||
|
||||
}
|
||||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
}
|
||||
return helpers.Command("run", "--rm", "--dns", "0.1.2.3",
|
||||
"--network=container:"+data.Labels().Get("container1"),
|
||||
testutil.CommonImage)
|
||||
},
|
||||
// 1 for nerdctl, 125 for docker
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, nil, nil),
|
||||
},
|
||||
{
|
||||
Description: "Test dns options is not supported",
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
containerName2 := data.Identifier()
|
||||
cmd := helpers.Command()
|
||||
cmd.WithArgs("run", "--name", containerName2, "--dns-option", "attempts:5",
|
||||
"--network=container:"+data.Labels().Get("containerName1"),
|
||||
testutil.AlpineImage, "cat", "/etc/resolv.conf")
|
||||
return cmd
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
// The Option doesnt throw an error but is never inserted to the resolv.conf
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, "attempts:5"), info)
|
||||
},
|
||||
}
|
||||
return helpers.Command("run", "--rm", "--dns-option", "attempts:5",
|
||||
"--network=container:"+data.Labels().Get("container1"),
|
||||
testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
},
|
||||
// The Option doesn't throw an error but is never inserted to the resolv.conf
|
||||
Expected: test.Expects(0, nil, expect.DoesNotContain("attempts:5")),
|
||||
},
|
||||
{
|
||||
Description: "Test publish is not supported",
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
containerName2 := data.Identifier()
|
||||
cmd := helpers.Command()
|
||||
cmd.WithArgs("run", "-d", "--name", containerName2, "--publish", "80:8080",
|
||||
"--network=container:"+data.Labels().Get("containerName1"),
|
||||
return helpers.Command("run", "--rm", "--publish", "80:8080",
|
||||
"--network=container:"+data.Labels().Get("container1"),
|
||||
testutil.AlpineImage)
|
||||
return cmd
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
if nerdtest.IsDocker() {
|
||||
return &test.Expected{
|
||||
ExitCode: 125,
|
||||
}
|
||||
|
||||
}
|
||||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
}
|
||||
},
|
||||
// 1 for nerdctl, 125 for docker
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, nil, nil),
|
||||
},
|
||||
{
|
||||
Description: "Test hostname is not supported",
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
containerName2 := data.Identifier()
|
||||
cmd := helpers.Command()
|
||||
cmd.WithArgs("run", "-d", "--name", containerName2, "--hostname", "test",
|
||||
"--network=container:"+data.Labels().Get("containerName1"),
|
||||
return helpers.Command("run", "--rm", "--hostname", "test",
|
||||
"--network=container:"+data.Labels().Get("container1"),
|
||||
testutil.AlpineImage)
|
||||
return cmd
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
if nerdtest.IsDocker() {
|
||||
return &test.Expected{
|
||||
ExitCode: 125,
|
||||
}
|
||||
|
||||
}
|
||||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
}
|
||||
},
|
||||
// 1 for nerdctl, 125 for docker
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, nil, nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -682,15 +679,15 @@ func TestSharedNetworkWithNone(t *testing.T) {
|
|||
Require: require.Not(require.Windows),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("run", "-d", "--name", data.Identifier("container1"), "--network", "none",
|
||||
testutil.NginxAlpineImage)
|
||||
testutil.CommonImage, "sleep", "inf")
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier("container1"))
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier("container1"))
|
||||
helpers.Anyhow("rm", "-f", data.Identifier("container2"))
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("run", "-d", "--name", data.Identifier("container2"),
|
||||
"--network=container:"+data.Identifier("container1"), testutil.NginxAlpineImage)
|
||||
return helpers.Command("run", "--rm",
|
||||
"--network=container:"+data.Identifier("container1"), testutil.CommonImage)
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, nil),
|
||||
}
|
||||
|
@ -914,14 +911,15 @@ func TestNoneNetworkHostName(t *testing.T) {
|
|||
nerdtest.Setup()
|
||||
testCase := &test.Case{
|
||||
Require: require.Not(require.Windows),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
output := helpers.Capture("run", "-d", "--name", data.Identifier(), "--network", "none", testutil.NginxAlpineImage)
|
||||
assert.Assert(helpers.T(), len(output) > 12, output)
|
||||
data.Labels().Set("hostname", output[:12])
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
output := helpers.Capture("run", "-d", "--name", data.Identifier(), "--network", "none", testutil.CommonImage, "sleep", "inf")
|
||||
assert.Assert(helpers.T(), len(output) > 12, output)
|
||||
data.Labels().Set("hostname", output[:12])
|
||||
nerdtest.EnsureContainerStarted(helpers, data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("exec", data.Identifier(), "cat", "/etc/hostname")
|
||||
},
|
||||
|
@ -939,20 +937,20 @@ func TestHostNetworkHostName(t *testing.T) {
|
|||
testCase := &test.Case{
|
||||
Require: require.Not(require.Windows),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
data.Labels().Set("containerName1", data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
helpers.Custom("cat", "/etc/hostname").Run(&test.Expected{
|
||||
Output: func(stdout string, t tig.T) {
|
||||
data.Labels().Set("hostHostname", stdout)
|
||||
},
|
||||
})
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Custom("cat", "/etc/hostname")
|
||||
return helpers.Command("run", "--rm",
|
||||
"--network", "host",
|
||||
testutil.AlpineImage, "cat", "/etc/hostname")
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
hostname := stdout
|
||||
assert.Assert(t, strings.Compare(strings.TrimSpace(helpers.Capture("run", "--name", data.Identifier(), "--network", "host", testutil.AlpineImage, "cat", "/etc/hostname")), strings.TrimSpace(hostname)) == 0, info)
|
||||
},
|
||||
Output: expect.Equals(data.Labels().Get("hostHostname")),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -963,27 +961,18 @@ func TestNoneNetworkDnsConfigs(t *testing.T) {
|
|||
nerdtest.Setup()
|
||||
testCase := &test.Case{
|
||||
Require: require.Not(require.Windows),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
data.Labels().Set("containerName1", data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("run", "-d", "--name", data.Identifier(), "--network", "none", "--dns", "0.1.2.3", "--dns-search", "example.com", "--dns-option", "timeout:3", "--dns-option", "attempts:5", testutil.NginxAlpineImage)
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
out := helpers.Capture("exec", data.Identifier(), "cat", "/etc/resolv.conf")
|
||||
assert.Assert(t, strings.Contains(out, "0.1.2.3"), info)
|
||||
assert.Assert(t, strings.Contains(out, "example.com"), info)
|
||||
assert.Assert(t, strings.Contains(out, "attempts:5"), info)
|
||||
assert.Assert(t, strings.Contains(out, "timeout:3"), info)
|
||||
|
||||
},
|
||||
}
|
||||
return helpers.Command("run", "--rm",
|
||||
"--network", "none",
|
||||
"--dns", "0.1.2.3", "--dns-search", "example.com", "--dns-option", "timeout:3", "--dns-option", "attempts:5",
|
||||
testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
},
|
||||
Expected: test.Expects(0, nil, expect.Contains(
|
||||
"0.1.2.3",
|
||||
"example.com",
|
||||
"attempts:5",
|
||||
"timeout:3",
|
||||
)),
|
||||
}
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
@ -992,26 +981,101 @@ func TestHostNetworkDnsConfigs(t *testing.T) {
|
|||
nerdtest.Setup()
|
||||
testCase := &test.Case{
|
||||
Require: require.Not(require.Windows),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
data.Labels().Set("containerName1", data.Identifier())
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("run", "-d", "--name", data.Identifier(), "--network", "host", "--dns", "0.1.2.3", "--dns-search", "example.com", "--dns-option", "timeout:3", "--dns-option", "attempts:5", testutil.NginxAlpineImage)
|
||||
return helpers.Command("run", "--rm",
|
||||
"--network", "host",
|
||||
"--dns", "0.1.2.3", "--dns-search", "example.com", "--dns-option", "timeout:3", "--dns-option", "attempts:5",
|
||||
testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
out := helpers.Capture("exec", data.Identifier(), "cat", "/etc/resolv.conf")
|
||||
assert.Assert(t, strings.Contains(out, "0.1.2.3"), info)
|
||||
assert.Assert(t, strings.Contains(out, "example.com"), info)
|
||||
assert.Assert(t, strings.Contains(out, "attempts:5"), info)
|
||||
assert.Assert(t, strings.Contains(out, "timeout:3"), info)
|
||||
Expected: test.Expects(0, nil, expect.Contains(
|
||||
"0.1.2.3",
|
||||
"example.com",
|
||||
"attempts:5",
|
||||
"timeout:3",
|
||||
)),
|
||||
}
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestDNSWithGlobalConfig(t *testing.T) {
|
||||
var configContent test.ConfigValue = `debug = false
|
||||
debug_full = false
|
||||
dns = ["10.10.10.10", "20.20.20.20"]
|
||||
dns_opts = ["ndots:2", "timeout:5"]
|
||||
dns_search = ["example.com", "test.local"]`
|
||||
|
||||
nerdtest.Setup()
|
||||
|
||||
testCase := &test.Case{
|
||||
Config: test.WithConfig(nerdtest.NerdctlToml, configContent),
|
||||
// NERDCTL_TOML not supported in Docker
|
||||
Require: require.Not(nerdtest.Docker),
|
||||
SubTests: []*test.Case{
|
||||
{
|
||||
Description: "Global DNS settings are used when command line options are not provided",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
|
||||
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
|
||||
cmd := helpers.Command("run", "--rm", testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
return cmd
|
||||
},
|
||||
}
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.Contains("nameserver 10.10.10.10"),
|
||||
expect.Contains("nameserver 20.20.20.20"),
|
||||
expect.Contains("search example.com test.local"),
|
||||
expect.Contains("options ndots:2 timeout:5"),
|
||||
)),
|
||||
},
|
||||
{
|
||||
Description: "Command line DNS options override global config",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
|
||||
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
|
||||
cmd := helpers.Command("run", "--rm",
|
||||
"--dns", "9.9.9.9",
|
||||
"--dns-search", "override.com",
|
||||
"--dns-opt", "ndots:3",
|
||||
testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
return cmd
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.Contains("nameserver 9.9.9.9"),
|
||||
expect.Contains("search override.com"),
|
||||
expect.Contains("options ndots:3"),
|
||||
)),
|
||||
},
|
||||
{
|
||||
Description: "Global DNS settings should also apply when using host network",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
|
||||
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
|
||||
cmd := helpers.Command("run", "--rm", "--network", "host",
|
||||
testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
return cmd
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.Contains("nameserver 10.10.10.10"),
|
||||
expect.Contains("nameserver 20.20.20.20"),
|
||||
expect.Contains("search example.com test.local"),
|
||||
expect.Contains("options ndots:2 timeout:5"),
|
||||
)),
|
||||
},
|
||||
{
|
||||
Description: "Global DNS settings should also apply when using none network",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
|
||||
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
|
||||
cmd := helpers.Command("run", "--rm", "--network", "none",
|
||||
testutil.CommonImage, "cat", "/etc/resolv.conf")
|
||||
return cmd
|
||||
},
|
||||
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
|
||||
expect.Contains("nameserver 10.10.10.10"),
|
||||
expect.Contains("nameserver 20.20.20.20"),
|
||||
expect.Contains("search example.com test.local"),
|
||||
expect.Contains("options ndots:2 timeout:5"),
|
||||
)),
|
||||
},
|
||||
},
|
||||
}
|
||||
testCase.Run(t)
|
||||
|
|
|
@ -69,7 +69,7 @@ func TestRunRestart(t *testing.T) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
assert.NilError(t, check(30))
|
||||
assert.NilError(t, check(5))
|
||||
|
||||
base.KillDaemon()
|
||||
base.EnsureDaemonActive()
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -44,7 +45,7 @@ func TestRunSoci(t *testing.T) {
|
|||
testCase.Setup = func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Custom("mount").Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
data.Labels().Set("beforeCount", strconv.Itoa(strings.Count(stdout, "fuse.rawBridge")))
|
||||
},
|
||||
})
|
||||
|
@ -60,12 +61,12 @@ func TestRunSoci(t *testing.T) {
|
|||
|
||||
testCase.Expected = func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var afterCount int
|
||||
beforeCount, _ := strconv.Atoi(data.Labels().Get("beforeCount"))
|
||||
|
||||
helpers.Custom("mount").Run(&test.Expected{
|
||||
Output: func(stdout, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
afterCount = strings.Count(stdout, "fuse.rawBridge")
|
||||
},
|
||||
})
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -156,7 +157,7 @@ func TestRunExitCode(t *testing.T) {
|
|||
Output: expect.All(
|
||||
expect.Match(regexp.MustCompile("Exited [(]123[)][A-Za-z0-9 ]+"+data.Identifier("exit123"))),
|
||||
expect.Match(regexp.MustCompile("Exited [(]0[)][A-Za-z0-9 ]+"+data.Identifier("exit0"))),
|
||||
func(stdout, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Equal(t, nerdtest.InspectContainer(helpers, data.Identifier("exit0")).State.Status, "exited")
|
||||
assert.Equal(t, nerdtest.InspectContainer(helpers, data.Identifier("exit123")).State.Status, "exited")
|
||||
},
|
||||
|
@ -837,3 +838,223 @@ func TestRunDomainname(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunHealthcheckFlags(t *testing.T) {
|
||||
testCase := nerdtest.Setup()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
shouldFail bool
|
||||
expectTest []string
|
||||
expectRetries int
|
||||
expectInterval time.Duration
|
||||
expectTimeout time.Duration
|
||||
expectStartPeriod time.Duration
|
||||
}{
|
||||
{
|
||||
name: "Valid_full_config",
|
||||
args: []string{
|
||||
"--health-cmd", "curl -f http://localhost || exit 1",
|
||||
"--health-interval", "30s",
|
||||
"--health-timeout", "5s",
|
||||
"--health-retries", "3",
|
||||
"--health-start-period", "2s",
|
||||
},
|
||||
expectTest: []string{"CMD-SHELL", "curl -f http://localhost || exit 1"},
|
||||
expectInterval: 30 * time.Second,
|
||||
expectTimeout: 5 * time.Second,
|
||||
expectRetries: 3,
|
||||
expectStartPeriod: 2 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "No_healthcheck",
|
||||
args: []string{
|
||||
"--no-healthcheck",
|
||||
},
|
||||
expectTest: []string{"NONE"},
|
||||
},
|
||||
{
|
||||
name: "No_healthcheck_flag",
|
||||
args: []string{},
|
||||
expectTest: nil,
|
||||
},
|
||||
{
|
||||
name: "Conflicting_flags",
|
||||
args: []string{
|
||||
"--no-healthcheck", "--health-cmd", "true",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "Negative_retries",
|
||||
args: []string{
|
||||
"--health-cmd", "true",
|
||||
"--health-retries", "-2",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "Negative_timeout",
|
||||
args: []string{
|
||||
"--health-cmd", "true",
|
||||
"--health-timeout", "-5s",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid_timeout_format",
|
||||
args: []string{
|
||||
"--health-cmd", "true",
|
||||
"--health-timeout", "5blah",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "Health_cmd_cmd_shell",
|
||||
args: []string{
|
||||
"--health-cmd", "curl -f http://localhost || exit 1",
|
||||
},
|
||||
expectTest: []string{"CMD-SHELL", "curl -f http://localhost || exit 1"},
|
||||
},
|
||||
{
|
||||
name: "Health_cmd_array_like",
|
||||
args: []string{
|
||||
"--health-cmd", "echo hello",
|
||||
},
|
||||
expectTest: []string{"CMD-SHELL", "echo hello"},
|
||||
},
|
||||
{
|
||||
name: "Health_cmd_empty",
|
||||
args: []string{
|
||||
"--health-cmd", "",
|
||||
"--health-retries", "2",
|
||||
},
|
||||
expectTest: nil,
|
||||
expectRetries: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
testCase.SubTests = append(testCase.SubTests, &test.Case{
|
||||
Description: tc.name,
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
args := append([]string{"run", "-d", "--name", tc.name}, tc.args...)
|
||||
args = append(args, testutil.CommonImage, "sleep", "infinity")
|
||||
return helpers.Command(args...)
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
if tc.shouldFail {
|
||||
return &test.Expected{
|
||||
ExitCode: expect.ExitCodeGenericFail,
|
||||
}
|
||||
}
|
||||
return &test.Expected{
|
||||
ExitCode: expect.ExitCodeSuccess,
|
||||
Output: expect.All(
|
||||
func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, tc.name)
|
||||
hc := inspect.Config.Healthcheck
|
||||
if tc.expectTest == nil {
|
||||
assert.Assert(t, hc == nil || len(hc.Test) == 0)
|
||||
} else {
|
||||
assert.Assert(t, hc != nil)
|
||||
assert.DeepEqual(t, hc.Test, tc.expectTest)
|
||||
}
|
||||
if tc.expectRetries > 0 {
|
||||
assert.Equal(t, hc.Retries, tc.expectRetries)
|
||||
}
|
||||
if tc.expectTimeout > 0 {
|
||||
assert.Equal(t, hc.Timeout, tc.expectTimeout)
|
||||
}
|
||||
if tc.expectInterval > 0 {
|
||||
assert.Equal(t, hc.Interval, tc.expectInterval)
|
||||
}
|
||||
if tc.expectStartPeriod > 0 {
|
||||
assert.Equal(t, hc.StartPeriod, tc.expectStartPeriod)
|
||||
}
|
||||
},
|
||||
),
|
||||
}
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", tc.name)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
||||
func TestRunHealthcheckFromImage(t *testing.T) {
|
||||
nerdtest.Setup()
|
||||
|
||||
dockerfile := fmt.Sprintf(`FROM %s
|
||||
HEALTHCHECK --interval=30s --timeout=10s CMD wget -q --spider http://localhost:8080 || exit 1
|
||||
`, testutil.CommonImage)
|
||||
|
||||
testCase := &test.Case{
|
||||
Require: nerdtest.Build,
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
data.Labels().Set("image", data.Identifier())
|
||||
helpers.Ensure("build", "-t", data.Labels().Get("image"), data.Temp().Path())
|
||||
},
|
||||
SubTests: []*test.Case{
|
||||
{
|
||||
Description: "merge_with_image",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("run", "-d", "--name", data.Identifier(),
|
||||
"--health-retries=5",
|
||||
"--health-interval=45s",
|
||||
data.Labels().Get("image"))
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: expect.ExitCodeSuccess,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
hc := inspect.Config.Healthcheck
|
||||
assert.Assert(t, hc != nil, "expected healthcheck config to be present")
|
||||
assert.DeepEqual(t, hc.Test, []string{"CMD-SHELL", "wget -q --spider http://localhost:8080 || exit 1"})
|
||||
assert.Equal(t, 5, hc.Retries) // From CLI flags
|
||||
assert.Equal(t, 45*time.Second, hc.Interval) // From CLI flags
|
||||
assert.Equal(t, 10*time.Second, hc.Timeout) // From Dockerfile
|
||||
}),
|
||||
}
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "Disable image health checks via runtime flag",
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command(
|
||||
"run", "-d", "--name", data.Identifier(),
|
||||
"--no-healthcheck",
|
||||
data.Labels().Get("image"),
|
||||
)
|
||||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: expect.ExitCodeSuccess,
|
||||
Output: expect.All(func(stdout string, t tig.T) {
|
||||
inspect := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
hc := inspect.Config.Healthcheck
|
||||
assert.Assert(t, hc != nil, "expected healthcheck config to be present")
|
||||
assert.DeepEqual(t, hc.Test, []string{"NONE"})
|
||||
}),
|
||||
}
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rm", "-f", data.Identifier())
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCase.Run(t)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -222,12 +223,13 @@ func TestUsernsMappingRunCmd(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
actualHostUID, err := getContainerHostUID(helpers, data.Identifier())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get container host UID: %v", err)
|
||||
t.Log(fmt.Sprintf("Failed to get container host UID: %v", err))
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"), info)
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"))
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -249,12 +251,13 @@ func TestUsernsMappingRunCmd(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
actualHostUID, err := getContainerHostUID(helpers, data.Identifier())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get container host UID: %v", err)
|
||||
t.Log(fmt.Sprintf("Failed to get container host UID: %v", err))
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"), info)
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"))
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -295,12 +298,13 @@ func TestUsernsMappingRunCmd(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
actualHostUID, err := getContainerHostUID(helpers, data.Identifier())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get container host UID: %v", err)
|
||||
t.Log(fmt.Sprintf("Failed to get container host UID: %v", err))
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"), info)
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"))
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -322,12 +326,13 @@ func TestUsernsMappingRunCmd(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
actualHostUID, err := getContainerHostUID(helpers, data.Identifier())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get container host UID: %v", err)
|
||||
t.Log(fmt.Sprintf("Failed to get container host UID: %v", err))
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"), info)
|
||||
assert.Assert(t, actualHostUID == data.Labels().Get("expectedHostUID"))
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -367,12 +372,13 @@ func TestUsernsMappingRunCmd(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
actualHostUID, err := getContainerHostUID(helpers, data.Identifier())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get container host UID: %v", err)
|
||||
t.Log(fmt.Sprintf("Failed to get container host UID: %v", err))
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Assert(t, actualHostUID == "0", info)
|
||||
assert.Assert(t, actualHostUID == "0")
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -67,7 +68,7 @@ func TestStartDetachKeys(t *testing.T) {
|
|||
ExitCode: 0,
|
||||
Errors: []error{errors.New("detach keys")},
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("inspect", "--format", "json", data.Identifier()), "\"Running\":true"))
|
||||
},
|
||||
),
|
||||
|
|
|
@ -66,14 +66,14 @@ func TestStopStart(t *testing.T) {
|
|||
return nil
|
||||
}
|
||||
|
||||
assert.NilError(t, check(30))
|
||||
assert.NilError(t, check(5))
|
||||
base.Cmd("stop", testContainerName).AssertOK()
|
||||
base.Cmd("exec", testContainerName, "ps").AssertFail()
|
||||
if check(1) == nil {
|
||||
t.Fatal("expected to get an error")
|
||||
}
|
||||
base.Cmd("start", testContainerName).AssertOK()
|
||||
assert.NilError(t, check(30))
|
||||
assert.NilError(t, check(5))
|
||||
}
|
||||
|
||||
func TestStopWithStopSignal(t *testing.T) {
|
||||
|
|
|
@ -163,7 +163,7 @@ RUN uname -m > /usr/share/nginx/html/index.html
|
|||
}
|
||||
|
||||
for testURL, expectedIndexHTML := range testCases {
|
||||
resp, err := nettestutil.HTTPGet(testURL, 50, false)
|
||||
resp, err := nettestutil.HTTPGet(testURL, 5, false)
|
||||
assert.NilError(t, err)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
assert.NilError(t, err)
|
||||
|
|
|
@ -283,3 +283,10 @@ func AddPersistentBoolFlag(cmd *cobra.Command, name string, aliases, nonPersiste
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HiddenPersistentStringArrayFlag creates a persistent string slice flag and hides it.
|
||||
// Used mainly to pass global config values to individual commands.
|
||||
func HiddenPersistentStringArrayFlag(cmd *cobra.Command, name string, value []string, usage string) {
|
||||
cmd.PersistentFlags().StringSlice(name, value, usage)
|
||||
cmd.PersistentFlags().MarkHidden(name)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg"
|
||||
"github.com/containerd/nerdctl/v2/pkg/api/types"
|
||||
)
|
||||
|
||||
|
@ -46,6 +47,39 @@ func VerifyOptions(cmd *cobra.Command) (opt types.ImageVerifyOptions, err error)
|
|||
return
|
||||
}
|
||||
|
||||
func ValidateHealthcheckFlags(options types.ContainerCreateOptions) error {
|
||||
healthFlagsSet :=
|
||||
options.HealthInterval != 0 ||
|
||||
options.HealthTimeout != 0 ||
|
||||
options.HealthRetries != 0 ||
|
||||
options.HealthStartPeriod != 0 ||
|
||||
options.HealthStartInterval != 0
|
||||
|
||||
if options.NoHealthcheck {
|
||||
if options.HealthCmd != "" || healthFlagsSet {
|
||||
return fmt.Errorf("--no-healthcheck conflicts with --health-* options")
|
||||
}
|
||||
}
|
||||
|
||||
// Note: HealthCmd can be empty with other healthcheck flags set cause healthCmd could be coming from image.
|
||||
if options.HealthInterval < 0 {
|
||||
return fmt.Errorf("--health-interval cannot be negative")
|
||||
}
|
||||
if options.HealthTimeout < 0 {
|
||||
return fmt.Errorf("--health-timeout cannot be negative")
|
||||
}
|
||||
if options.HealthRetries < 0 {
|
||||
return fmt.Errorf("--health-retries cannot be negative")
|
||||
}
|
||||
if options.HealthStartPeriod < 0 {
|
||||
return fmt.Errorf("--health-start-period cannot be negative")
|
||||
}
|
||||
if options.HealthStartInterval < 0 {
|
||||
return fmt.Errorf("--health-start-interval cannot be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error) {
|
||||
debug, err := cmd.Flags().GetBool("debug")
|
||||
if err != nil {
|
||||
|
@ -111,6 +145,24 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error)
|
|||
if err != nil {
|
||||
return types.GlobalCommandOptions{}, err
|
||||
}
|
||||
dns, err := cmd.Flags().GetStringSlice("global-dns")
|
||||
if err != nil {
|
||||
return types.GlobalCommandOptions{}, err
|
||||
}
|
||||
dnsOpts, err := cmd.Flags().GetStringSlice("global-dns-opts")
|
||||
if err != nil {
|
||||
return types.GlobalCommandOptions{}, err
|
||||
}
|
||||
dnsSearch, err := cmd.Flags().GetStringSlice("global-dns-search")
|
||||
if err != nil {
|
||||
return types.GlobalCommandOptions{}, err
|
||||
}
|
||||
|
||||
// Point to dataRoot for filesystem-helpers implementing rollback / backups.
|
||||
err = pkg.InitFS(dataRoot)
|
||||
if err != nil {
|
||||
return types.GlobalCommandOptions{}, err
|
||||
}
|
||||
|
||||
return types.GlobalCommandOptions{
|
||||
Debug: debug,
|
||||
|
@ -129,6 +181,9 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error)
|
|||
BridgeIP: bridgeIP,
|
||||
KubeHideDupe: kubeHideDupe,
|
||||
CDISpecDirs: cdiSpecDirs,
|
||||
DNS: dns,
|
||||
DNSOpts: dnsOpts,
|
||||
DNSSearch: dnsSearch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ func ComposeUp(t *testing.T, base *testutil.Base, dockerComposeYAML string, opts
|
|||
base.Cmd("network", "inspect", fmt.Sprintf("%s_default", projectName)).AssertOK()
|
||||
|
||||
checkWordpress := func() error {
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 10, false)
|
||||
resp, err := nettestutil.HTTPGet("http://127.0.0.1:8080", 5, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -89,6 +89,12 @@ func convertCommand() *cobra.Command {
|
|||
cmd.Flags().String("overlaybd-dbstr", "", "Database config string for overlaybd")
|
||||
// #endregion
|
||||
|
||||
// #region soci flags
|
||||
cmd.Flags().Bool("soci", false, "Convert image to SOCI Index V2 format.")
|
||||
cmd.Flags().Int64("soci-min-layer-size", -1, "The minimum size of layers that will be converted to SOCI Index V2 format")
|
||||
cmd.Flags().Int64("soci-span-size", -1, "The size of SOCI spans")
|
||||
// #endregion
|
||||
|
||||
// #region generic flags
|
||||
cmd.Flags().Bool("uncompress", false, "Convert tar.gz layers to uncompressed tar layers")
|
||||
cmd.Flags().Bool("oci", false, "Convert Docker media types to OCI media types")
|
||||
|
@ -213,6 +219,21 @@ func convertOptions(cmd *cobra.Command) (types.ImageConvertOptions, error) {
|
|||
}
|
||||
// #endregion
|
||||
|
||||
// #region soci flags
|
||||
soci, err := cmd.Flags().GetBool("soci")
|
||||
if err != nil {
|
||||
return types.ImageConvertOptions{}, err
|
||||
}
|
||||
sociMinLayerSize, err := cmd.Flags().GetInt64("soci-min-layer-size")
|
||||
if err != nil {
|
||||
return types.ImageConvertOptions{}, err
|
||||
}
|
||||
sociSpanSize, err := cmd.Flags().GetInt64("soci-span-size")
|
||||
if err != nil {
|
||||
return types.ImageConvertOptions{}, err
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region generic flags
|
||||
uncompress, err := cmd.Flags().GetBool("uncompress")
|
||||
if err != nil {
|
||||
|
@ -237,37 +258,6 @@ func convertOptions(cmd *cobra.Command) (types.ImageConvertOptions, error) {
|
|||
return types.ImageConvertOptions{
|
||||
GOptions: globalOptions,
|
||||
Format: format,
|
||||
// #region estargz flags
|
||||
Estargz: estargz,
|
||||
EstargzRecordIn: estargzRecordIn,
|
||||
EstargzCompressionLevel: estargzCompressionLevel,
|
||||
EstargzChunkSize: estargzChunkSize,
|
||||
EstargzMinChunkSize: estargzMinChunkSize,
|
||||
EstargzExternalToc: estargzExternalTOC,
|
||||
EstargzKeepDiffID: estargzKeepDiffID,
|
||||
// #endregion
|
||||
// #region zstd flags
|
||||
Zstd: zstd,
|
||||
ZstdCompressionLevel: zstdCompressionLevel,
|
||||
// #endregion
|
||||
// #region zstd:chunked flags
|
||||
ZstdChunked: zstdchunked,
|
||||
ZstdChunkedCompressionLevel: zstdChunkedCompressionLevel,
|
||||
ZstdChunkedChunkSize: zstdChunkedChunkSize,
|
||||
ZstdChunkedRecordIn: zstdChunkedRecordIn,
|
||||
// #endregion
|
||||
// #region nydus flags
|
||||
Nydus: nydus,
|
||||
NydusBuilderPath: nydusBuilderPath,
|
||||
NydusWorkDir: nydusWorkDir,
|
||||
NydusPrefetchPatterns: nydusPrefetchPatterns,
|
||||
NydusCompressor: nydusCompressor,
|
||||
// #endregion
|
||||
// #region overlaybd flags
|
||||
Overlaybd: overlaybd,
|
||||
OverlayFsType: overlaybdFsType,
|
||||
OverlaydbDBStr: overlaybdDbstr,
|
||||
// #endregion
|
||||
// #region generic flags
|
||||
Uncompress: uncompress,
|
||||
Oci: oci,
|
||||
|
@ -276,6 +266,45 @@ func convertOptions(cmd *cobra.Command) (types.ImageConvertOptions, error) {
|
|||
Platforms: platforms,
|
||||
AllPlatforms: allPlatforms,
|
||||
// #endregion
|
||||
// Embed image format options
|
||||
EstargzOptions: types.EstargzOptions{
|
||||
Estargz: estargz,
|
||||
EstargzRecordIn: estargzRecordIn,
|
||||
EstargzCompressionLevel: estargzCompressionLevel,
|
||||
EstargzChunkSize: estargzChunkSize,
|
||||
EstargzMinChunkSize: estargzMinChunkSize,
|
||||
EstargzExternalToc: estargzExternalTOC,
|
||||
EstargzKeepDiffID: estargzKeepDiffID,
|
||||
},
|
||||
ZstdOptions: types.ZstdOptions{
|
||||
Zstd: zstd,
|
||||
ZstdCompressionLevel: zstdCompressionLevel,
|
||||
},
|
||||
ZstdChunkedOptions: types.ZstdChunkedOptions{
|
||||
ZstdChunked: zstdchunked,
|
||||
ZstdChunkedCompressionLevel: zstdChunkedCompressionLevel,
|
||||
ZstdChunkedChunkSize: zstdChunkedChunkSize,
|
||||
ZstdChunkedRecordIn: zstdChunkedRecordIn,
|
||||
},
|
||||
NydusOptions: types.NydusOptions{
|
||||
Nydus: nydus,
|
||||
NydusBuilderPath: nydusBuilderPath,
|
||||
NydusWorkDir: nydusWorkDir,
|
||||
NydusPrefetchPatterns: nydusPrefetchPatterns,
|
||||
NydusCompressor: nydusCompressor,
|
||||
},
|
||||
OverlaybdOptions: types.OverlaybdOptions{
|
||||
Overlaybd: overlaybd,
|
||||
OverlayFsType: overlaybdFsType,
|
||||
OverlaydbDBStr: overlaybdDbstr,
|
||||
},
|
||||
SociConvertOptions: types.SociConvertOptions{
|
||||
Soci: soci,
|
||||
SociOptions: types.SociOptions{
|
||||
SpanSize: sociSpanSize,
|
||||
MinLayerSize: sociMinLayerSize,
|
||||
},
|
||||
},
|
||||
Stdout: cmd.OutOrStdout(),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@ package image
|
|||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/testregistry"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest/registry"
|
||||
)
|
||||
|
||||
func TestImageConvert(t *testing.T) {
|
||||
|
@ -88,6 +89,24 @@ func TestImageConvert(t *testing.T) {
|
|||
},
|
||||
Expected: test.Expects(0, nil, nil),
|
||||
},
|
||||
{
|
||||
Description: "soci",
|
||||
Require: require.All(
|
||||
require.Not(nerdtest.Docker),
|
||||
nerdtest.Soci,
|
||||
nerdtest.SociVersion("0.10.0"),
|
||||
),
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rmi", "-f", data.Identifier("converted-image"))
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Command("image", "convert", "--soci",
|
||||
"--soci-span-size", "2097152",
|
||||
"--soci-min-layer-size", "0",
|
||||
testutil.CommonImage, data.Identifier("converted-image"))
|
||||
},
|
||||
Expected: test.Expects(0, nil, nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -100,7 +119,11 @@ func TestImageConvertNydusVerify(t *testing.T) {
|
|||
|
||||
const remoteImageKey = "remoteImageKey"
|
||||
|
||||
var registry *testregistry.RegistryServer
|
||||
var reg *registry.Server
|
||||
|
||||
// It is unclear what is problematic here, but we use the kernel version to discriminate against EL
|
||||
// See: https://github.com/containerd/nerdctl/issues/4332
|
||||
testutil.RequireKernelVersion(t, ">= 6.0.0-0")
|
||||
|
||||
testCase := &test.Case{
|
||||
Require: require.All(
|
||||
|
@ -110,26 +133,30 @@ func TestImageConvertNydusVerify(t *testing.T) {
|
|||
require.Binary("nydusd"),
|
||||
require.Not(nerdtest.Docker),
|
||||
nerdtest.Rootful,
|
||||
nerdtest.Registry,
|
||||
),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
base := testutil.NewBase(t)
|
||||
registry = testregistry.NewWithNoAuth(base, 0, false)
|
||||
data.Labels().Set(remoteImageKey, fmt.Sprintf("%s:%d/nydusd-image:test", "localhost", registry.Port))
|
||||
reg = nerdtest.RegistryWithNoAuth(data, helpers, 0, false)
|
||||
reg.Setup(data, helpers)
|
||||
|
||||
data.Labels().Set(remoteImageKey, fmt.Sprintf("%s:%d/nydusd-image:test", "localhost", reg.Port))
|
||||
helpers.Ensure("image", "convert", "--nydus", "--oci", testutil.CommonImage, data.Identifier("converted-image"))
|
||||
helpers.Ensure("tag", data.Identifier("converted-image"), data.Labels().Get(remoteImageKey))
|
||||
helpers.Ensure("push", data.Labels().Get(remoteImageKey))
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Anyhow("rmi", "-f", data.Identifier("converted-image"))
|
||||
if registry != nil {
|
||||
registry.Cleanup(nil)
|
||||
if reg != nil {
|
||||
reg.Cleanup(data, helpers)
|
||||
helpers.Anyhow("rmi", "-f", data.Labels().Get(remoteImageKey))
|
||||
}
|
||||
},
|
||||
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
|
||||
return helpers.Custom("nydusify",
|
||||
cmd := helpers.Custom("nydusify",
|
||||
"check",
|
||||
"--work-dir",
|
||||
data.Temp().Dir("nydusify-temp"),
|
||||
"--source",
|
||||
testutil.CommonImage,
|
||||
"--target",
|
||||
|
@ -137,6 +164,8 @@ func TestImageConvertNydusVerify(t *testing.T) {
|
|||
"--source-insecure",
|
||||
"--target-insecure",
|
||||
)
|
||||
cmd.WithTimeout(30 * time.Second)
|
||||
return cmd
|
||||
},
|
||||
Expected: test.Expects(0, nil, nil),
|
||||
}
|
||||
|
|
|
@ -28,13 +28,13 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/testregistry"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest/registry"
|
||||
)
|
||||
|
||||
func TestImageEncryptJWE(t *testing.T) {
|
||||
nerdtest.Setup()
|
||||
|
||||
var registry *testregistry.RegistryServer
|
||||
var reg *registry.Server
|
||||
|
||||
const remoteImageKey = "remoteImageKey"
|
||||
|
||||
|
@ -44,12 +44,14 @@ func TestImageEncryptJWE(t *testing.T) {
|
|||
require.Not(nerdtest.Docker),
|
||||
// This test needs to rmi the common image
|
||||
nerdtest.Private,
|
||||
nerdtest.Registry,
|
||||
),
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
if registry != nil {
|
||||
registry.Cleanup(nil)
|
||||
if reg != nil {
|
||||
reg.Cleanup(data, helpers)
|
||||
helpers.Anyhow("rmi", "-f", data.Labels().Get(remoteImageKey))
|
||||
}
|
||||
|
||||
helpers.Anyhow("rmi", "-f", data.Identifier("decrypted"))
|
||||
},
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
|
@ -57,10 +59,11 @@ func TestImageEncryptJWE(t *testing.T) {
|
|||
data.Labels().Set("private", pri)
|
||||
data.Labels().Set("public", pub)
|
||||
|
||||
base := testutil.NewBase(t)
|
||||
registry = testregistry.NewWithNoAuth(base, 0, false)
|
||||
reg = nerdtest.RegistryWithNoAuth(data, helpers, 0, false)
|
||||
reg.Setup(data, helpers)
|
||||
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
encryptImageRef := fmt.Sprintf("127.0.0.1:%d/%s:encrypted", registry.Port, data.Identifier())
|
||||
encryptImageRef := fmt.Sprintf("127.0.0.1:%d/%s:encrypted", reg.Port, data.Identifier())
|
||||
helpers.Ensure("image", "encrypt", "--recipient=jwe:"+pub, testutil.CommonImage, encryptImageRef)
|
||||
inspector := helpers.Capture("image", "inspect", "--mode=native", "--format={{len .Index.Manifests}}", encryptImageRef)
|
||||
assert.Equal(t, inspector, "1\n")
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/formatter"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -43,6 +44,43 @@ type historyObj struct {
|
|||
Comment string
|
||||
}
|
||||
|
||||
const createdAt1 = "2021-03-31T10:21:21-07:00"
|
||||
const createdAt2 = "2021-03-31T10:21:23-07:00"
|
||||
|
||||
// Expected content of the common image on arm64
|
||||
var (
|
||||
createdAtTime, _ = time.Parse(time.RFC3339, createdAt2)
|
||||
expectedHistory = []historyObj{
|
||||
{
|
||||
CreatedBy: "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
|
||||
Size: "0B",
|
||||
CreatedAt: createdAt2,
|
||||
Snapshot: "<missing>",
|
||||
Comment: "",
|
||||
CreatedSince: formatter.TimeSinceInHuman(createdAtTime),
|
||||
},
|
||||
{
|
||||
CreatedBy: "/bin/sh -c #(nop) ADD file:3b16ffee2b26d8af5…",
|
||||
Size: "5.947MB",
|
||||
CreatedAt: createdAt1,
|
||||
Snapshot: "sha256:56bf55b8eed1f0b4794a30386e4d1d3da949c…",
|
||||
Comment: "",
|
||||
CreatedSince: formatter.TimeSinceInHuman(createdAtTime),
|
||||
},
|
||||
}
|
||||
expectedHistoryNoTrunc = []historyObj{
|
||||
{
|
||||
Snapshot: "<missing>",
|
||||
Size: "0",
|
||||
},
|
||||
{
|
||||
Snapshot: "sha256:56bf55b8eed1f0b4794a30386e4d1d3da949c25bcb5155e898097cd75dc77c2a",
|
||||
CreatedBy: "/bin/sh -c #(nop) ADD file:3b16ffee2b26d8af5db152fcc582aaccd9e1ec9e3343874e9969a205550fe07d in / ",
|
||||
Size: "5947392",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func decode(stdout string) ([]historyObj, error) {
|
||||
dec := json.NewDecoder(strings.NewReader(stdout))
|
||||
object := []historyObj{}
|
||||
|
@ -90,65 +128,65 @@ func TestImageHistory(t *testing.T) {
|
|||
{
|
||||
Description: "trunc, no quiet, human",
|
||||
Command: test.Command("image", "history", "--human=true", "--format=json", testutil.CommonImage),
|
||||
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
|
||||
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
|
||||
history, err := decode(stdout)
|
||||
assert.NilError(t, err, info)
|
||||
assert.Equal(t, len(history), 2, info)
|
||||
assert.NilError(t, err, "decode should not fail")
|
||||
assert.Equal(t, len(history), 2, "history should be 2 in length")
|
||||
|
||||
localTimeL1, _ := time.Parse(time.RFC3339, "2021-03-31T10:21:23-07:00")
|
||||
localTimeL2, _ := time.Parse(time.RFC3339, "2021-03-31T10:21:21-07:00")
|
||||
compTime1, _ := time.Parse(time.RFC3339, history[0].CreatedAt)
|
||||
compTime2, _ := time.Parse(time.RFC3339, history[1].CreatedAt)
|
||||
assert.Equal(t, compTime1.UTC().String(), localTimeL1.UTC().String(), info)
|
||||
assert.Equal(t, history[0].CreatedBy, "/bin/sh -c #(nop) CMD [\"/bin/sh\"]", info)
|
||||
assert.Equal(t, compTime2.UTC().String(), localTimeL2.UTC().String(), info)
|
||||
assert.Equal(t, history[1].CreatedBy, "/bin/sh -c #(nop) ADD file:3b16ffee2b26d8af5…", info)
|
||||
h0Time, _ := time.Parse(time.RFC3339, history[0].CreatedAt)
|
||||
h1Time, _ := time.Parse(time.RFC3339, history[1].CreatedAt)
|
||||
comp0Time, _ := time.Parse(time.RFC3339, expectedHistory[0].CreatedAt)
|
||||
comp1Time, _ := time.Parse(time.RFC3339, expectedHistory[1].CreatedAt)
|
||||
|
||||
assert.Equal(t, history[0].Size, "0B", info)
|
||||
assert.Equal(t, history[0].CreatedSince, formatter.TimeSinceInHuman(compTime1), info)
|
||||
assert.Equal(t, history[0].Snapshot, "<missing>", info)
|
||||
assert.Equal(t, history[0].Comment, "", info)
|
||||
assert.Equal(t, h0Time.UTC().String(), comp0Time.UTC().String())
|
||||
assert.Equal(t, history[0].CreatedBy, expectedHistory[0].CreatedBy)
|
||||
assert.Equal(t, history[0].Size, expectedHistory[0].Size)
|
||||
assert.Equal(t, history[0].CreatedSince, expectedHistory[0].CreatedSince)
|
||||
assert.Equal(t, history[0].Snapshot, expectedHistory[0].Snapshot)
|
||||
assert.Equal(t, history[0].Comment, expectedHistory[0].Comment)
|
||||
|
||||
assert.Equal(t, history[1].Size, "5.947MB", info)
|
||||
assert.Equal(t, history[1].CreatedSince, formatter.TimeSinceInHuman(compTime2), info)
|
||||
assert.Equal(t, history[1].Snapshot, "sha256:56bf55b8eed1f0b4794a30386e4d1d3da949c…", info)
|
||||
assert.Equal(t, history[1].Comment, "", info)
|
||||
assert.Equal(t, h1Time.UTC().String(), comp1Time.UTC().String())
|
||||
assert.Equal(t, history[1].CreatedBy, expectedHistory[1].CreatedBy)
|
||||
assert.Equal(t, history[1].Size, expectedHistory[1].Size)
|
||||
assert.Equal(t, history[1].CreatedSince, expectedHistory[1].CreatedSince)
|
||||
assert.Equal(t, history[1].Snapshot, expectedHistory[1].Snapshot)
|
||||
assert.Equal(t, history[1].Comment, expectedHistory[1].Comment)
|
||||
}),
|
||||
},
|
||||
{
|
||||
Description: "no human - dates and sizes and not prettyfied",
|
||||
Description: "no human - dates and sizes are not prettyfied",
|
||||
Command: test.Command("image", "history", "--human=false", "--format=json", testutil.CommonImage),
|
||||
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
|
||||
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
|
||||
history, err := decode(stdout)
|
||||
assert.NilError(t, err, info)
|
||||
assert.Equal(t, history[0].Size, "0", info)
|
||||
assert.Equal(t, history[0].CreatedSince, history[0].CreatedAt, info)
|
||||
assert.Equal(t, history[1].Size, "5947392", info)
|
||||
assert.Equal(t, history[1].CreatedSince, history[1].CreatedAt, info)
|
||||
assert.NilError(t, err, "decode should not fail")
|
||||
assert.Equal(t, history[0].Size, expectedHistoryNoTrunc[0].Size)
|
||||
assert.Equal(t, history[0].CreatedSince, history[0].CreatedAt)
|
||||
assert.Equal(t, history[1].Size, expectedHistoryNoTrunc[1].Size)
|
||||
assert.Equal(t, history[1].CreatedSince, history[1].CreatedAt)
|
||||
}),
|
||||
},
|
||||
{
|
||||
Description: "no trunc - do not truncate sha or cmd",
|
||||
Command: test.Command("image", "history", "--human=false", "--no-trunc", "--format=json", testutil.CommonImage),
|
||||
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
|
||||
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
|
||||
history, err := decode(stdout)
|
||||
assert.NilError(t, err, info)
|
||||
assert.Equal(t, history[1].Snapshot, "sha256:56bf55b8eed1f0b4794a30386e4d1d3da949c25bcb5155e898097cd75dc77c2a")
|
||||
assert.Equal(t, history[1].CreatedBy, "/bin/sh -c #(nop) ADD file:3b16ffee2b26d8af5db152fcc582aaccd9e1ec9e3343874e9969a205550fe07d in / ")
|
||||
assert.NilError(t, err, "decode should not fail")
|
||||
assert.Equal(t, history[1].Snapshot, expectedHistoryNoTrunc[1].Snapshot)
|
||||
assert.Equal(t, history[1].CreatedBy, expectedHistoryNoTrunc[1].CreatedBy)
|
||||
}),
|
||||
},
|
||||
{
|
||||
Description: "Quiet has no effect with format, so, go no-json, no-trunc",
|
||||
Command: test.Command("image", "history", "--human=false", "--no-trunc", "--quiet", testutil.CommonImage),
|
||||
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
|
||||
assert.Equal(t, stdout, "<missing>\nsha256:56bf55b8eed1f0b4794a30386e4d1d3da949c25bcb5155e898097cd75dc77c2a\n")
|
||||
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
|
||||
assert.Equal(t, stdout, expectedHistoryNoTrunc[0].Snapshot+"\n"+expectedHistoryNoTrunc[1].Snapshot+"\n")
|
||||
}),
|
||||
},
|
||||
{
|
||||
Description: "With quiet, trunc has no effect",
|
||||
Command: test.Command("image", "history", "--human=false", "--no-trunc", "--quiet", testutil.CommonImage),
|
||||
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
|
||||
assert.Equal(t, stdout, "<missing>\nsha256:56bf55b8eed1f0b4794a30386e4d1d3da949c25bcb5155e898097cd75dc77c2a\n")
|
||||
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
|
||||
assert.Equal(t, stdout, expectedHistoryNoTrunc[0].Snapshot+"\n"+expectedHistoryNoTrunc[1].Snapshot+"\n")
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -45,14 +46,14 @@ func TestImageInspectSimpleCases(t *testing.T) {
|
|||
{
|
||||
Description: "Contains some stuff",
|
||||
Command: test.Command("image", "inspect", testutil.CommonImage),
|
||||
Expected: test.Expects(0, nil, func(stdout string, info string, t *testing.T) {
|
||||
Expected: test.Expects(0, nil, func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Image
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
|
||||
assert.Assert(t, len(dc[0].RootFS.Layers) > 0, info)
|
||||
assert.Assert(t, dc[0].Architecture != "", info)
|
||||
assert.Assert(t, dc[0].Size > 0, info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
|
||||
assert.Assert(t, len(dc[0].RootFS.Layers) > 0, "there should be at least one rootfs layer\n")
|
||||
assert.Assert(t, dc[0].Architecture != "", "architecture should be set\n")
|
||||
assert.Assert(t, dc[0].Size > 0, "size should be > 0 \n")
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
@ -115,11 +116,11 @@ func TestImageInspectDifferentValidReferencesForTheSameImage(t *testing.T) {
|
|||
Command: test.Command("image", "inspect", "busybox"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Image
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
|
||||
reference := dc[0].ID
|
||||
sha := strings.TrimPrefix(dc[0].RepoDigests[0], "busybox@sha256:")
|
||||
|
||||
|
@ -140,11 +141,11 @@ func TestImageInspectDifferentValidReferencesForTheSameImage(t *testing.T) {
|
|||
Command: test.Command("image", "inspect", "busybox"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Image
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
|
||||
reference := dc[0].ID
|
||||
sha := strings.TrimPrefix(dc[0].RepoDigests[0], "busybox@sha256:")
|
||||
|
||||
|
@ -173,11 +174,11 @@ func TestImageInspectDifferentValidReferencesForTheSameImage(t *testing.T) {
|
|||
Command: test.Command("image", "inspect", "busybox"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Image
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
|
||||
sha := strings.TrimPrefix(dc[0].RepoDigests[0], "busybox@sha256:")
|
||||
|
||||
for _, id := range []string{"doesnotexist", "doesnotexist:either", "busybox:bogustag"} {
|
||||
|
@ -196,11 +197,11 @@ func TestImageInspectDifferentValidReferencesForTheSameImage(t *testing.T) {
|
|||
Command: test.Command("image", "inspect", "busybox"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Image
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
|
||||
|
||||
for _, id := range []string{"∞∞∞∞∞∞∞∞∞∞", "busybox:∞∞∞∞∞∞∞∞∞∞"} {
|
||||
cmd := helpers.Command("image", "inspect", id)
|
||||
|
@ -218,11 +219,11 @@ func TestImageInspectDifferentValidReferencesForTheSameImage(t *testing.T) {
|
|||
Command: test.Command("image", "inspect", "busybox", "busybox"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var dc []dockercompat.Image
|
||||
err := json.Unmarshal([]byte(stdout), &dc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, 2, len(dc), "Unexpectedly did not get 2 results\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, 2, len(dc), "Unexpectedly did not get 2 results\n")
|
||||
reference := nerdtest.InspectImage(helpers, "busybox")
|
||||
assert.Equal(t, dc[0].ID, reference.ID)
|
||||
assert.Equal(t, dc[1].ID, reference.ID)
|
||||
|
|
|
@ -19,8 +19,7 @@ package image
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
@ -31,7 +30,9 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/tabutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -40,10 +41,12 @@ import (
|
|||
func TestImages(t *testing.T) {
|
||||
nerdtest.Setup()
|
||||
|
||||
commonImage, _ := referenceutil.Parse(testutil.CommonImage)
|
||||
|
||||
testCase := &test.Case{
|
||||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
helpers.Ensure("pull", "--quiet", commonImage.String())
|
||||
helpers.Ensure("pull", "--quiet", testutil.NginxAlpineImage)
|
||||
},
|
||||
SubTests: []*test.Case{
|
||||
|
@ -52,53 +55,53 @@ func TestImages(t *testing.T) {
|
|||
Command: test.Command("images"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) >= 2, info)
|
||||
assert.Assert(t, len(lines) >= 2, "there should be at least two lines\n")
|
||||
header := "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tPLATFORM\tSIZE\tBLOB SIZE"
|
||||
if nerdtest.IsDocker() {
|
||||
header = "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tSIZE"
|
||||
}
|
||||
tab := tabutil.NewReader(header)
|
||||
err := tab.ParseHeader(lines[0])
|
||||
assert.NilError(t, err, info)
|
||||
assert.NilError(t, err, "ParseHeader should not fail\n")
|
||||
found := false
|
||||
for _, line := range lines[1:] {
|
||||
repo, _ := tab.ReadRow(line, "REPOSITORY")
|
||||
tag, _ := tab.ReadRow(line, "TAG")
|
||||
if repo+":"+tag == testutil.CommonImage {
|
||||
if repo+":"+tag == commonImage.FamiliarName()+":"+commonImage.Tag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, found, info)
|
||||
assert.Assert(t, found, "we should have found an image\n")
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "With names",
|
||||
Command: test.Command("images", "--names", testutil.CommonImage),
|
||||
Command: test.Command("images", "--names", commonImage.String()),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
expect.Contains(testutil.CommonImage),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
expect.Contains(commonImage.String()),
|
||||
func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) >= 2, info)
|
||||
assert.Assert(t, len(lines) >= 2, "there should be at least two lines\n")
|
||||
tab := tabutil.NewReader("NAME\tIMAGE ID\tCREATED\tPLATFORM\tSIZE\tBLOB SIZE")
|
||||
err := tab.ParseHeader(lines[0])
|
||||
assert.NilError(t, err, info)
|
||||
assert.NilError(t, err, "ParseHeader should not fail\n")
|
||||
found := false
|
||||
for _, line := range lines[1:] {
|
||||
name, _ := tab.ReadRow(line, "NAME")
|
||||
if name == testutil.CommonImage {
|
||||
if name == commonImage.String() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert.Assert(t, found, info)
|
||||
assert.Assert(t, found, "we should have found an image\n")
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -109,12 +112,12 @@ func TestImages(t *testing.T) {
|
|||
Command: test.Command("images", "--format", "'{{json .CreatedAt}}'"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) >= 2, info)
|
||||
assert.Assert(t, len(lines) >= 2, "there should be at least two lines\n")
|
||||
createdTimes := lines
|
||||
slices.Reverse(createdTimes)
|
||||
assert.Assert(t, slices.IsSorted(createdTimes), info)
|
||||
assert.Assert(t, slices.IsSorted(createdTimes), "created times should be sorted\n")
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -135,22 +138,23 @@ func TestImages(t *testing.T) {
|
|||
func TestImagesFilter(t *testing.T) {
|
||||
nerdtest.Setup()
|
||||
|
||||
commonImage, _ := referenceutil.Parse(testutil.CommonImage)
|
||||
|
||||
testCase := &test.Case{
|
||||
Require: nerdtest.Build,
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
helpers.Ensure("tag", testutil.CommonImage, "taggedimage:one-fragment-one")
|
||||
helpers.Ensure("tag", testutil.CommonImage, "taggedimage:two-fragment-two")
|
||||
helpers.Ensure("pull", "--quiet", commonImage.String())
|
||||
helpers.Ensure("tag", commonImage.String(), "taggedimage:one-fragment-one")
|
||||
helpers.Ensure("tag", commonImage.String(), "taggedimage:two-fragment-two")
|
||||
|
||||
dockerfile := fmt.Sprintf(`FROM %s
|
||||
CMD ["echo", "nerdctl-build-test-string"] \n
|
||||
LABEL foo=bar
|
||||
LABEL version=0.1
|
||||
RUN echo "actually creating a layer so that docker sets the createdAt time"
|
||||
`, testutil.CommonImage)
|
||||
`, commonImage.String())
|
||||
buildCtx := data.Temp().Path()
|
||||
err := os.WriteFile(filepath.Join(buildCtx, "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
data.Labels().Set("buildCtx", buildCtx)
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
|
@ -237,47 +241,45 @@ RUN echo "actually creating a layer so that docker sets the createdAt time"
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
expect.Contains(testutil.ImageRepo(testutil.CommonImage)),
|
||||
expect.Contains(commonImage.FamiliarName(), commonImage.Tag),
|
||||
expect.DoesNotContain(data.Labels().Get("builtImageID")),
|
||||
),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "since=" + testutil.CommonImage,
|
||||
Command: test.Command("images", "--filter", fmt.Sprintf("since=%s", testutil.CommonImage)),
|
||||
Description: "since=" + commonImage.String(),
|
||||
Command: test.Command("images", "--filter", fmt.Sprintf("since=%s", commonImage.String())),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
expect.Contains(data.Labels().Get("builtImageID")),
|
||||
expect.DoesNotContain(testutil.ImageRepo(testutil.CommonImage)),
|
||||
expect.DoesNotMatch(regexp.MustCompile(commonImage.FamiliarName()+"[\\s]+"+commonImage.Tag)),
|
||||
),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "since=" + testutil.CommonImage + " " + testutil.CommonImage,
|
||||
Command: test.Command("images", "--filter", fmt.Sprintf("since=%s", testutil.CommonImage), testutil.CommonImage),
|
||||
Description: "since=" + commonImage.String() + " " + commonImage.String(),
|
||||
Command: test.Command("images", "--filter", fmt.Sprintf("since=%s", commonImage.String()), commonImage.String()),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: expect.DoesNotContain(
|
||||
data.Labels().Get("builtImageID"),
|
||||
testutil.ImageRepo(testutil.CommonImage),
|
||||
Output: expect.All(
|
||||
expect.DoesNotContain(data.Labels().Get("builtImageID")),
|
||||
expect.DoesNotMatch(regexp.MustCompile(commonImage.FamiliarName()+"[\\s]+"+commonImage.Tag)),
|
||||
),
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "since=non-exists-image",
|
||||
Require: nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/3511"),
|
||||
Command: test.Command("images", "--filter", "since=non-exists-image"),
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, []error{errors.New("No such image: ")}, nil),
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, []error{errors.New("no such image: ")}, nil),
|
||||
},
|
||||
{
|
||||
Description: "before=non-exists-image",
|
||||
Require: nerdtest.NerdctlNeedsFixing("https://github.com/containerd/nerdctl/issues/3511"),
|
||||
Command: test.Command("images", "--filter", "before=non-exists-image"),
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, []error{errors.New("No such image: ")}, nil),
|
||||
Expected: test.Expects(expect.ExitCodeGenericFail, []error{errors.New("no such image: ")}, nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -298,8 +300,7 @@ func TestImagesFilterDangling(t *testing.T) {
|
|||
CMD ["echo", "nerdctl-build-notag-string"]
|
||||
`, testutil.CommonImage)
|
||||
buildCtx := data.Temp().Path()
|
||||
err := os.WriteFile(filepath.Join(buildCtx, "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
data.Labels().Set("buildCtx", buildCtx)
|
||||
},
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
|
@ -343,7 +344,7 @@ func TestImagesKubeWithKubeHideDupe(t *testing.T) {
|
|||
Command: test.Command("--kube-hide-dupe", "images"),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var imageID string
|
||||
var skipLine int
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
|
@ -353,7 +354,7 @@ func TestImagesKubeWithKubeHideDupe(t *testing.T) {
|
|||
}
|
||||
tab := tabutil.NewReader(header)
|
||||
err := tab.ParseHeader(lines[0])
|
||||
assert.NilError(t, err, info)
|
||||
assert.NilError(t, err, "ParseHeader should not fail\n")
|
||||
found := true
|
||||
for i, line := range lines[1:] {
|
||||
repo, _ := tab.ReadRow(line, "REPOSITORY")
|
||||
|
@ -374,7 +375,7 @@ func TestImagesKubeWithKubeHideDupe(t *testing.T) {
|
|||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, found, info)
|
||||
assert.Assert(t, found, "We should have found the image\n")
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -61,7 +62,7 @@ func TestLoadStdinFromPipe(t *testing.T) {
|
|||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
expect.Contains(fmt.Sprintf("Loaded image: %s:latest", identifier)),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, strings.Contains(helpers.Capture("images"), identifier))
|
||||
},
|
||||
),
|
||||
|
|
|
@ -18,8 +18,6 @@ package image
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -29,6 +27,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -72,8 +71,7 @@ func TestImagePrune(t *testing.T) {
|
|||
`, testutil.CommonImage)
|
||||
|
||||
buildCtx := data.Temp().Path()
|
||||
err := os.WriteFile(filepath.Join(buildCtx, "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
helpers.Ensure("build", buildCtx)
|
||||
// After we rebuild with tag, docker will no longer show the <none> version from above
|
||||
// Swapping order does not change anything.
|
||||
|
@ -87,13 +85,13 @@ func TestImagePrune(t *testing.T) {
|
|||
identifier := data.Identifier()
|
||||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, identifier), info)
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, identifier))
|
||||
},
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, !strings.Contains(imgList, "<none>"), imgList)
|
||||
assert.Assert(t, strings.Contains(imgList, identifier), info)
|
||||
assert.Assert(t, strings.Contains(imgList, identifier))
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -120,8 +118,7 @@ func TestImagePrune(t *testing.T) {
|
|||
`, testutil.CommonImage)
|
||||
|
||||
buildCtx := data.Temp().Path()
|
||||
err := os.WriteFile(filepath.Join(buildCtx, "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
helpers.Ensure("build", buildCtx)
|
||||
helpers.Ensure("build", "-t", identifier, buildCtx)
|
||||
imgList := helpers.Capture("images")
|
||||
|
@ -133,18 +130,18 @@ func TestImagePrune(t *testing.T) {
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, data.Identifier()), info)
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, data.Identifier()))
|
||||
},
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, strings.Contains(imgList, data.Identifier()), info)
|
||||
assert.Assert(t, strings.Contains(imgList, data.Identifier()))
|
||||
assert.Assert(t, !strings.Contains(imgList, "<none>"), imgList)
|
||||
helpers.Ensure("rm", "-f", data.Identifier())
|
||||
removed := helpers.Capture("image", "prune", "--force", "--all")
|
||||
assert.Assert(t, strings.Contains(removed, data.Identifier()), info)
|
||||
assert.Assert(t, strings.Contains(removed, data.Identifier()))
|
||||
imgList = helpers.Capture("images")
|
||||
assert.Assert(t, !strings.Contains(imgList, data.Identifier()), info)
|
||||
assert.Assert(t, !strings.Contains(imgList, data.Identifier()))
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -164,8 +161,7 @@ CMD ["echo", "nerdctl-test-image-prune-filter-label"]
|
|||
LABEL foo=bar
|
||||
LABEL version=0.1`, testutil.CommonImage)
|
||||
buildCtx := data.Temp().Path()
|
||||
err := os.WriteFile(filepath.Join(buildCtx, "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
helpers.Ensure("build", "-t", data.Identifier(), buildCtx)
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, strings.Contains(imgList, data.Identifier()), "Missing "+data.Identifier())
|
||||
|
@ -174,18 +170,18 @@ LABEL version=0.1`, testutil.CommonImage)
|
|||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, data.Identifier()), info)
|
||||
func(stdout string, t tig.T) {
|
||||
assert.Assert(t, !strings.Contains(stdout, data.Identifier()))
|
||||
},
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, strings.Contains(imgList, data.Identifier()), info)
|
||||
assert.Assert(t, strings.Contains(imgList, data.Identifier()))
|
||||
},
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
prune := helpers.Capture("image", "prune", "--force", "--all", "--filter", "label=foo=bar")
|
||||
assert.Assert(t, strings.Contains(prune, data.Identifier()), info)
|
||||
assert.Assert(t, strings.Contains(prune, data.Identifier()))
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, !strings.Contains(imgList, data.Identifier()), info)
|
||||
assert.Assert(t, !strings.Contains(imgList, data.Identifier()))
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -204,8 +200,7 @@ LABEL version=0.1`, testutil.CommonImage)
|
|||
RUN echo "Anything, so that we create actual content for docker to set the current time for CreatedAt"
|
||||
CMD ["echo", "nerdctl-test-image-prune-until"]`, testutil.CommonImage)
|
||||
buildCtx := data.Temp().Path()
|
||||
err := os.WriteFile(filepath.Join(buildCtx, "Dockerfile"), []byte(dockerfile), 0o600)
|
||||
assert.NilError(helpers.T(), err)
|
||||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
helpers.Ensure("build", "-t", data.Identifier(), buildCtx)
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, strings.Contains(imgList, data.Identifier()), "Missing "+data.Identifier())
|
||||
|
@ -216,9 +211,9 @@ CMD ["echo", "nerdctl-test-image-prune-until"]`, testutil.CommonImage)
|
|||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
expect.DoesNotContain(data.Labels().Get("imageID")),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, strings.Contains(imgList, data.Labels().Get("imageID")), info)
|
||||
assert.Assert(t, strings.Contains(imgList, data.Labels().Get("imageID")))
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -235,9 +230,9 @@ CMD ["echo", "nerdctl-test-image-prune-until"]`, testutil.CommonImage)
|
|||
return &test.Expected{
|
||||
Output: expect.All(
|
||||
expect.Contains(data.Labels().Get("imageID")),
|
||||
func(stdout string, info string, t *testing.T) {
|
||||
func(stdout string, t tig.T) {
|
||||
imgList := helpers.Capture("images")
|
||||
assert.Assert(t, !strings.Contains(imgList, data.Labels().Get("imageID")), imgList, info)
|
||||
assert.Assert(t, !strings.Contains(imgList, data.Labels().Get("imageID")), imgList)
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
|
@ -129,8 +130,8 @@ CMD ["echo", "nerdctl-build-test-string"]
|
|||
data.Temp().Save(dockerfile, "Dockerfile")
|
||||
reg = nerdtest.RegistryWithNoAuth(data, helpers, 80, false)
|
||||
reg.Setup(data, helpers)
|
||||
testImageRef := fmt.Sprintf("%s/%s:%s",
|
||||
reg.IP.String(), data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s/%s",
|
||||
reg.IP.String(), data.Identifier())
|
||||
buildCtx := data.Temp().Path()
|
||||
|
||||
helpers.Ensure("build", "-t", testImageRef, buildCtx)
|
||||
|
@ -182,7 +183,7 @@ func TestImagePullSoci(t *testing.T) {
|
|||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
cmd := helpers.Custom("mount")
|
||||
cmd.Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
data.Labels().Set("remoteSnapshotsInitialCount", strconv.Itoa(strings.Count(stdout, "fuse.rawBridge")))
|
||||
},
|
||||
})
|
||||
|
@ -196,7 +197,7 @@ func TestImagePullSoci(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, _ string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
remoteSnapshotsInitialCount, _ := strconv.Atoi(data.Labels().Get("remoteSnapshotsInitialCount"))
|
||||
remoteSnapshotsActualCount := strings.Count(stdout, "fuse.rawBridge")
|
||||
assert.Equal(t,
|
||||
|
@ -218,7 +219,7 @@ func TestImagePullSoci(t *testing.T) {
|
|||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
cmd := helpers.Custom("mount")
|
||||
cmd.Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
data.Labels().Set("remoteSnapshotsInitialCount", strconv.Itoa(strings.Count(stdout, "fuse.rawBridge")))
|
||||
},
|
||||
})
|
||||
|
@ -232,7 +233,7 @@ func TestImagePullSoci(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
remoteSnapshotsInitialCount, _ := strconv.Atoi(data.Labels().Get("remoteSnapshotsInitialCount"))
|
||||
remoteSnapshotsActualCount := strings.Count(stdout, "fuse.rawBridge")
|
||||
assert.Equal(t,
|
||||
|
|
|
@ -20,43 +20,53 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/testregistry"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest/registry"
|
||||
)
|
||||
|
||||
func TestPush(t *testing.T) {
|
||||
nerdtest.Setup()
|
||||
|
||||
var registryNoAuthHTTPRandom, registryNoAuthHTTPDefault, registryTokenAuthHTTPSRandom *testregistry.RegistryServer
|
||||
var registryNoAuthHTTPRandom, registryNoAuthHTTPDefault, registryTokenAuthHTTPSRandom *registry.Server
|
||||
var tokenServer *registry.TokenAuthServer
|
||||
|
||||
testCase := &test.Case{
|
||||
Require: require.Linux,
|
||||
Require: require.All(
|
||||
require.Linux,
|
||||
nerdtest.Registry,
|
||||
),
|
||||
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
base := testutil.NewBase(t)
|
||||
registryNoAuthHTTPRandom = testregistry.NewWithNoAuth(base, 0, false)
|
||||
registryNoAuthHTTPDefault = testregistry.NewWithNoAuth(base, 80, false)
|
||||
registryTokenAuthHTTPSRandom = testregistry.NewWithTokenAuth(base, "admin", "badmin", 0, true)
|
||||
registryNoAuthHTTPRandom = nerdtest.RegistryWithNoAuth(data, helpers, 0, false)
|
||||
registryNoAuthHTTPRandom.Setup(data, helpers)
|
||||
registryNoAuthHTTPDefault = nerdtest.RegistryWithNoAuth(data, helpers, 80, false)
|
||||
registryNoAuthHTTPDefault.Setup(data, helpers)
|
||||
registryTokenAuthHTTPSRandom, tokenServer = nerdtest.RegistryWithTokenAuth(data, helpers, "admin", "badmin", 0, true)
|
||||
tokenServer.Setup(data, helpers)
|
||||
registryTokenAuthHTTPSRandom.Setup(data, helpers)
|
||||
},
|
||||
|
||||
Cleanup: func(data test.Data, helpers test.Helpers) {
|
||||
if registryNoAuthHTTPRandom != nil {
|
||||
registryNoAuthHTTPRandom.Cleanup(nil)
|
||||
registryNoAuthHTTPRandom.Cleanup(data, helpers)
|
||||
}
|
||||
if registryNoAuthHTTPDefault != nil {
|
||||
registryNoAuthHTTPDefault.Cleanup(nil)
|
||||
registryNoAuthHTTPDefault.Cleanup(data, helpers)
|
||||
}
|
||||
if registryTokenAuthHTTPSRandom != nil {
|
||||
registryTokenAuthHTTPSRandom.Cleanup(nil)
|
||||
registryTokenAuthHTTPSRandom.Cleanup(data, helpers)
|
||||
}
|
||||
if tokenServer != nil {
|
||||
tokenServer.Cleanup(data, helpers)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -65,8 +75,8 @@ func TestPush(t *testing.T) {
|
|||
Description: "plain http",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.CommonImage, testImageRef)
|
||||
},
|
||||
|
@ -85,8 +95,8 @@ func TestPush(t *testing.T) {
|
|||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.CommonImage, testImageRef)
|
||||
},
|
||||
|
@ -104,8 +114,8 @@ func TestPush(t *testing.T) {
|
|||
Description: "plain http with localhost",
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
"127.0.0.1", registryNoAuthHTTPRandom.Port, data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
"127.0.0.1", registryNoAuthHTTPRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.CommonImage, testImageRef)
|
||||
},
|
||||
|
@ -119,8 +129,8 @@ func TestPush(t *testing.T) {
|
|||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
testImageRef := fmt.Sprintf("%s/%s:%s",
|
||||
registryNoAuthHTTPDefault.IP.String(), data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s/%s",
|
||||
registryNoAuthHTTPDefault.IP.String(), data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.CommonImage, testImageRef)
|
||||
},
|
||||
|
@ -139,8 +149,8 @@ func TestPush(t *testing.T) {
|
|||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryTokenAuthHTTPSRandom.IP.String(), registryTokenAuthHTTPSRandom.Port, data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryTokenAuthHTTPSRandom.IP.String(), registryTokenAuthHTTPSRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.CommonImage, testImageRef)
|
||||
helpers.Ensure("--insecure-registry", "login", "-u", "admin", "-p", "badmin",
|
||||
|
@ -162,8 +172,8 @@ func TestPush(t *testing.T) {
|
|||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.CommonImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryTokenAuthHTTPSRandom.IP.String(), registryTokenAuthHTTPSRandom.Port, data.Identifier(), strings.Split(testutil.CommonImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryTokenAuthHTTPSRandom.IP.String(), registryTokenAuthHTTPSRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.CommonImage, testImageRef)
|
||||
helpers.Ensure("--hosts-dir", registryTokenAuthHTTPSRandom.HostsDir, "login", "-u", "admin", "-p", "badmin",
|
||||
|
@ -185,8 +195,8 @@ func TestPush(t *testing.T) {
|
|||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.NonDistBlobImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), strings.Split(testutil.NonDistBlobImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.NonDistBlobImage, testImageRef)
|
||||
},
|
||||
|
@ -200,12 +210,12 @@ func TestPush(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
blobURL := fmt.Sprintf("http://%s:%d/v2/%s/blobs/%s", registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), testutil.NonDistBlobDigest)
|
||||
resp, err := http.Get(blobURL)
|
||||
assert.Assert(t, err, "error making http request")
|
||||
if resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
assert.Equal(t, resp.StatusCode, http.StatusNotFound, "non-distributable blob should not be available")
|
||||
},
|
||||
|
@ -217,8 +227,8 @@ func TestPush(t *testing.T) {
|
|||
Require: require.Not(nerdtest.Docker),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.NonDistBlobImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), strings.Split(testutil.NonDistBlobImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.NonDistBlobImage, testImageRef)
|
||||
},
|
||||
|
@ -232,12 +242,12 @@ func TestPush(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
blobURL := fmt.Sprintf("http://%s:%d/v2/%s/blobs/%s", registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), testutil.NonDistBlobDigest)
|
||||
resp, err := http.Get(blobURL)
|
||||
assert.Assert(t, err, "error making http request")
|
||||
if resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
assert.Equal(t, resp.StatusCode, http.StatusOK, "non-distributable blob should be available")
|
||||
},
|
||||
|
@ -252,8 +262,8 @@ func TestPush(t *testing.T) {
|
|||
),
|
||||
Setup: func(data test.Data, helpers test.Helpers) {
|
||||
helpers.Ensure("pull", "--quiet", testutil.UbuntuImage)
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s:%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier(), strings.Split(testutil.UbuntuImage, ":")[1])
|
||||
testImageRef := fmt.Sprintf("%s:%d/%s",
|
||||
registryNoAuthHTTPRandom.IP.String(), registryNoAuthHTTPRandom.Port, data.Identifier())
|
||||
data.Labels().Set("testImageRef", testImageRef)
|
||||
helpers.Ensure("tag", testutil.UbuntuImage, testImageRef)
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/imgutil"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -63,7 +64,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("image is being used")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains(repoName),
|
||||
})
|
||||
|
@ -83,7 +84,7 @@ func TestRemove(t *testing.T) {
|
|||
Command: test.Command("rmi", "-f", testutil.CommonImage),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.DoesNotContain(repoName),
|
||||
})
|
||||
|
@ -108,7 +109,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("image is being used")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains(repoName),
|
||||
})
|
||||
|
@ -140,7 +141,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains("<none>"),
|
||||
})
|
||||
|
@ -162,7 +163,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("image is being used")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains(repoName),
|
||||
})
|
||||
|
@ -184,7 +185,7 @@ func TestRemove(t *testing.T) {
|
|||
Command: test.Command("rmi", "-f", testutil.CommonImage),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
// a created container with removed image doesn't impact other `rmi` command
|
||||
Output: expect.DoesNotContain(repoName, nginxRepoName),
|
||||
|
@ -212,7 +213,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("image is being used")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains(repoName),
|
||||
})
|
||||
|
@ -246,7 +247,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains("<none>"),
|
||||
})
|
||||
|
@ -272,7 +273,7 @@ func TestRemove(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("image is being used")},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.Contains(repoName),
|
||||
})
|
||||
|
@ -293,7 +294,7 @@ func TestRemove(t *testing.T) {
|
|||
Command: test.Command("rmi", "-f", testutil.CommonImage),
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: expect.DoesNotContain(repoName),
|
||||
})
|
||||
|
@ -336,10 +337,10 @@ func TestIssue3016(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("images", data.Labels().Get(tagIDKey)).Run(&test.Expected{
|
||||
ExitCode: 0,
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Equal(t, len(strings.Split(stdout, "\n")), 2)
|
||||
},
|
||||
})
|
||||
|
@ -378,17 +379,17 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("--kube-hide-dupe", "images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numTags+1, info)
|
||||
assert.Assert(t, len(lines) == numTags+1)
|
||||
},
|
||||
})
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numNoTags+1, info)
|
||||
assert.Assert(t, len(lines) == numNoTags+1)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@ -410,17 +411,17 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("--kube-hide-dupe", "images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numTags+1, info)
|
||||
assert.Assert(t, len(lines) == numTags+1)
|
||||
},
|
||||
})
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numNoTags+2, info)
|
||||
assert.Assert(t, len(lines) == numNoTags+2)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@ -440,17 +441,17 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("--kube-hide-dupe", "images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numTags, info)
|
||||
assert.Assert(t, len(lines) == numTags)
|
||||
},
|
||||
})
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numNoTags, info)
|
||||
assert.Assert(t, len(lines) == numNoTags)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@ -469,7 +470,7 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
helpers.Command("--kube-hide-dupe", "rmi", stdout[0:12]).Run(&test.Expected{
|
||||
ExitCode: 1,
|
||||
Errors: []error{errors.New("multiple IDs found with provided prefix: ")},
|
||||
|
@ -478,9 +479,9 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
ExitCode: 0,
|
||||
})
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numNoTags, info)
|
||||
assert.Assert(t, len(lines) == numNoTags)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@ -499,7 +500,7 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
imgID := strings.Split(stdout, "\n")
|
||||
helpers.Command("--kube-hide-dupe", "rmi", imgID[0]).Run(&test.Expected{
|
||||
ExitCode: 1,
|
||||
|
@ -509,9 +510,9 @@ func TestRemoveKubeWithKubeHideDupe(t *testing.T) {
|
|||
ExitCode: 0,
|
||||
})
|
||||
helpers.Command("images").Run(&test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||
assert.Assert(t, len(lines) == numNoTags, info)
|
||||
assert.Assert(t, len(lines) == numNoTags)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
func SaveCommand() *cobra.Command {
|
||||
var cmd = &cobra.Command{
|
||||
Use: "save",
|
||||
Use: "save [flags] IMAGE [IMAGE...]",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Short: "Save one or more images to a tar archive (streamed to STDOUT by default)",
|
||||
Long: "The archive implements both Docker Image Spec v1.2 and OCI Image Spec v1.0.",
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/containerd/nerdctl/mod/tigron/expect"
|
||||
"github.com/containerd/nerdctl/mod/tigron/require"
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
testhelpers "github.com/containerd/nerdctl/v2/cmd/nerdctl/helpers"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -48,7 +49,7 @@ func TestSaveContent(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
rootfsPath := filepath.Join(data.Temp().Path(), "rootfs")
|
||||
err := testhelpers.ExtractDockerArchive(filepath.Join(data.Temp().Path(), "out.tar"), rootfsPath)
|
||||
assert.NilError(t, err)
|
||||
|
@ -188,7 +189,7 @@ func TestSaveMultipleImagesWithSameIDAndLoad(t *testing.T) {
|
|||
return &test.Expected{
|
||||
ExitCode: 0,
|
||||
Errors: []error{},
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
assert.Equal(t, strings.Count(stdout, data.Labels().Get("id")), 2)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/containerd/nerdctl/mod/tigron/test"
|
||||
"github.com/containerd/nerdctl/mod/tigron/tig"
|
||||
|
||||
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
|
||||
"github.com/containerd/nerdctl/v2/pkg/testutil"
|
||||
|
@ -50,23 +51,23 @@ func TestInspectSimpleCase(t *testing.T) {
|
|||
},
|
||||
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
|
||||
return &test.Expected{
|
||||
Output: func(stdout string, info string, t *testing.T) {
|
||||
Output: func(stdout string, t tig.T) {
|
||||
var inspectResult []json.RawMessage
|
||||
err := json.Unmarshal([]byte(stdout), &inspectResult)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.Equal(t, len(inspectResult), 2, "Unexpectedly got multiple results\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
assert.Equal(t, len(inspectResult), 2, "Unexpectedly got multiple results\n")
|
||||
|
||||
var dci dockercompat.Image
|
||||
err = json.Unmarshal(inspectResult[0], &dci)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
inspecti := nerdtest.InspectImage(helpers, testutil.CommonImage)
|
||||
assert.Equal(t, dci.ID, inspecti.ID, info)
|
||||
assert.Equal(t, dci.ID, inspecti.ID, "id should match\n")
|
||||
|
||||
var dcc dockercompat.Container
|
||||
err = json.Unmarshal(inspectResult[1], &dcc)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n"+info)
|
||||
assert.NilError(t, err, "Unable to unmarshal output\n")
|
||||
inspectc := nerdtest.InspectContainer(helpers, data.Identifier())
|
||||
assert.Assert(t, dcc.ID == inspectc.ID, info)
|
||||
assert.Equal(t, dcc.ID, inspectc.ID, "id should match\n")
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue