Merge pull request #2554 from cgwalters/layer-info-extended

proxy: Add GetLayerInfoPiped
This commit is contained in:
Colin Walters 2025-03-24 10:35:27 -04:00 committed by GitHub
commit 5b0b0d3954
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 78 additions and 7 deletions

View File

@ -92,7 +92,8 @@ import (
// 0.2.4: Added OpenImageOptional
// 0.2.5: Added LayerInfoJSON
// 0.2.6: Policy Verification before pulling OCI
const protocolVersion = "0.2.6"
// 0.2.7: Added GetLayerInfoPiped
const protocolVersion = "0.2.7"
// maxMsgSize is the current limit on a packet size.
// Note that all non-metadata (i.e. payload data) is sent over a pipe.
@ -618,9 +619,10 @@ func (h *proxyHandler) GetBlob(args []any) (replyBuf, error) {
// GetLayerInfo returns data about the layers of an image, useful for reading the layer contents.
//
// This needs to be called since the data returned by GetManifest() does not allow to correctly
// calling GetBlob() for the containers-storage: transport (which doesnt store the original compressed
// representations referenced in the manifest).
// This is the same as GetLayerInfoPiped, but returns its contents inline. This is subject to
// failure for large images (because we use SOCK_SEQPACKET which has a maximum buffer size)
// and is hence only retained for backwards compatibility. Callers are expected to use
// the semver to know whether they can call the new API.
func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
@ -666,6 +668,59 @@ func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
return ret, nil
}
// GetLayerInfoPiped returns data about the layers of an image, useful for reading the layer contents.
//
// This needs to be called since the data returned by GetManifest() does not allow to correctly
// calling GetBlob() for the containers-storage: transport (which doesnt store the original compressed
// representations referenced in the manifest).
func (h *proxyHandler) GetLayerInfoPiped(args []any) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("found %d args, expecting (imgid)", len(args))
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
ctx := context.TODO()
err = h.cacheTargetManifest(imgref)
if err != nil {
return ret, err
}
img := imgref.cachedimg
layerInfos, err := img.LayerInfosForCopy(ctx)
if err != nil {
return ret, err
}
if layerInfos == nil {
layerInfos = img.LayerInfos()
}
layers := make([]convertedLayerInfo, 0, len(layerInfos))
for _, layer := range layerInfos {
layers = append(layers, convertedLayerInfo{layer.Digest, layer.Size, layer.MediaType})
}
serialized, err := json.Marshal(&layers)
if err != nil {
return ret, err
}
return h.returnBytes(nil, serialized)
}
// FinishPipe waits for the worker goroutine to finish, and closes the write side of the pipe.
func (h *proxyHandler) FinishPipe(args []any) (replyBuf, error) {
h.lock.Lock()
@ -805,6 +860,8 @@ func (h *proxyHandler) processRequest(readBytes []byte) (rb replyBuf, terminate
rb, err = h.GetBlob(req.Args)
case "GetLayerInfo":
rb, err = h.GetLayerInfo(req.Args)
case "GetLayerInfoPiped":
rb, err = h.GetLayerInfoPiped(req.Args)
case "FinishPipe":
rb, err = h.FinishPipe(req.Args)
case "Shutdown":

View File

@ -229,7 +229,8 @@ type byteFetch struct {
err error
}
func runTestGetManifestAndConfig(p *proxy, img string) error {
// This exercises all the metadata fetching APIs.
func runTestMetadataAPIs(p *proxy, img string) error {
v, err := p.callNoFd("OpenImage", []any{img})
if err != nil {
return err
@ -291,6 +292,19 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
return fmt.Errorf("No CMD or ENTRYPOINT set")
}
_, layerInfoBytes, err := p.callReadAllBytes("GetLayerInfoPiped", []any{imgid})
if err != nil {
return err
}
var layerInfoBytesData []interface{}
err = json.Unmarshal(layerInfoBytes, &layerInfoBytesData)
if err != nil {
return err
}
if len(layerInfoBytesData) == 0 {
return fmt.Errorf("expected layer info data")
}
// Also test this legacy interface
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []any{imgid})
if err != nil {
@ -337,13 +351,13 @@ func (s *proxySuite) TestProxy() {
p, err := newProxy()
require.NoError(t, err)
err = runTestGetManifestAndConfig(p, knownNotManifestListedImageX8664)
err = runTestMetadataAPIs(p, knownNotManifestListedImageX8664)
if err != nil {
err = fmt.Errorf("Testing image %s: %v", knownNotManifestListedImageX8664, err)
}
assert.NoError(t, err)
err = runTestGetManifestAndConfig(p, knownListImage)
err = runTestMetadataAPIs(p, knownListImage)
if err != nil {
err = fmt.Errorf("Testing image %s: %v", knownListImage, err)
}