Files
containerd/pkg/testutil/registry.go
Akihiro Suda f418688f2c integration: build the whiteout-test image locally
Build the image for TestIssue13030 locally using pkg/archive,
instead of pulling the prebuilt ghcr.io/containerd/whiteout-test image.

The image is built by appending layers generated with archive.WriteDiff
on top of the busybox base image, served with the new
testutil.ServeImage helper on a localhost HTTP registry, and pulled
through the regular client.Pull path with WithPullUnpack. This removes
the need to publish the multi-arch whiteout-test image, so the
Dockerfile and Makefile added in PR 13704 are removed along with the
image list entry.

Assisted-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2026-07-08 23:39:42 +09:00

93 lines
2.9 KiB
Go

/*
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 testutil
import (
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ServeImage serves an image from a read-only HTTP registry listening on
// localhost, and returns a reference to pull the image from.
//
// blobs must be keyed by digest and contain the manifest blob referenced by
// target, along with the blobs it references, except for those already
// present in the puller's content store. The registry is shut down when the
// test completes.
func ServeImage(t *testing.T, name, tag string, target ocispec.Descriptor, blobs map[digest.Digest][]byte) string {
manifestPath := "/v2/" + name + "/manifests/"
blobPath := "/v2/" + name + "/blobs/"
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
var b []byte
mediaType := "application/octet-stream"
switch {
case strings.HasPrefix(r.URL.Path, manifestPath):
// A manifest may be requested by the tag, by the target
// digest, or, for a multi-platform image, by the digest of
// a platform manifest.
dgst := target.Digest
if ref := strings.TrimPrefix(r.URL.Path, manifestPath); ref != tag {
var err error
if dgst, err = digest.Parse(ref); err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
}
var ok bool
if b, ok = blobs[dgst]; !ok {
w.WriteHeader(http.StatusNotFound)
return
}
if dgst == target.Digest {
mediaType = target.MediaType
}
w.Header().Set("Docker-Content-Digest", dgst.String())
case strings.HasPrefix(r.URL.Path, blobPath):
dgst, err := digest.Parse(strings.TrimPrefix(r.URL.Path, blobPath))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
var ok bool
if b, ok = blobs[dgst]; !ok {
w.WriteHeader(http.StatusNotFound)
return
}
default:
w.WriteHeader(http.StatusNotFound)
return
}
w.Header().Set("Content-Type", mediaType)
w.Header().Set("Content-Length", strconv.Itoa(len(b)))
if r.Method == http.MethodGet {
w.Write(b)
}
}))
t.Cleanup(srv.Close)
return strings.TrimPrefix(srv.URL, "http://") + "/" + name + ":" + tag
}