Support injecting container images for development
This adds the LoadImage RPC and an accompanying subcommand
to the debug API which allows loading images into
an existing Metropolis node for
development or testing.
Change-Id: I51d802630ae4c95fb874e01bfb6510ab69c322e1
Reviewed-on: https://review.monogon.dev/c/monogon/+/219
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/metropolis/node/core/BUILD.bazel b/metropolis/node/core/BUILD.bazel
index cb82fdd..e0d6d87 100644
--- a/metropolis/node/core/BUILD.bazel
+++ b/metropolis/node/core/BUILD.bazel
@@ -26,6 +26,8 @@
"//metropolis/pkg/supervisor:go_default_library",
"//metropolis/pkg/tpm:go_default_library",
"//metropolis/proto/api:go_default_library",
+ "@com_github_containerd_containerd//:go_default_library",
+ "@com_github_containerd_containerd//namespaces:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
diff --git a/metropolis/node/core/debug_service.go b/metropolis/node/core/debug_service.go
index 30f7ac7..123d1ab 100644
--- a/metropolis/node/core/debug_service.go
+++ b/metropolis/node/core/debug_service.go
@@ -25,10 +25,13 @@
"regexp"
"strings"
+ ctr "github.com/containerd/containerd"
+ "github.com/containerd/containerd/namespaces"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"source.monogon.dev/metropolis/node/core/roleserve"
+ "source.monogon.dev/metropolis/node/core/localstorage"
"source.monogon.dev/metropolis/pkg/logtree"
apb "source.monogon.dev/metropolis/proto/api"
)
@@ -41,6 +44,8 @@
type debugService struct {
roleserve *roleserve.Service
logtree *logtree.LogTree
+ ephemeralVolume *localstorage.EphemeralContainerdDirectory
+
// traceLock provides exclusive access to the Linux tracing infrastructure
// (ftrace)
// This is a channel because Go's mutexes can't be cancelled or be acquired
@@ -272,3 +277,40 @@
}
return nil
}
+
+// imageReader is an adapter converting a gRPC stream into an io.Reader
+type imageReader struct {
+ srv apb.NodeDebugService_LoadImageServer
+ restOfPart []byte
+}
+
+func (i *imageReader) Read(p []byte) (n int, err error) {
+ n1 := copy(p, i.restOfPart)
+ if len(p) > len(i.restOfPart) {
+ part, err := i.srv.Recv()
+ if err != nil {
+ return n1, err
+ }
+ n2 := copy(p[n1:], part.DataPart)
+ i.restOfPart = part.DataPart[n2:]
+ return n1 + n2, nil
+ } else {
+ i.restOfPart = i.restOfPart[n1:]
+ return n1, nil
+ }
+}
+
+// LoadImage loads an OCI image into the image cache of this node
+func (s *debugService) LoadImage(srv apb.NodeDebugService_LoadImageServer) error {
+ client, err := ctr.New(s.ephemeralVolume.ClientSocket.FullPath())
+ if err != nil {
+ return status.Errorf(codes.Unavailable, "failed to connect to containerd: %v", err)
+ }
+ ctxWithNS := namespaces.WithNamespace(srv.Context(), "k8s.io")
+ reader := &imageReader{srv: srv}
+ _, err = client.Import(ctxWithNS, reader)
+ if err != nil {
+ return status.Errorf(codes.Unknown, "failed to import image: %v", err)
+ }
+ return srv.SendAndClose(&apb.LoadImageResponse{})
+}
diff --git a/metropolis/node/core/main.go b/metropolis/node/core/main.go
index 566e65d..e15cd6c 100644
--- a/metropolis/node/core/main.go
+++ b/metropolis/node/core/main.go
@@ -217,6 +217,7 @@
roleserve: rs,
logtree: lt,
traceLock: make(chan struct{}, 1),
+ ephemeralVolume: &root.Ephemeral.Containerd,
}
dbgSrv := grpc.NewServer()
apb.RegisterNodeDebugServiceServer(dbgSrv, dbg)