osbase/oci: implement support for OCI index

Previously, only OCI images were supported, now we can also handle
indexes. The new Ref type is either an Image or Index.

Change-Id: I1b282ed6078d53e9a69e7613f601fdbbe64e192b
Reviewed-on: https://review.monogon.dev/c/monogon/+/4475
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/metropolis/cli/metroctl/cmd_install_usb.go b/metropolis/cli/metroctl/cmd_install_usb.go
index 4453ed7..5af158a 100644
--- a/metropolis/cli/metroctl/cmd_install_usb.go
+++ b/metropolis/cli/metroctl/cmd_install_usb.go
@@ -38,7 +38,7 @@
 		if err != nil {
 			return err
 		}
-		image, err := oci.ReadLayout(imagePathResolved)
+		image, err := oci.AsImage(oci.ReadLayout(imagePathResolved))
 		if err != nil {
 			return fmt.Errorf("failed to read OS image: %w", err)
 		}
diff --git a/metropolis/cli/takeover/install.go b/metropolis/cli/takeover/install.go
index 97e8565..9755c55 100644
--- a/metropolis/cli/takeover/install.go
+++ b/metropolis/cli/takeover/install.go
@@ -37,7 +37,7 @@
 		return err
 	}
 
-	image, err := oci.ReadLayout("/osimage")
+	image, err := oci.AsImage(oci.ReadLayout("/osimage"))
 	if err != nil {
 		return fmt.Errorf("failed to read OS image: %w", err)
 	}
diff --git a/metropolis/cli/takeover/takeover.go b/metropolis/cli/takeover/takeover.go
index 805a06d..5ff26bd 100644
--- a/metropolis/cli/takeover/takeover.go
+++ b/metropolis/cli/takeover/takeover.go
@@ -94,7 +94,7 @@
 		return nil, err
 	}
 
-	image, err := oci.ReadLayout(filepath.Join(filepath.Dir(currPath), "osimage"))
+	image, err := oci.AsImage(oci.ReadLayout(filepath.Join(filepath.Dir(currPath), "osimage")))
 	if err != nil {
 		return nil, fmt.Errorf("failed to read OS image: %w", err)
 	}
diff --git a/metropolis/installer/main.go b/metropolis/installer/main.go
index 735a8cd..ecb4d01 100644
--- a/metropolis/installer/main.go
+++ b/metropolis/installer/main.go
@@ -161,7 +161,7 @@
 		return fmt.Errorf("failed to open node parameters from ESP: %w", err)
 	}
 
-	ociImage, err := oci.ReadLayout("/installer/metropolis-installer/osimage")
+	ociImage, err := oci.AsImage(oci.ReadLayout("/installer/metropolis-installer/osimage"))
 	if err != nil {
 		return fmt.Errorf("failed to read OS image from ESP: %w", err)
 	}
diff --git a/metropolis/installer/test/run_test.go b/metropolis/installer/test/run_test.go
index daff8cd..898ad44 100644
--- a/metropolis/installer/test/run_test.go
+++ b/metropolis/installer/test/run_test.go
@@ -156,7 +156,7 @@
 		log.Fatal(err)
 	}
 
-	image, err := oci.ReadLayout(xImagePath)
+	image, err := oci.AsImage(oci.ReadLayout(xImagePath))
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/metropolis/node/core/update/BUILD.bazel b/metropolis/node/core/update/BUILD.bazel
index 73f6773..ed98d21 100644
--- a/metropolis/node/core/update/BUILD.bazel
+++ b/metropolis/node/core/update/BUILD.bazel
@@ -18,6 +18,7 @@
         "//osbase/efivarfs",
         "//osbase/gpt",
         "//osbase/kexec",
+        "//osbase/oci",
         "//osbase/oci/osimage",
         "//osbase/oci/registry",
         "@com_github_cenkalti_backoff_v4//:backoff",
diff --git a/metropolis/node/core/update/e2e/e2e_test.go b/metropolis/node/core/update/e2e/e2e_test.go
index 890a06f..041de3f 100644
--- a/metropolis/node/core/update/e2e/e2e_test.go
+++ b/metropolis/node/core/update/e2e/e2e_test.go
@@ -141,7 +141,7 @@
 		Port: 80,
 	}
 
-	imageX, err := oci.ReadLayout(xImageXPath)
+	imageX, err := oci.AsImage(oci.ReadLayout(xImageXPath))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -160,8 +160,8 @@
 	}
 
 	registryServer := registry.NewServer()
-	registryServer.AddImage("testos", "y", imageY)
-	registryServer.AddImage("testos", "z", imageZ)
+	registryServer.AddRef("testos", "y", imageY)
+	registryServer.AddRef("testos", "z", imageZ)
 	registryLis, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		t.Fatal(err)
@@ -225,8 +225,8 @@
 		"-device", "virtio-rng-pci",
 		"-serial", "stdio",
 		"-no-reboot",
-		"-fw_cfg", "name=opt/testos_y_digest,string=" + imageY.ManifestDigest,
-		"-fw_cfg", "name=opt/testos_z_digest,string=" + imageZ.ManifestDigest,
+		"-fw_cfg", "name=opt/testos_y_digest,string=" + imageY.Digest(),
+		"-fw_cfg", "name=opt/testos_z_digest,string=" + imageZ.Digest(),
 	}
 	return qemuArgs
 }
diff --git a/metropolis/node/core/update/update.go b/metropolis/node/core/update/update.go
index 66e7501..047e9f8 100644
--- a/metropolis/node/core/update/update.go
+++ b/metropolis/node/core/update/update.go
@@ -32,6 +32,7 @@
 	"source.monogon.dev/osbase/efivarfs"
 	"source.monogon.dev/osbase/gpt"
 	"source.monogon.dev/osbase/kexec"
+	"source.monogon.dev/osbase/oci"
 	"source.monogon.dev/osbase/oci/osimage"
 	"source.monogon.dev/osbase/oci/registry"
 
@@ -289,7 +290,7 @@
 		Repository: imageRef.Repository,
 	}
 
-	image, err := client.Read(downloadCtx, imageRef.Tag, imageRef.Digest)
+	image, err := oci.AsImage(client.Read(downloadCtx, imageRef.Tag, imageRef.Digest))
 	if err != nil {
 		return fmt.Errorf("failed to fetch OS image: %w", err)
 	}
diff --git a/metropolis/test/launch/cluster.go b/metropolis/test/launch/cluster.go
index 7f64427..c77a101 100644
--- a/metropolis/test/launch/cluster.go
+++ b/metropolis/test/launch/cluster.go
@@ -166,7 +166,7 @@
 	}
 
 	// Initialize the node's storage.
-	ociImage, err := oci.ReadLayout(xNodeImagePath)
+	ociImage, err := oci.AsImage(oci.ReadLayout(xNodeImagePath))
 	if err != nil {
 		return nil, fmt.Errorf("failed to read OS image: %w", err)
 	}
diff --git a/metropolis/test/localregistry/localregistry.go b/metropolis/test/localregistry/localregistry.go
index 694476e..7440aae 100644
--- a/metropolis/test/localregistry/localregistry.go
+++ b/metropolis/test/localregistry/localregistry.go
@@ -32,7 +32,7 @@
 		if err != nil {
 			return nil, fmt.Errorf("failed to read image from %q: %w", i.Path, err)
 		}
-		err = s.AddImage(i.Repository, i.Tag, image)
+		err = s.AddRef(i.Repository, i.Tag, image)
 		if err != nil {
 			return nil, fmt.Errorf("failed to add image: %w", err)
 		}