From 3606b2d1defe8742d640ad3b8aa6e9a7a4c2f2ac Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 6 Dec 2021 17:15:46 -0500 Subject: [PATCH 1/2] proxy: Add a GetFullConfig method Sadly...I swear I had tested this at one point, but it was *definitely* not the intention that we just return the container runtime configuration. I need a method to return the full image configuration. At some point I must have accidentally added a redundant `.Config`. This whole new method `GetFullConfig` is like `GetConfig` but returns the whole image configuration. A specific motivation here is that it's only in the image configuration that we can stick arbitrary metadata (labels) that will survive a round trip through docker schema v2. --- cmd/skopeo/proxy.go | 43 +++++++++++++++++++++++++++++++++++++-- integration/proxy_test.go | 25 ++++++++++++++++++++--- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/cmd/skopeo/proxy.go b/cmd/skopeo/proxy.go index dad201dd..e65e47b3 100644 --- a/cmd/skopeo/proxy.go +++ b/cmd/skopeo/proxy.go @@ -430,7 +430,45 @@ func (h *proxyHandler) GetManifest(args []interface{}) (replyBuf, error) { return h.returnBytes(digest, serialized) } -// GetConfig returns a copy of the image configuration, converted to OCI format. +// GetFullConfig returns a copy of the image configuration, converted to OCI format. +// https://github.com/opencontainers/image-spec/blob/main/config.md +func (h *proxyHandler) GetFullConfig(args []interface{}) (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("invalid request, expecting: [imgid]") + } + imgref, err := h.parseImageFromID(args[0]) + if err != nil { + return ret, err + } + err = h.cacheTargetManifest(imgref) + if err != nil { + return ret, err + } + img := imgref.cachedimg + + ctx := context.TODO() + config, err := img.OCIConfig(ctx) + if err != nil { + return ret, err + } + serialized, err := json.Marshal(&config) + if err != nil { + return ret, err + } + return h.returnBytes(nil, serialized) +} + +// GetConfig returns a copy of the container runtime configuration, converted to OCI format. +// Note that due to a historical mistake, this returns not the full image configuration, +// but just the container runtime configuration. You should use GetFullConfig instead. func (h *proxyHandler) GetConfig(args []interface{}) (replyBuf, error) { h.lock.Lock() defer h.lock.Unlock() @@ -463,7 +501,6 @@ func (h *proxyHandler) GetConfig(args []interface{}) (replyBuf, error) { return ret, err } return h.returnBytes(nil, serialized) - } // GetBlob fetches a blob, performing digest verification. @@ -639,6 +676,8 @@ func (h *proxyHandler) processRequest(req request) (rb replyBuf, terminate bool, rb, err = h.GetManifest(req.Args) case "GetConfig": rb, err = h.GetConfig(req.Args) + case "GetFullConfig": + rb, err = h.GetFullConfig(req.Args) case "GetBlob": rb, err = h.GetBlob(req.Args) case "FinishPipe": diff --git a/integration/proxy_test.go b/integration/proxy_test.go index b456289d..f510675c 100644 --- a/integration/proxy_test.go +++ b/integration/proxy_test.go @@ -250,18 +250,37 @@ func runTestGetManifestAndConfig(p *proxy, img string) error { return err } - v, configBytes, err := p.callReadAllBytes("GetConfig", []interface{}{imgid}) + v, configBytes, err := p.callReadAllBytes("GetFullConfig", []interface{}{imgid}) if err != nil { return err } - var config imgspecv1.ImageConfig + var config imgspecv1.Image err = json.Unmarshal(configBytes, &config) if err != nil { return err } + // Validate that the image config seems sane + if config.Architecture == "" { + return fmt.Errorf("No architecture found") + } + if len(config.Config.Cmd) == 0 && len(config.Config.Entrypoint) == 0 { + return fmt.Errorf("No CMD or ENTRYPOINT set") + } + + // Also test this legacy interface + v, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []interface{}{imgid}) + if err != nil { + return err + } + var ctrconfig imgspecv1.ImageConfig + err = json.Unmarshal(ctrconfigBytes, &ctrconfig) + if err != nil { + return err + } + // Validate that the config seems sane - if len(config.Cmd) == 0 && len(config.Entrypoint) == 0 { + if len(ctrconfig.Cmd) == 0 && len(ctrconfig.Entrypoint) == 0 { return fmt.Errorf("No CMD or ENTRYPOINT set") } From e7dc5e79f2c7e0c6bf8df56985481f28879956be Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 6 Dec 2021 20:59:17 -0500 Subject: [PATCH 2/2] proxy: Also bump compatible semver To denote we have new API. --- cmd/skopeo/proxy.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/skopeo/proxy.go b/cmd/skopeo/proxy.go index e65e47b3..883b1d20 100644 --- a/cmd/skopeo/proxy.go +++ b/cmd/skopeo/proxy.go @@ -87,7 +87,8 @@ import ( // // 0.2.1: Initial version // 0.2.2: Added support for fetching image configuration as OCI -const protocolVersion = "0.2.2" +// 0.2.3: Added GetFullConfig +const protocolVersion = "0.2.3" // maxMsgSize is the current limit on a packet size. // Note that all non-metadata (i.e. payload data) is sent over a pipe.