m/test/localregistry: use osbase/oci/registry
This replaces the localregistry implementation with a small wrapper
around the new registry package.
The images attribute of the Bazel rule was changed from a list to a
dict, which makes the repository and tag independent from the file path.
Change-Id: I1f6213dd67f7bdcf2373fe136958caa68b9f4d10
Reviewed-on: https://review.monogon.dev/c/monogon/+/4089
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/test/e2e/BUILD.bazel b/metropolis/test/e2e/BUILD.bazel
index 85ef13a..dc43be6 100644
--- a/metropolis/test/e2e/BUILD.bazel
+++ b/metropolis/test/e2e/BUILD.bazel
@@ -2,12 +2,12 @@
localregistry_manifest(
name = "testimages_manifest",
- images = [
- "//metropolis/test/e2e/selftest:selftest_image",
- "//metropolis/test/e2e/persistentvolume:persistentvolume_image",
- "//metropolis/test/e2e/httpserver:httpserver_image",
- "//metropolis/test/e2e/connectivity/agent:agent_image",
- ],
+ images = {
+ "selftest:latest": "//metropolis/test/e2e/selftest:selftest_image",
+ "persistentvolume:latest": "//metropolis/test/e2e/persistentvolume:persistentvolume_image",
+ "httpserver:latest": "//metropolis/test/e2e/httpserver:httpserver_image",
+ "connectivity/agent:latest": "//metropolis/test/e2e/connectivity/agent:agent_image",
+ },
visibility = [
"//metropolis/test/e2e/suites:__subpackages__",
],
diff --git a/metropolis/test/e2e/connectivity/connectivity.go b/metropolis/test/e2e/connectivity/connectivity.go
index 070bacc..7ecbb08 100644
--- a/metropolis/test/e2e/connectivity/connectivity.go
+++ b/metropolis/test/e2e/connectivity/connectivity.go
@@ -218,7 +218,7 @@
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Name: "connectivitytester",
- Image: "test.monogon.internal/metropolis/test/e2e/connectivity/agent/agent_image",
+ Image: "test.monogon.internal/connectivity/agent:latest",
Stdin: true,
}},
EnableServiceLinks: ptr.To(false),
diff --git a/metropolis/test/e2e/suites/kubernetes/kubernetes_helpers.go b/metropolis/test/e2e/suites/kubernetes/kubernetes_helpers.go
index 1f45fab..02eddfd 100644
--- a/metropolis/test/e2e/suites/kubernetes/kubernetes_helpers.go
+++ b/metropolis/test/e2e/suites/kubernetes/kubernetes_helpers.go
@@ -78,7 +78,7 @@
{
Name: "test",
ImagePullPolicy: corev1.PullIfNotPresent,
- Image: "test.monogon.internal/metropolis/test/e2e/httpserver/httpserver_image",
+ Image: "test.monogon.internal/httpserver:latest",
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(8080)},
@@ -130,7 +130,7 @@
{
Name: "test",
ImagePullPolicy: corev1.PullIfNotPresent,
- Image: "test.monogon.internal/metropolis/test/e2e/selftest/selftest_image",
+ Image: "test.monogon.internal/selftest:latest",
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
@@ -191,7 +191,7 @@
{
Name: "test",
ImagePullPolicy: corev1.PullIfNotPresent,
- Image: "test.monogon.internal/metropolis/test/e2e/persistentvolume/persistentvolume_image",
+ Image: "test.monogon.internal/persistentvolume:latest",
Args: []string{"-runtimeclass", runtimeClass},
VolumeMounts: []corev1.VolumeMount{
{
diff --git a/metropolis/test/launch/BUILD.bazel b/metropolis/test/launch/BUILD.bazel
index 166ba3a..492fdd4 100644
--- a/metropolis/test/launch/BUILD.bazel
+++ b/metropolis/test/launch/BUILD.bazel
@@ -49,8 +49,8 @@
"//metropolis/node/core/rpc/resolver",
"//metropolis/proto/api",
"//metropolis/proto/common",
- "//metropolis/test/localregistry",
"//osbase/logbuffer",
+ "//osbase/oci/registry",
"//osbase/test/qemu",
"@com_github_cenkalti_backoff_v4//:backoff",
"@com_github_kballard_go_shellquote//:go-shellquote",
diff --git a/metropolis/test/launch/cluster.go b/metropolis/test/launch/cluster.go
index d8eb8cd..5ad4121 100644
--- a/metropolis/test/launch/cluster.go
+++ b/metropolis/test/launch/cluster.go
@@ -51,7 +51,7 @@
"source.monogon.dev/metropolis/node"
"source.monogon.dev/metropolis/node/core/rpc"
"source.monogon.dev/metropolis/node/core/rpc/resolver"
- "source.monogon.dev/metropolis/test/localregistry"
+ "source.monogon.dev/osbase/oci/registry"
"source.monogon.dev/osbase/test/qemu"
)
@@ -583,7 +583,7 @@
// Optional local registry which will be made available to the cluster to
// pull images from. This is a more efficient alternative to preseeding all
// images used for testing.
- LocalRegistry *localregistry.Server
+ LocalRegistry *registry.Server
// InitialClusterConfiguration will be passed to the first node when creating the
// cluster, and defines some basic properties of the cluster. If not specified,
diff --git a/metropolis/test/localregistry/BUILD.bazel b/metropolis/test/localregistry/BUILD.bazel
index fa1f229..a5621fc 100644
--- a/metropolis/test/localregistry/BUILD.bazel
+++ b/metropolis/test/localregistry/BUILD.bazel
@@ -7,12 +7,8 @@
visibility = ["//visibility:public"],
deps = [
"//metropolis/test/localregistry/spec",
- "@com_github_docker_distribution//:distribution",
- "@com_github_docker_distribution//manifest/manifestlist",
- "@com_github_docker_distribution//manifest/ocischema",
- "@com_github_docker_distribution//manifest/schema2",
- "@com_github_docker_distribution//reference",
- "@com_github_opencontainers_go_digest//:go-digest",
+ "//osbase/oci",
+ "//osbase/oci/registry",
"@io_bazel_rules_go//go/runfiles",
"@org_golang_google_protobuf//encoding/prototext",
],
diff --git a/metropolis/test/localregistry/def.bzl b/metropolis/test/localregistry/def.bzl
index 289dc34..3b8e378 100644
--- a/metropolis/test/localregistry/def.bzl
+++ b/metropolis/test/localregistry/def.bzl
@@ -1,14 +1,14 @@
-#load("@io_bazel_rules_docker//container:providers.bzl", "ImageInfo")
-
def _localregistry_manifest_impl(ctx):
manifest_out = ctx.actions.declare_file(ctx.label.name + ".prototxt")
images = []
referenced = [manifest_out]
- for i in ctx.attr.images:
- image_file = i.files.to_list()[0]
+ for key, label in ctx.attr.images.items():
+ image_file = label[DefaultInfo].files.to_list()[0]
+ repository, _, tag = key.partition(":")
image = struct(
- name = i.label.package + "/" + i.label.name,
+ repository = repository,
+ tag = tag,
path = image_file.short_path,
)
referenced.append(image_file)
@@ -23,10 +23,12 @@
Builds a manifest for serving images directly from the build files.
""",
attrs = {
- "images": attr.label_list(
+ "images": attr.string_keyed_label_dict(
mandatory = True,
doc = """
- List of images to be served from the local registry.
+ Images to be served from the local registry.
+ The key defines the repository and tag, separated by ':'.
+ The value is a label which must contain an OCI layout directory.
""",
providers = [],
),
diff --git a/metropolis/test/localregistry/localregistry.go b/metropolis/test/localregistry/localregistry.go
index 1f648e0..694476e 100644
--- a/metropolis/test/localregistry/localregistry.go
+++ b/metropolis/test/localregistry/localregistry.go
@@ -6,170 +6,36 @@
package localregistry
import (
- "bytes"
- "encoding/json"
"fmt"
- "io"
- "log"
- "net/http"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
+ "path"
"github.com/bazelbuild/rules_go/go/runfiles"
- "github.com/docker/distribution"
- "github.com/docker/distribution/manifest/manifestlist"
- "github.com/docker/distribution/manifest/ocischema"
- "github.com/docker/distribution/manifest/schema2"
- "github.com/docker/distribution/reference"
- "github.com/opencontainers/go-digest"
"google.golang.org/protobuf/encoding/prototext"
"source.monogon.dev/metropolis/test/localregistry/spec"
+ "source.monogon.dev/osbase/oci"
+ "source.monogon.dev/osbase/oci/registry"
)
-type Server struct {
- manifests map[string][]byte
- blobs map[digest.Digest]blobMeta
-}
-
-type blobMeta struct {
- filePath string
- mediaType string
- contentLength int64
-}
-
-func manifestDescriptorFromBazel(image *spec.Image) (manifestlist.ManifestDescriptor, error) {
- indexPath, err := runfiles.Rlocation(filepath.Join("_main", image.Path, "index.json"))
- if err != nil {
- return manifestlist.ManifestDescriptor{}, fmt.Errorf("while locating manifest list file: %w", err)
- }
-
- manifestListRaw, err := os.ReadFile(indexPath)
- if err != nil {
- return manifestlist.ManifestDescriptor{}, fmt.Errorf("while opening manifest list file: %w", err)
- }
-
- var imageManifestList manifestlist.ManifestList
- if err := json.Unmarshal(manifestListRaw, &imageManifestList); err != nil {
- return manifestlist.ManifestDescriptor{}, fmt.Errorf("while unmarshaling manifest list for %q: %w", image.Name, err)
- }
-
- if len(imageManifestList.Manifests) != 1 {
- return manifestlist.ManifestDescriptor{}, fmt.Errorf("unexpected manifest list length > 1")
- }
-
- return imageManifestList.Manifests[0], nil
-}
-
-func manifestFromBazel(s *Server, image *spec.Image, md manifestlist.ManifestDescriptor) (ocischema.Manifest, error) {
- manifestPath, err := runfiles.Rlocation(filepath.Join("_main", image.Path, "blobs", md.Digest.Algorithm().String(), md.Digest.Hex()))
- if err != nil {
- return ocischema.Manifest{}, fmt.Errorf("while locating manifest file: %w", err)
- }
- manifestRaw, err := os.ReadFile(manifestPath)
- if err != nil {
- return ocischema.Manifest{}, fmt.Errorf("while opening manifest file: %w", err)
- }
-
- var imageManifest ocischema.Manifest
- if err := json.Unmarshal(manifestRaw, &imageManifest); err != nil {
- return ocischema.Manifest{}, fmt.Errorf("while unmarshaling manifest for %q: %w", image.Name, err)
- }
-
- // For Digest lookups
- s.manifests[image.Name] = manifestRaw
- s.manifests[md.Digest.String()] = manifestRaw
-
- return imageManifest, nil
-}
-
-func addBazelBlobFromDescriptor(s *Server, image *spec.Image, dd distribution.Descriptor) error {
- path, err := runfiles.Rlocation(filepath.Join("_main", image.Path, "blobs", dd.Digest.Algorithm().String(), dd.Digest.Hex()))
- if err != nil {
- return fmt.Errorf("while locating blob: %w", err)
- }
- s.blobs[dd.Digest] = blobMeta{filePath: path, mediaType: dd.MediaType, contentLength: dd.Size}
- return nil
-}
-
-func FromBazelManifest(mb []byte) (*Server, error) {
+func FromBazelManifest(mb []byte) (*registry.Server, error) {
var bazelManifest spec.Manifest
if err := prototext.Unmarshal(mb, &bazelManifest); err != nil {
- log.Fatalf("failed to parse manifest: %v", err)
+ return nil, fmt.Errorf("failed to parse manifest: %w", err)
}
- s := Server{
- manifests: make(map[string][]byte),
- blobs: make(map[digest.Digest]blobMeta),
- }
+ s := registry.NewServer()
for _, i := range bazelManifest.Images {
- md, err := manifestDescriptorFromBazel(i)
+ resolvedPath, err := runfiles.Rlocation(path.Join("_main", i.Path))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to resolve image path %q: %w", i.Path, err)
}
-
- if err := addBazelBlobFromDescriptor(&s, i, md.Descriptor); err != nil {
- return nil, err
- }
-
- m, err := manifestFromBazel(&s, i, md)
+ image, err := oci.ReadLayout(resolvedPath)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to read image from %q: %w", i.Path, err)
}
-
- if err := addBazelBlobFromDescriptor(&s, i, m.Config); err != nil {
- return nil, err
- }
- for _, l := range m.Layers {
- if err := addBazelBlobFromDescriptor(&s, i, l); err != nil {
- return nil, err
- }
+ err = s.AddImage(i.Repository, i.Tag, image)
+ if err != nil {
+ return nil, fmt.Errorf("failed to add image: %w", err)
}
}
- return &s, nil
-}
-
-var (
- versionCheckEp = regexp.MustCompile(`^/v2/$`)
- manifestEp = regexp.MustCompile("^/v2/(" + reference.NameRegexp.String() + ")/manifests/(" + reference.TagRegexp.String() + "|" + digest.DigestRegexp.String() + ")$")
- blobEp = regexp.MustCompile("^/v2/(" + reference.NameRegexp.String() + ")/blobs/(" + digest.DigestRegexp.String() + ")$")
-)
-
-func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- if req.Method != http.MethodGet && req.Method != http.MethodHead {
- w.WriteHeader(http.StatusMethodNotAllowed)
- fmt.Fprintf(w, "Registry is read-only, only GET and HEAD are allowed\n")
- return
- }
- w.Header().Set("Content-Type", "application/json")
- if versionCheckEp.MatchString(req.URL.Path) {
- w.WriteHeader(http.StatusOK)
- fmt.Fprintf(w, "{}")
- return
- } else if matches := manifestEp.FindStringSubmatch(req.URL.Path); len(matches) > 0 {
- name := matches[1]
- manifest, ok := s.manifests[name]
- if !ok {
- w.WriteHeader(http.StatusNotFound)
- fmt.Fprintf(w, "Image not found")
- return
- }
- w.Header().Set("Content-Type", schema2.MediaTypeManifest)
- w.Header().Set("Content-Length", strconv.FormatInt(int64(len(manifest)), 10))
- w.WriteHeader(http.StatusOK)
- io.Copy(w, bytes.NewReader(manifest))
- } else if matches := blobEp.FindStringSubmatch(req.URL.Path); len(matches) > 0 {
- bm, ok := s.blobs[digest.Digest(matches[2])]
- if !ok {
- w.WriteHeader(http.StatusNotFound)
- fmt.Fprintf(w, "Blob not found")
- return
- }
- w.Header().Set("Content-Type", bm.mediaType)
- w.Header().Set("Content-Length", strconv.FormatInt(bm.contentLength, 10))
- http.ServeFile(w, req, bm.filePath)
- } else {
- w.WriteHeader(http.StatusNotFound)
- }
+ return s, nil
}
diff --git a/metropolis/test/localregistry/spec/manifest.proto b/metropolis/test/localregistry/spec/manifest.proto
index d9cbeab..529c013 100644
--- a/metropolis/test/localregistry/spec/manifest.proto
+++ b/metropolis/test/localregistry/spec/manifest.proto
@@ -6,14 +6,16 @@
// Single image metadata
message Image {
- // Name of the image (no domain or tag, just slash-separated path)
- string name = 1;
- // Path to the image
- string path = 2;
+ // Repository where the image is served
+ string repository = 1;
+ // Tag where the image is served
+ string tag = 2;
+ // Path to the OCI layout directory containing the image
+ string path = 3;
}
// Main message
message Manifest {
// List of images for the local registry
repeated Image images = 1;
-}
\ No newline at end of file
+}