You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/10/22 15:28:34 UTC
(skywalking-infra-e2e) 01/01: Load kind images per platform with Docker's containerd image store
This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch fix/kind-load-containerd-image-store
in repository https://gitbox.apache.org/repos/asf/skywalking-infra-e2e.git
commit 46bb54dc7d8536d88afcbb06df666f626e3a9e3e
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Thu Oct 22 23:22:42 2026 +0800
Load kind images per platform with Docker's containerd image store
Docker 29 enables the containerd image store by default (ubuntu-26.04
runners). A pulled multi-platform image there only holds the content of
the host platform, but `docker save` still exports an index referencing
every platform, so `kind load docker-image` fails in the nodes'
`ctr images import --all-platforms` with "content digest ... not found"
(kubernetes-sigs/kind#3795).
When the daemon reports the containerd image store and the client's
negotiated API is 1.48 or later (Docker 28+), save only the kind nodes'
platform and load it with `kind load image-archive`, the workaround kind
documents. The classic image store keeps `kind load docker-image`.
Failed loads now carry the node command output, with a hint when an
older Docker's containerd store is missing content.
The kind e2e now runs on ubuntu-24.04 (Docker 28, classic store) and
ubuntu-26.04 (Docker 29, containerd store), imports a pinned
multi-platform image and a locally built one, and loads into a worker
node too.
Co-Authored-By: Claude Opus 5.5 <no...@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CsP9pv9o23vwAUH88Qfe22
---
.github/workflows/e2e-test.yaml | 10 +-
CHANGES.md | 1 +
Makefile | 5 +-
go.mod | 4 +-
internal/components/setup/kind.go | 9 +-
internal/components/setup/kind_image.go | 234 +++++++++++++++++++++++
internal/components/setup/kind_image_test.go | 146 ++++++++++++++
test/e2e/kind/collect-on-failure/deployment.yaml | 2 +-
test/e2e/kind/collect-on-failure/e2e.yaml | 2 +-
test/e2e/kind/deployment.yaml | 29 ++-
test/e2e/kind/e2e.yaml | 7 +-
test/e2e/kind/kind-cluster.yaml | 2 +
12 files changed, 434 insertions(+), 17 deletions(-)
diff --git a/.github/workflows/e2e-test.yaml b/.github/workflows/e2e-test.yaml
index c1ffd56..7333655 100644
--- a/.github/workflows/e2e-test.yaml
+++ b/.github/workflows/e2e-test.yaml
@@ -29,8 +29,14 @@ on:
jobs:
build:
- name: E2E Test
- runs-on: ubuntu-latest
+ name: E2E Test (${{ matrix.os }})
+ # ubuntu-24.04 has Docker 28 with the classic image store,
+ # ubuntu-26.04 has Docker 29 with the containerd image store.
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-24.04, ubuntu-26.04]
+ runs-on: ${{ matrix.os }}
steps:
- name: Set up Go
uses: actions/setup-go@v5
diff --git a/CHANGES.md b/CHANGES.md
index dc2d13d..bbb60ec 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,7 @@ Unreleased
* Upgrade Go dependencies affected by known security advisories: `golang.org/x/crypto`, `github.com/containerd/containerd/v2`, `google.golang.org/grpc`, `github.com/moby/go-archive`, `github.com/moby/spdystream`, `github.com/moby/buildkit`, and the OpenTelemetry SDK and OTLP trace exporters.
* Upgrade `testcontainers-go` to v0.44.0 so Docker Compose no longer imports a BuildKit package removed in the patched release.
* Use `github.com/moby/moby/client` for Docker Engine API calls. `github.com/docker/docker` has no patched release for its advisories.
+* Fix `setup.kind.import-images` failing with `ctr: content digest ... not found` when Docker uses the containerd image store (the default from Docker 29, e.g. on `ubuntu-26.04` runners). Images are now saved for the kind nodes' platform and loaded with `kind load image-archive`; Docker with the classic image store keeps `kind load docker-image`.
1.4.0
------------------
diff --git a/Makefile b/Makefile
index 1c46d33..141c4d0 100644
--- a/Makefile
+++ b/Makefile
@@ -120,7 +120,8 @@ e2e-test: $(GOOS)
# Run E2E test with KinD to verify import-images functionality
e2e-test-kind:
$(MAKE) $(GOOS)
- docker pull busybox:latest
+ docker pull busybox:1.37.0
+ printf 'FROM busybox:1.37.0\n' | docker build -t skywalking-infra-e2e/kind-local:test -
./bin/$(GOOS)/$(PROJECT) run -c ./test/e2e/kind/e2e.yaml
.PHONY: e2e-test-kind-collect
@@ -129,7 +130,7 @@ e2e-test-kind:
# It succeeds if the collected files exist at the expected output directory.
e2e-test-kind-collect:
$(MAKE) $(GOOS)
- docker pull busybox:latest
+ docker pull busybox:1.37.0
rm -rf /tmp/e2e-collect-test
./bin/$(GOOS)/$(PROJECT) run -c ./test/e2e/kind/collect-on-failure/e2e.yaml || true
@echo "Verifying collected files..."
diff --git a/go.mod b/go.mod
index ff7b259..070a827 100644
--- a/go.mod
+++ b/go.mod
@@ -3,8 +3,10 @@ module github.com/apache/skywalking-infra-e2e
go 1.26.0
require (
+ github.com/containerd/platforms v1.0.0-rc.4
github.com/google/go-cmp v0.7.0
github.com/moby/moby/client v0.5.0
+ github.com/opencontainers/image-spec v1.1.1
github.com/pterm/pterm v0.12.45
github.com/sirupsen/logrus v1.9.4
github.com/spf13/cobra v1.10.2
@@ -45,7 +47,6 @@ require (
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
- github.com/containerd/platforms v1.0.0-rc.4 // indirect
github.com/containerd/ttrpc v1.2.8 // indirect
github.com/containerd/typeurl/v2 v2.3.0 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
@@ -139,7 +140,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
- github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.4.1 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
diff --git a/internal/components/setup/kind.go b/internal/components/setup/kind.go
index 9a654ba..664104d 100644
--- a/internal/components/setup/kind.go
+++ b/internal/components/setup/kind.go
@@ -211,13 +211,8 @@ func KindSetup(e2eConfig *config.E2EConfig) error {
if err != nil {
return err
}
- for _, image := range images {
- args := []string{"load", "docker-image", image, "--name", clusterName}
-
- logger.Log.Infof("import docker images: %s", image)
- if err := kind.Run(kindcmd.NewLogger(), kindcmd.StandardIOStreams(), args); err != nil {
- return err
- }
+ if err := importImages(context.Background(), images, clusterName); err != nil {
+ return err
}
}
diff --git a/internal/components/setup/kind_image.go b/internal/components/setup/kind_image.go
new file mode 100644
index 0000000..4e5d367
--- /dev/null
+++ b/internal/components/setup/kind_image.go
@@ -0,0 +1,234 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 setup
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+
+ "github.com/containerd/platforms"
+ docker "github.com/moby/moby/client"
+ "github.com/moby/moby/client/pkg/versions"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ kind "sigs.k8s.io/kind/cmd/kind/app"
+ kindcmd "sigs.k8s.io/kind/pkg/cmd"
+ kindexec "sigs.k8s.io/kind/pkg/exec"
+
+ "github.com/apache/skywalking-infra-e2e/internal/logger"
+ "github.com/apache/skywalking-infra-e2e/internal/util"
+)
+
+// imageLoadMode is how images on the docker host are loaded into the kind nodes.
+type imageLoadMode int
+
+const (
+ // loadDockerImage runs `kind load docker-image`: kind saves the whole image
+ // and the nodes import it with `ctr images import --all-platforms`.
+ loadDockerImage imageLoadMode = iota
+ // loadPlatformArchive saves only the platform of the kind nodes, then runs
+ // `kind load image-archive`. With docker's containerd image store, a pulled
+ // multi-platform image only holds the content of the platform it was pulled
+ // for, so importing all of its platforms fails with
+ // "content digest ... not found" (kubernetes-sigs/kind#3795).
+ loadPlatformArchive
+)
+
+const (
+ // containerdSnapshotterDriverType is the "driver-type" docker reports in
+ // its driver status when the containerd image store is in use.
+ containerdSnapshotterDriverType = "io.containerd.snapshotter.v1"
+ // minPlatformSaveAPIVersion is the first docker API version (docker 28.0)
+ // that accepts a platform when saving an image.
+ minPlatformSaveAPIVersion = "1.48"
+)
+
+// usesContainerdImageStore reports whether the docker driver status says the
+// containerd image store is in use.
+func usesContainerdImageStore(driverStatus [][2]string) bool {
+ for _, status := range driverStatus {
+ if status[0] == "driver-type" && status[1] == containerdSnapshotterDriverType {
+ return true
+ }
+ }
+ return false
+}
+
+// chooseImageLoadMode picks the load mode from the image store and the API
+// version the docker client uses. The classic image store keeps the
+// `kind load docker-image` path unchanged.
+func chooseImageLoadMode(containerdStore bool, apiVersion string) imageLoadMode {
+ if containerdStore && !versions.LessThan(apiVersion, minPlatformSaveAPIVersion) {
+ return loadPlatformArchive
+ }
+ return loadDockerImage
+}
+
+// nodePlatform is the platform of the kind nodes, which run on the docker
+// host's architecture.
+func nodePlatform(arch string) ocispec.Platform {
+ return platforms.Normalize(ocispec.Platform{OS: "linux", Architecture: arch})
+}
+
+// importImages loads the images on the docker host into the kind cluster.
+func importImages(ctx context.Context, images []string, clusterName string) error {
+ cli, err := docker.New(docker.FromEnv)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err := cli.Close(); err != nil {
+ logger.Log.Warnf("failed to close docker client: %v", err)
+ }
+ }()
+
+ // negotiate first, so ClientVersion is the API version requests really use
+ if _, err := cli.Ping(ctx, docker.PingOptions{NegotiateAPIVersion: true}); err != nil {
+ return fmt.Errorf("ping docker daemon error: %w", err)
+ }
+ info, err := cli.Info(ctx, docker.InfoOptions{})
+ if err != nil {
+ return fmt.Errorf("get docker info error: %w", err)
+ }
+ version, err := cli.ServerVersion(ctx, docker.ServerVersionOptions{})
+ if err != nil {
+ return fmt.Errorf("get docker version error: %w", err)
+ }
+
+ containerdStore := usesContainerdImageStore(info.Info.DriverStatus)
+ apiVersion := cli.ClientVersion()
+ mode := chooseImageLoadMode(containerdStore, apiVersion)
+ platform := nodePlatform(version.Arch)
+ switch {
+ case mode == loadPlatformArchive:
+ logger.Log.Infof("docker %s (API %s) uses the containerd image store, images are saved for %s before loading into kind",
+ version.Version, apiVersion, platforms.Format(platform))
+ case containerdStore:
+ logger.Log.Warnf("docker %s (API %s) uses the containerd image store, but saving a single platform needs API %s, "+
+ "multi-platform images may fail to load into kind", version.Version, apiVersion, minPlatformSaveAPIVersion)
+ }
+
+ for _, image := range images {
+ logger.Log.Infof("import docker images: %s", image)
+ if mode == loadPlatformArchive {
+ err = loadImageArchive(ctx, cli, image, platform, clusterName)
+ } else {
+ err = runKindLoad("load", "docker-image", image, "--name", clusterName)
+ if containerdStore && isMissingContentError(err) {
+ err = fmt.Errorf("%w (docker's containerd image store holds only part of this image; "+
+ "use docker 28 or later, or disable the containerd-snapshotter feature of docker)", err)
+ }
+ }
+ if err != nil {
+ return fmt.Errorf("import docker image %s error: %w", image, err)
+ }
+ }
+ return nil
+}
+
+// loadImageArchive saves the platform of the image to a temporary archive and
+// loads it with `kind load image-archive`.
+func loadImageArchive(ctx context.Context, cli *docker.Client, image string, platform ocispec.Platform, clusterName string) error {
+ archive, err := os.CreateTemp(util.WorkDir, "kind-image-*.tar")
+ if err != nil {
+ return fmt.Errorf("create image archive error: %w", err)
+ }
+ defer func() {
+ if err := os.Remove(archive.Name()); err != nil && !os.IsNotExist(err) {
+ logger.Log.Warnf("failed to remove image archive %s: %v", archive.Name(), err)
+ }
+ }()
+
+ if err := saveImage(ctx, cli, image, platform, archive); err != nil {
+ _ = archive.Close()
+ return err
+ }
+ if err := archive.Close(); err != nil {
+ return fmt.Errorf("write image archive error: %w", err)
+ }
+ return runKindLoad("load", "image-archive", archive.Name(), "--name", clusterName)
+}
+
+func saveImage(ctx context.Context, cli *docker.Client, image string, platform ocispec.Platform, dst io.Writer) error {
+ saved, err := cli.ImageSave(ctx, []string{image}, docker.ImageSaveWithPlatforms(platform))
+ if err != nil {
+ return fmt.Errorf("save image for %s error, the local image may not hold this platform, "+
+ "pull it with `docker pull --platform %s %s`: %w", platforms.Format(platform), platforms.Format(platform), image, err)
+ }
+ defer func() {
+ if err := saved.Close(); err != nil {
+ logger.Log.Warnf("failed to close image save output: %v", err)
+ }
+ }()
+ if _, err := io.Copy(dst, saved); err != nil {
+ return fmt.Errorf("save image for %s error: %w", platforms.Format(platform), err)
+ }
+ return nil
+}
+
+// runKindLoad runs a kind load command. kind leaves the output of the failed
+// node command out of the error, so it is added back.
+func runKindLoad(args ...string) error {
+ err := kind.Run(kindcmd.NewLogger(), kindcmd.StandardIOStreams(), args)
+ if err == nil {
+ return nil
+ }
+ if output := kindCommandOutput(err); output != "" {
+ return &kindLoadError{err: err, output: output}
+ }
+ return err
+}
+
+// kindLoadError is a failed kind load with the output of the node command.
+type kindLoadError struct {
+ err error
+ output string
+}
+
+func (e *kindLoadError) Error() string {
+ return fmt.Sprintf("%v: %s", e.err, e.output)
+}
+
+func (e *kindLoadError) Unwrap() error {
+ return e.err
+}
+
+func kindCommandOutput(err error) string {
+ var runErr *kindexec.RunError
+ if !errors.As(err, &runErr) {
+ // kind's own errors may only chain through Cause()
+ if runErr = kindexec.RunErrorForError(err); runErr == nil {
+ return ""
+ }
+ }
+ return strings.TrimSpace(string(runErr.Output))
+}
+
+// isMissingContentError reports whether the nodes failed to import an image
+// because the archive references content the docker host does not hold.
+func isMissingContentError(err error) bool {
+ var loadErr *kindLoadError
+ if !errors.As(err, &loadErr) {
+ return false
+ }
+ return strings.Contains(loadErr.output, "content digest") && strings.Contains(loadErr.output, "not found")
+}
diff --git a/internal/components/setup/kind_image_test.go b/internal/components/setup/kind_image_test.go
new file mode 100644
index 0000000..b02ab02
--- /dev/null
+++ b/internal/components/setup/kind_image_test.go
@@ -0,0 +1,146 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 setup
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ kinderrors "sigs.k8s.io/kind/pkg/errors"
+ kindexec "sigs.k8s.io/kind/pkg/exec"
+)
+
+func TestUsesContainerdImageStore(t *testing.T) {
+ tests := []struct {
+ name string
+ driverStatus [][2]string
+ want bool
+ }{
+ {name: "containerd image store", driverStatus: [][2]string{{"driver-type", "io.containerd.snapshotter.v1"}}, want: true},
+ {name: "classic overlay2", driverStatus: [][2]string{{"Backing Filesystem", "extfs"}, {"Supports d_type", "true"}}},
+ {name: "no driver status", driverStatus: nil},
+ {name: "other driver type", driverStatus: [][2]string{{"driver-type", "io.containerd.snapshotter.v2"}}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := usesContainerdImageStore(tt.driverStatus); got != tt.want {
+ t.Errorf("usesContainerdImageStore() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestChooseImageLoadMode(t *testing.T) {
+ tests := []struct {
+ name string
+ containerdStore bool
+ apiVersion string
+ want imageLoadMode
+ }{
+ {name: "docker 28 classic store", apiVersion: "1.48", want: loadDockerImage},
+ {name: "docker 29 classic store", apiVersion: "1.54", want: loadDockerImage},
+ {name: "docker 29 containerd store", containerdStore: true, apiVersion: "1.54", want: loadPlatformArchive},
+ {name: "docker 28 containerd store", containerdStore: true, apiVersion: "1.48", want: loadPlatformArchive},
+ {name: "docker 27 containerd store", containerdStore: true, apiVersion: "1.47", want: loadDockerImage},
+ {name: "API pinned below 1.48", containerdStore: true, apiVersion: "1.44", want: loadDockerImage},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := chooseImageLoadMode(tt.containerdStore, tt.apiVersion); got != tt.want {
+ t.Errorf("chooseImageLoadMode() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestNodePlatform(t *testing.T) {
+ tests := []struct {
+ arch string
+ want ocispec.Platform
+ }{
+ {arch: "amd64", want: ocispec.Platform{OS: "linux", Architecture: "amd64"}},
+ {arch: "x86_64", want: ocispec.Platform{OS: "linux", Architecture: "amd64"}},
+ {arch: "arm64", want: ocispec.Platform{OS: "linux", Architecture: "arm64"}},
+ {arch: "aarch64", want: ocispec.Platform{OS: "linux", Architecture: "arm64"}},
+ {arch: "arm", want: ocispec.Platform{OS: "linux", Architecture: "arm", Variant: "v7"}},
+ }
+ for _, tt := range tests {
+ t.Run(tt.arch, func(t *testing.T) {
+ if got := nodePlatform(tt.arch); got.OS != tt.want.OS || got.Architecture != tt.want.Architecture || got.Variant != tt.want.Variant {
+ t.Errorf("nodePlatform(%q) = %+v, want %+v", tt.arch, got, tt.want)
+ }
+ })
+ }
+}
+
+func TestIsMissingContentError(t *testing.T) {
+ runErr := func(output string) error {
+ return fmt.Errorf("failed to load image: %w", &kindexec.RunError{
+ Command: []string{"ctr", "images", "import"},
+ Output: []byte(output),
+ Inner: errors.New("exit status 1"),
+ })
+ }
+ tests := []struct {
+ name string
+ err error
+ want bool
+ }{
+ {
+ name: "missing content",
+ err: &kindLoadError{err: runErr(""), output: "ctr: content digest sha256:bfb54eac: not found"},
+ want: true,
+ },
+ {
+ name: "other ctr failure",
+ err: &kindLoadError{err: runErr(""), output: "ctr: failed to extract layer: no space left on device"},
+ },
+ {name: "wrapped missing content", err: fmt.Errorf("import: %w", &kindLoadError{output: "ctr: content digest sha256:1: not found"}), want: true},
+ {name: "plain error", err: errors.New("content digest sha256:1: not found")},
+ {name: "nil", err: nil},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := isMissingContentError(tt.err); got != tt.want {
+ t.Errorf("isMissingContentError() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestKindCommandOutput(t *testing.T) {
+ err := fmt.Errorf("failed to load image: %w", &kindexec.RunError{
+ Command: []string{"ctr", "images", "import"},
+ Output: []byte("ctr: content digest sha256:1: not found\n"),
+ Inner: errors.New("exit status 1"),
+ })
+ if got, want := kindCommandOutput(err), "ctr: content digest sha256:1: not found"; got != want {
+ t.Errorf("kindCommandOutput() = %q, want %q", got, want)
+ }
+ // kind wraps the failed node command with its own errors package
+ kindErr := kinderrors.Wrap(&kindexec.RunError{Output: []byte("ctr: failed\n"), Inner: errors.New("exit status 1")}, "failed to load image")
+ if got, want := kindCommandOutput(kindErr), "ctr: failed"; got != want {
+ t.Errorf("kindCommandOutput() = %q, want %q", got, want)
+ }
+ if got := kindCommandOutput(errors.New("no command")); got != "" {
+ t.Errorf("kindCommandOutput() = %q, want empty", got)
+ }
+}
diff --git a/test/e2e/kind/collect-on-failure/deployment.yaml b/test/e2e/kind/collect-on-failure/deployment.yaml
index e6ad9fc..1c152eb 100644
--- a/test/e2e/kind/collect-on-failure/deployment.yaml
+++ b/test/e2e/kind/collect-on-failure/deployment.yaml
@@ -22,7 +22,7 @@ metadata:
spec:
containers:
- name: test
- image: busybox:latest
+ image: busybox:1.37.0
imagePullPolicy: Never
command:
- sh
diff --git a/test/e2e/kind/collect-on-failure/e2e.yaml b/test/e2e/kind/collect-on-failure/e2e.yaml
index 072bdcc..c1f6390 100644
--- a/test/e2e/kind/collect-on-failure/e2e.yaml
+++ b/test/e2e/kind/collect-on-failure/e2e.yaml
@@ -23,7 +23,7 @@ setup:
timeout: 10m
kind:
import-images:
- - busybox:latest
+ - busybox:1.37.0
steps:
- name: deploy collect-test pod
command: kubectl apply -f test/e2e/kind/collect-on-failure/deployment.yaml
diff --git a/test/e2e/kind/deployment.yaml b/test/e2e/kind/deployment.yaml
index ed727a6..0e29bf9 100644
--- a/test/e2e/kind/deployment.yaml
+++ b/test/e2e/kind/deployment.yaml
@@ -20,6 +20,33 @@ metadata:
spec:
containers:
- name: test
- image: busybox:latest
+ image: busybox:1.37.0
imagePullPolicy: Never # Force using locally imported image
command: ["sleep", "3600"]
+ - name: local
+ image: skywalking-infra-e2e/kind-local:test
+ imagePullPolicy: Never
+ command: ["sleep", "3600"]
+---
+# The control-plane is tainted, so the pod above runs on the worker;
+# this one checks the images were loaded into the control-plane too.
+apiVersion: v1
+kind: Pod
+metadata:
+ name: import-test-control-plane
+spec:
+ nodeSelector:
+ node-role.kubernetes.io/control-plane: ""
+ tolerations:
+ - key: node-role.kubernetes.io/control-plane
+ operator: Exists
+ effect: NoSchedule
+ containers:
+ - name: test
+ image: busybox:1.37.0
+ imagePullPolicy: Never
+ command: ["sleep", "3600"]
+ - name: local
+ image: skywalking-infra-e2e/kind-local:test
+ imagePullPolicy: Never
+ command: ["sleep", "3600"]
diff --git a/test/e2e/kind/e2e.yaml b/test/e2e/kind/e2e.yaml
index 20dc72e..82d5556 100644
--- a/test/e2e/kind/e2e.yaml
+++ b/test/e2e/kind/e2e.yaml
@@ -21,7 +21,9 @@ setup:
timeout: 10m
kind:
import-images:
- - busybox:latest
+ # a multi-platform image pulled from a registry, and an image built locally
+ - busybox:1.37.0
+ - skywalking-infra-e2e/kind-local:test
steps:
- name: deploy test pod
command: kubectl apply -f test/e2e/kind/deployment.yaml
@@ -37,3 +39,6 @@ verify:
- name: verify pod running with imported image
query: kubectl get pod import-test -o jsonpath='{.status.phase}'
expected: expected.yaml
+ - name: verify pod running with imported image on the control-plane
+ query: kubectl get pod import-test-control-plane -o jsonpath='{.status.phase}'
+ expected: expected.yaml
diff --git a/test/e2e/kind/kind-cluster.yaml b/test/e2e/kind/kind-cluster.yaml
index 1081c51..d07e27b 100644
--- a/test/e2e/kind/kind-cluster.yaml
+++ b/test/e2e/kind/kind-cluster.yaml
@@ -17,3 +17,5 @@ kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
+ # images must be loaded into every node
+ - role: worker