mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-02-03 20:40:26 -05:00
Add the fake registry server functionality to agnhost windows
there are recent changes in k8s e2e test for image pull test to switch to use fake registry server, https://github.com/kubernetes/kubernetes/pull/133272 https://github.com/kubernetes/kubernetes/pull/134453 Unfortunately, this does not take into account of windows node. so the corresonding test on windows node strat to break The try to address this issue, by (1): update the agnhost windows image to include the fake registery server functionality as well (2): update the (common) pull image test to include windows node, will require (a): deploy the agnhost faker register server as a HPC pod (b): update the comanndline/options for the windows container specific
This commit is contained in:
parent
f2b4e9f9c5
commit
979f73bf7d
6 changed files with 119 additions and 26 deletions
|
|
@ -127,6 +127,10 @@ func podManifest(podTestLabel string) (*v1.Pod, error) {
|
|||
},
|
||||
}
|
||||
pod.Spec.Containers[0].SecurityContext.RunAsUser = ptr.To[int64](5123)
|
||||
pod.Spec.Containers[0].SecurityContext.WindowsOptions = &v1.WindowsSecurityContextOptions{
|
||||
RunAsUserName: ptr.To("NT AUTHORITY\\SYSTEM"),
|
||||
HostProcess: ptr.To(true),
|
||||
}
|
||||
// setting HostNetwork to true so that the registry is accessible on localhost:<hostport>
|
||||
// and we don't have to deal with any CNI quirks.
|
||||
pod.Spec.HostNetwork = true
|
||||
|
|
|
|||
|
|
@ -15,9 +15,23 @@
|
|||
ARG BASEIMAGE
|
||||
ARG REGISTRY
|
||||
ARG OS_VERSION
|
||||
ARG GOLANG_VERSION
|
||||
|
||||
FROM --platform=linux/$BUILDARCH golang:$GOLANG_VERSION AS prepregistry
|
||||
|
||||
RUN go install github.com/google/go-containerregistry/cmd/crane@latest && \
|
||||
apt-get update && apt-get install -y jq
|
||||
|
||||
COPY fakeregistryserver/prepare_registry.sh /prepare_registry.sh
|
||||
COPY fakeregistryserver/images.txt /images.txt
|
||||
|
||||
RUN chmod +x /prepare_registry.sh
|
||||
|
||||
# run the script during the build to create the artifact inside the image
|
||||
RUN /prepare_registry.sh
|
||||
|
||||
# We're using a Linux image to unpack the archives, then we're copying them over to Windows.
|
||||
FROM --platform=linux/amd64 alpine:3.21 as prep
|
||||
FROM --platform=linux/$BUILDARCH alpine:3.21 as prep
|
||||
|
||||
ADD https://github.com/coredns/coredns/releases/download/v1.5.0/coredns_1.5.0_windows_amd64.tgz /coredns.tgz
|
||||
ADD https://iperf.fr/download/windows/iperf-2.0.9-win64.zip /iperf.zip
|
||||
|
|
@ -32,6 +46,8 @@ RUN tar -xzvf /coredns.tgz &&\
|
|||
mv coreutils-8.31-28-windows-64bit wincoreutils &&\
|
||||
mkdir /uploads
|
||||
|
||||
# TODO: Change to linux/$BUILDARCH when windows-servercore-cache has arm64 variant
|
||||
# Keep linux/amd64 hardcoded: windows-servercore-cache only has amd64 variant (no arm64 available)
|
||||
FROM --platform=linux/amd64 $REGISTRY/windows-servercore-cache:1.0-linux-amd64-$OS_VERSION as servercore-helper
|
||||
FROM $BASEIMAGE
|
||||
|
||||
|
|
@ -48,6 +64,8 @@ COPY --from=servercore-helper /Windows/System32/netapi32.dll /Windows/System32/n
|
|||
COPY --from=prep /coredns.exe /coredns.exe
|
||||
COPY --from=prep /iperf /iperf
|
||||
COPY --from=prep /wincoreutils/sync.exe /bin/sync.exe
|
||||
# Copy the fake registry data from the preparer stage
|
||||
COPY --from=prepregistry /registry /var/registry
|
||||
|
||||
# NOTE(claudiub): docker buildx sets the PATH env variable to a Linux-like PATH, which is not desirable.
|
||||
ENV PATH="C:\dig\;C:\bin\;C:\curl\;C:\Windows\system32;C:\Windows;C:\Program Files\PowerShell;"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.62.1
|
||||
2.62.2
|
||||
|
|
|
|||
|
|
@ -24,6 +24,18 @@ readonly REGISTRY_DIR="/registry"
|
|||
# This script prepares a directory with container images to be used as a fake registry,
|
||||
# then creates a tarball of that directory inside the container.
|
||||
|
||||
# digest_to_path converts a digest to a nested directory path following the Docker
|
||||
# registry storage layout. For example, "sha256:abc123..." becomes
|
||||
# "<base_dir>/sha256/ab/abc123.../data".
|
||||
digest_to_path() {
|
||||
local base_dir="$1"
|
||||
local digest="$2"
|
||||
local algo="${digest%%:*}" # e.g., "sha256"
|
||||
local hash="${digest#*:}" # e.g., "abc123..."
|
||||
local prefix="${hash:0:2}" # first 2 chars
|
||||
echo "$base_dir/$algo/$prefix/$hash/data"
|
||||
}
|
||||
|
||||
# function to download an image manifest and its blobs to create a fake registry layout.
|
||||
prepare_image() {
|
||||
local image_name="$1"
|
||||
|
|
@ -42,33 +54,44 @@ prepare_image() {
|
|||
crane manifest "$REGISTRY_URL/$image_name:$tag" | jq '.manifests |= map(select(.platform.os != "windows"))' > "$tmp_manifest_path"
|
||||
echo "Saved manifest list to $tmp_manifest_path"
|
||||
|
||||
local manifest_digest
|
||||
manifest_digest="sha256:$(sha256sum < "$tmp_manifest_path" | awk '{print $1}')"
|
||||
mv "$tmp_manifest_path" "$image_dir/manifests/$manifest_digest"
|
||||
echo "Saved manifest list to $image_dir/manifests/$manifest_digest"
|
||||
local manifest_hash manifest_digest manifest_path
|
||||
manifest_hash="$(sha256sum < "$tmp_manifest_path" | awk '{print $1}')"
|
||||
manifest_digest="sha256:$manifest_hash"
|
||||
manifest_path="$(digest_to_path "$image_dir/manifests" "$manifest_digest")"
|
||||
mkdir -p "$(dirname "$manifest_path")"
|
||||
mv "$tmp_manifest_path" "$manifest_path"
|
||||
echo "Saved manifest list to $manifest_path"
|
||||
|
||||
# the file named after the tag now contains only the digest, acting as a redirect pointer
|
||||
echo "$manifest_digest" > "$image_dir/manifests/${internal_tag}"
|
||||
echo "Created tag file ${internal_tag} pointing to digest $manifest_digest"
|
||||
|
||||
echo "Parsing manifest list and downloading individual manifests and blobs..."
|
||||
|
||||
jq -r '.manifests[].digest' < "$image_dir/manifests/$manifest_digest" | while read -r individual_manifest_digest; do
|
||||
|
||||
jq -r '.manifests[].digest' < "$manifest_path" | while read -r individual_manifest_digest; do
|
||||
echo " Downloading manifest $individual_manifest_digest..."
|
||||
local individual_manifest_path="$image_dir/manifests/$individual_manifest_digest"
|
||||
local individual_manifest_path
|
||||
individual_manifest_path="$(digest_to_path "$image_dir/manifests" "$individual_manifest_digest")"
|
||||
mkdir -p "$(dirname "$individual_manifest_path")"
|
||||
crane manifest "$REGISTRY_URL/$image_name@$individual_manifest_digest" > "$individual_manifest_path"
|
||||
echo " Saved manifest to $individual_manifest_path"
|
||||
|
||||
local config_digest
|
||||
config_digest=$(jq -r '.config.digest' < "$individual_manifest_path")
|
||||
echo " Downloading config blob $config_digest..."
|
||||
crane blob "$REGISTRY_URL/$image_name@$config_digest" > "$image_dir/blobs/$config_digest"
|
||||
echo " Saved config blob to $image_dir/blobs/$config_digest"
|
||||
local config_path
|
||||
config_path="$(digest_to_path "$image_dir/blobs" "$config_digest")"
|
||||
mkdir -p "$(dirname "$config_path")"
|
||||
crane blob "$REGISTRY_URL/$image_name@$config_digest" > "$config_path"
|
||||
echo " Saved config blob to $config_path"
|
||||
|
||||
jq -r '.layers[].digest' < "$individual_manifest_path" | while read -r layer_digest; do
|
||||
echo " Downloading layer blob $layer_digest..."
|
||||
crane blob "$REGISTRY_URL/$image_name@$layer_digest" > "$image_dir/blobs/$layer_digest"
|
||||
echo " Saved layer blob to $image_dir/blobs/$layer_digest"
|
||||
local layer_path
|
||||
layer_path="$(digest_to_path "$image_dir/blobs" "$layer_digest")"
|
||||
mkdir -p "$(dirname "$layer_path")"
|
||||
crane blob "$REGISTRY_URL/$image_name@$layer_digest" > "$layer_path"
|
||||
echo " Saved layer blob to $layer_path"
|
||||
done
|
||||
done
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
|
@ -52,6 +53,8 @@ var CmdFakeRegistryServer = &cobra.Command{
|
|||
}
|
||||
|
||||
func main(cmd *cobra.Command, args []string) {
|
||||
log.Printf("Registry server starting with registry directory: %s", registryDir)
|
||||
|
||||
registryMux := NewRegistryServerMux(private)
|
||||
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
|
|
@ -86,9 +89,23 @@ func auth(h http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
// digestToPath converts a digest to a nested directory path following the Docker
|
||||
// registry storage layout. For example, "sha256:abc123..." becomes
|
||||
// "<baseDir>/sha256/ab/abc123.../data". This avoids filesystem issues with colons
|
||||
// in filenames on Windows and matches the standard Docker registry layout.
|
||||
func digestToPath(baseDir, digest string) string {
|
||||
parts := strings.SplitN(digest, ":", 2)
|
||||
if len(parts) != 2 || len(parts[1]) < 2 {
|
||||
return filepath.Join(baseDir, digest)
|
||||
}
|
||||
algo := parts[0] // e.g., "sha256"
|
||||
hash := parts[1] // e.g., "abc123..."
|
||||
return filepath.Join(baseDir, algo, hash[:2], hash, "data")
|
||||
}
|
||||
|
||||
// handleBlobs serves blob requests
|
||||
func handleBlobs(w http.ResponseWriter, r *http.Request, imageName, identifier string) {
|
||||
filePath := fmt.Sprintf("%s/%s/blobs/%s", registryDir, imageName, identifier)
|
||||
filePath := digestToPath(filepath.Join(registryDir, imageName, "blobs"), identifier)
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
log.Printf("Serving blob: %s", filePath)
|
||||
http.ServeFile(w, r, filePath)
|
||||
|
|
@ -98,12 +115,16 @@ func handleBlobs(w http.ResponseWriter, r *http.Request, imageName, identifier s
|
|||
// based on the manifest's mediaType field. If the identifier is a tag, it
|
||||
// reads the digest from the tag file and issues a redirect.
|
||||
func handleManifests(w http.ResponseWriter, r *http.Request, imageName, identifier string) {
|
||||
filePath := fmt.Sprintf("%s/%s/manifests/%s", registryDir, imageName, identifier)
|
||||
manifestsDir := filepath.Join(registryDir, imageName, "manifests")
|
||||
|
||||
// if the identifier is not a digest, assume it's a tag and perform a redirect.
|
||||
if !strings.HasPrefix(identifier, "sha256:") {
|
||||
digest, err := os.ReadFile(filePath)
|
||||
// Tags are stored as flat files (not nested)
|
||||
tagFilePath := filepath.Join(manifestsDir, identifier)
|
||||
log.Printf("Looking for tag file: %s", tagFilePath)
|
||||
digest, err := os.ReadFile(tagFilePath)
|
||||
if err != nil {
|
||||
log.Printf("Tag file not found: %s, error: %v", tagFilePath, err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
|
@ -113,8 +134,11 @@ func handleManifests(w http.ResponseWriter, r *http.Request, imageName, identifi
|
|||
return
|
||||
}
|
||||
|
||||
filePath := digestToPath(manifestsDir, identifier)
|
||||
log.Printf("Serving manifest: %s", filePath)
|
||||
manifestContent, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
log.Printf("Manifest file not found: %s, error: %v", filePath, err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,25 +66,27 @@ func setupTestRegistry(t *testing.T) (string, func() error) {
|
|||
|
||||
manifestsDir := filepath.Join(tempDir, testImageName, "manifests")
|
||||
blobsDir := filepath.Join(tempDir, testImageName, "blobs")
|
||||
if err := os.MkdirAll(manifestsDir, 0755); err != nil {
|
||||
|
||||
// write the manifest file using nested path
|
||||
manifestPath := digestToPath(manifestsDir, testManifestDigest)
|
||||
if err := os.MkdirAll(filepath.Dir(manifestPath), 0755); err != nil {
|
||||
t.Fatalf("Failed to create manifests dir: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(blobsDir, 0755); err != nil {
|
||||
t.Fatalf("Failed to create blobs dir: %v", err)
|
||||
}
|
||||
|
||||
// write the manifest file
|
||||
if err := os.WriteFile(filepath.Join(manifestsDir, testManifestDigest), []byte(testManifestContent), 0644); err != nil {
|
||||
if err := os.WriteFile(manifestPath, []byte(testManifestContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to write manifest file: %v", err)
|
||||
}
|
||||
|
||||
// write the tag file
|
||||
// write the tag file (tags are still flat files)
|
||||
if err := os.WriteFile(filepath.Join(manifestsDir, testTag), []byte(testManifestDigest), 0644); err != nil {
|
||||
t.Fatalf("Failed to write tag file: %v", err)
|
||||
}
|
||||
|
||||
// write the blob file
|
||||
if err := os.WriteFile(filepath.Join(blobsDir, testBlobDigest), []byte(testBlobContent), 0644); err != nil {
|
||||
// write the blob file using nested path
|
||||
blobPath := digestToPath(blobsDir, testBlobDigest)
|
||||
if err := os.MkdirAll(filepath.Dir(blobPath), 0755); err != nil {
|
||||
t.Fatalf("Failed to create blobs dir: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(blobPath, []byte(testBlobContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to write blob file: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +97,28 @@ func setupTestRegistry(t *testing.T) (string, func() error) {
|
|||
return tempDir, cleanup
|
||||
}
|
||||
|
||||
func TestDigestToPath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
baseDir string
|
||||
digest string
|
||||
expected string
|
||||
}{
|
||||
{"valid sha256", "/base", "sha256:abc123", "/base/sha256/ab/abc123/data"},
|
||||
{"short hash", "/base", "sha256:a", "/base/sha256:a"}, // fallback
|
||||
{"no colon", "/base", "invalid", "/base/invalid"},
|
||||
{"empty hash", "/base", "sha256:", "/base/sha256:"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := digestToPath(tt.baseDir, tt.digest)
|
||||
if got != tt.expected {
|
||||
t.Errorf("digestToPath() = %v, want %v", got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryServer(t *testing.T) {
|
||||
tempDir, cleanup := setupTestRegistry(t)
|
||||
defer func() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue