mirror of
https://github.com/regclient/regclient.git
synced 2025-07-30 20:03:04 +03:00
Adjusting types to use New/Opts initializers
Signed-off-by: Brandon Mitchell <git@bmitch.net>
This commit is contained in:
@ -168,8 +168,13 @@ func TestBlobGet(t *testing.T) {
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.WarnLevel,
|
||||
}
|
||||
rc := New(WithConfigHosts(rcHosts), WithLog(log))
|
||||
|
||||
delayInit, _ := time.ParseDuration("0.05s")
|
||||
delayMax, _ := time.ParseDuration("0.10s")
|
||||
rc := New(
|
||||
WithConfigHosts(rcHosts),
|
||||
WithLog(log),
|
||||
WithRetryDelay(delayInit, delayMax),
|
||||
)
|
||||
// Test successful blob
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
ref, err := ref.New(tsURL.Host + blobRepo)
|
||||
|
@ -123,7 +123,10 @@ func (s *Sandbox) configExport(ls *lua.LState) int {
|
||||
ls.RaiseError("Failed exporting config (go2lua): %v", err)
|
||||
}
|
||||
// save image to a new config
|
||||
bc := blob.NewOCIConfig(ociImage)
|
||||
bc := blob.NewOCIConfig(
|
||||
blob.WithRef(origC.r),
|
||||
blob.WithImage(ociImage),
|
||||
)
|
||||
newC = &config{
|
||||
conf: bc,
|
||||
m: origC.m,
|
||||
|
@ -145,7 +145,7 @@ func (s *Sandbox) manifestExport(ls *lua.LState) int {
|
||||
ls.RaiseError("Failed exporting manifest (go2lua): %v", err)
|
||||
}
|
||||
// save image to a new manifest
|
||||
rcM, err := manifest.FromOrig(reflect.ValueOf(newMMP).Elem().Interface()) // reflect is needed again to deref the pointer now
|
||||
rcM, err := manifest.New(manifest.WithOrig(reflect.ValueOf(newMMP).Elem().Interface())) // reflect is needed again to deref the pointer now
|
||||
// rcM, err := manifest.FromOrig(newMM)
|
||||
if err != nil {
|
||||
ls.RaiseError("Failed exporting manifest (from orig): %v", err)
|
||||
@ -238,7 +238,7 @@ func (s *Sandbox) manifestPut(ls *lua.LState) int {
|
||||
"image": r.r.CommonName(),
|
||||
}).Debug("Put manifest")
|
||||
|
||||
m, err := manifest.FromOrig(sbm.m.GetOrigManifest())
|
||||
m, err := manifest.New(manifest.WithOrig(sbm.m.GetOrigManifest()))
|
||||
if err != nil {
|
||||
ls.RaiseError("Failed to put manifest: %v", err)
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ func runArtifactPut(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// generate manifest
|
||||
mm, err := manifest.FromOrig(m)
|
||||
mm, err := manifest.New(manifest.WithOrig(m))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func getPlatformDesc(rc *regclient.RegClient, m manifest.Manifest) (*ociv1.Descr
|
||||
if !m.IsSet() {
|
||||
m, err = rc.ManifestGet(context.Background(), m.GetRef())
|
||||
if err != nil {
|
||||
return desc, err
|
||||
return desc, fmt.Errorf("unable to retrieve manifest list: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,6 +239,9 @@ func runManifestDigest(cmd *cobra.Command, args []string) error {
|
||||
// retrieve the specified platform from the manifest list
|
||||
for m.IsList() && !manifestOpts.list && !manifestOpts.requireList {
|
||||
desc, err := getPlatformDesc(rc, m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed retrieving platform specific digest: %w", err)
|
||||
}
|
||||
r.Digest = desc.Digest.String()
|
||||
m, err = rc.ManifestHead(context.Background(), r)
|
||||
if err != nil {
|
||||
@ -283,7 +286,13 @@ func runManifestPut(cmd *cobra.Command, args []string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rcM, err := manifest.New(manifestOpts.contentType, raw, r, nil)
|
||||
rcM, err := manifest.New(
|
||||
manifest.WithRef(r),
|
||||
manifest.WithRaw(raw),
|
||||
manifest.WithDesc(ociv1.Descriptor{
|
||||
MediaType: manifestOpts.contentType,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
14
image.go
14
image.go
@ -196,7 +196,7 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re
|
||||
cd, err := m.GetConfigDigest()
|
||||
if err != nil {
|
||||
// docker schema v1 does not have a config object, ignore if it's missing
|
||||
if !errors.Is(err, manifest.ErrUnsupportedMediaType) {
|
||||
if !errors.Is(err, types.ErrUnsupportedMediaType) {
|
||||
rc.log.WithFields(logrus.Fields{
|
||||
"ref": refSrc.Reference,
|
||||
"err": err,
|
||||
@ -449,7 +449,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, ref ref.Ref, des
|
||||
// add config
|
||||
confD, err := m.GetConfigDescriptor()
|
||||
// ignore unsupported media type errors
|
||||
if err != nil && !errors.Is(err, manifest.ErrUnsupportedMediaType) {
|
||||
if err != nil && !errors.Is(err, types.ErrUnsupportedMediaType) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
@ -462,7 +462,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, ref ref.Ref, des
|
||||
// loop over layers
|
||||
layerDL, err := m.GetLayers()
|
||||
// ignore unsupported media type errors
|
||||
if err != nil && !errors.Is(err, manifest.ErrUnsupportedMediaType) {
|
||||
if err != nil && !errors.Is(err, types.ErrUnsupportedMediaType) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
@ -558,7 +558,7 @@ func (rc *RegClient) ImageImport(ctx context.Context, ref ref.Ref, rs io.ReadSee
|
||||
return fmt.Errorf("Failed to import layers from docker tar: %w", err)
|
||||
}
|
||||
// push docker manifest
|
||||
m, err := manifest.FromOrig(trd.dockerManifest)
|
||||
m, err := manifest.New(manifest.WithOrig(trd.dockerManifest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -677,7 +677,7 @@ func (rc *RegClient) imageImportOCIAddHandler(ctx context.Context, ref ref.Ref,
|
||||
// no need to process docker manifest.json when OCI layout is available
|
||||
delete(trd.handlers, dockerManifestFilename)
|
||||
// create a manifest from the index
|
||||
trd.ociManifest, err = manifest.FromOrig(trd.ociIndex)
|
||||
trd.ociManifest, err = manifest.New(manifest.WithOrig(trd.ociIndex))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -745,7 +745,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, ref ref.R
|
||||
types.MediaTypeDocker2Manifest, types.MediaTypeDocker2ManifestList,
|
||||
types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList:
|
||||
// known manifest media types
|
||||
md, err := manifest.FromDescriptor(d, b)
|
||||
md, err := manifest.New(manifest.WithDesc(d), manifest.WithRaw(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -757,7 +757,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, ref ref.R
|
||||
return rc.imageImportBlob(ctx, ref, d, trd)
|
||||
default:
|
||||
// attempt manifest import, fall back to blob import
|
||||
md, err := manifest.FromDescriptor(d, b)
|
||||
md, err := manifest.New(manifest.WithDesc(d), manifest.WithRaw(b))
|
||||
if err == nil {
|
||||
return rc.imageImportOCIHandleManifest(ctx, ref, md, trd, true)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
dockerSchema2 "github.com/docker/distribution/manifest/schema2"
|
||||
@ -142,7 +143,13 @@ func TestManifest(t *testing.T) {
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: logrus.WarnLevel,
|
||||
}
|
||||
rc := New(WithConfigHosts(rcHosts), WithLog(log))
|
||||
delayInit, _ := time.ParseDuration("0.05s")
|
||||
delayMax, _ := time.ParseDuration("0.10s")
|
||||
rc := New(
|
||||
WithConfigHosts(rcHosts),
|
||||
WithLog(log),
|
||||
WithRetryDelay(delayInit, delayMax),
|
||||
)
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
getRef, err := ref.New(tsURL.Host + repoPath + ":" + getTag)
|
||||
if err != nil {
|
||||
|
@ -6,8 +6,12 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
topTypes "github.com/regclient/regclient/types"
|
||||
topManifest "github.com/regclient/regclient/types/manifest"
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -28,12 +32,30 @@ const (
|
||||
type Manifest = topManifest.Manifest
|
||||
|
||||
var (
|
||||
New = topManifest.New
|
||||
FromDescriptor = topManifest.FromDescriptor
|
||||
FromOrig = topManifest.FromOrig
|
||||
|
||||
ErrNotFound = topTypes.ErrNotFound
|
||||
ErrNotImplemented = topTypes.ErrNotImplemented
|
||||
ErrUnavailable = topTypes.ErrUnavailable
|
||||
ErrUnsupportedMediaType = topTypes.ErrUnsupported
|
||||
)
|
||||
|
||||
func New(mediaType string, raw []byte, r ref.Ref, header http.Header) (Manifest, error) {
|
||||
return topManifest.New(
|
||||
topManifest.WithDesc(ociv1.Descriptor{
|
||||
MediaType: mediaType,
|
||||
}),
|
||||
topManifest.WithRef(r),
|
||||
topManifest.WithRaw(raw),
|
||||
topManifest.WithHeader(header),
|
||||
)
|
||||
}
|
||||
|
||||
func FromDescriptor(desc ociv1.Descriptor, mBytes []byte) (Manifest, error) {
|
||||
return topManifest.New(
|
||||
topManifest.WithDesc(desc),
|
||||
topManifest.WithRaw(mBytes),
|
||||
)
|
||||
}
|
||||
|
||||
func FromOrig(orig interface{}) (Manifest, error) {
|
||||
return topManifest.New(topManifest.WithOrig(orig))
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/internal/reghttp"
|
||||
"github.com/regclient/regclient/types"
|
||||
"github.com/regclient/regclient/types/blob"
|
||||
@ -62,9 +63,14 @@ func (reg *Reg) BlobGet(ctx context.Context, r ref.Ref, d digest.Digest) (blob.R
|
||||
return nil, fmt.Errorf("Failed to get blob, digest %s, ref %s: %w", d, r.CommonName(), reghttp.HttpError(resp.HTTPResponse().StatusCode))
|
||||
}
|
||||
|
||||
b := blob.NewReader(resp)
|
||||
b.SetMeta(r, d, 0)
|
||||
b.SetResp(resp.HTTPResponse())
|
||||
b := blob.NewReader(
|
||||
blob.WithRef(r),
|
||||
blob.WithReadCloser(resp),
|
||||
blob.WithDesc(ociv1.Descriptor{
|
||||
Digest: d,
|
||||
}),
|
||||
blob.WithResp(resp.HTTPResponse()),
|
||||
)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
@ -90,9 +96,13 @@ func (reg *Reg) BlobHead(ctx context.Context, r ref.Ref, d digest.Digest) (blob.
|
||||
return nil, fmt.Errorf("Failed to request blob head, digest %s, ref %s: %w", d, r.CommonName(), reghttp.HttpError(resp.HTTPResponse().StatusCode))
|
||||
}
|
||||
|
||||
b := blob.NewReader(nil)
|
||||
b.SetMeta(r, d, 0)
|
||||
b.SetResp(resp.HTTPResponse())
|
||||
b := blob.NewReader(
|
||||
blob.WithRef(r),
|
||||
blob.WithDesc(ociv1.Descriptor{
|
||||
Digest: d,
|
||||
}),
|
||||
blob.WithResp(resp.HTTPResponse()),
|
||||
)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
@ -93,10 +93,11 @@ func (reg *Reg) ManifestGet(ctx context.Context, r ref.Ref) (manifest.Manifest,
|
||||
return nil, fmt.Errorf("Error reading manifest for %s: %w", r.CommonName(), err)
|
||||
}
|
||||
|
||||
// parse body into variable according to media type
|
||||
mt := resp.HTTPResponse().Header.Get("Content-Type")
|
||||
|
||||
return manifest.New(mt, rawBody, r, resp.HTTPResponse().Header)
|
||||
return manifest.New(
|
||||
manifest.WithRef(r),
|
||||
manifest.WithHeader(resp.HTTPResponse().Header),
|
||||
manifest.WithRaw(rawBody),
|
||||
)
|
||||
}
|
||||
|
||||
// ManifestHead returns metadata on the manifest from the registry
|
||||
@ -142,10 +143,10 @@ func (reg *Reg) ManifestHead(ctx context.Context, r ref.Ref) (manifest.Manifest,
|
||||
return nil, fmt.Errorf("Failed to request manifest head %s: %w", r.CommonName(), reghttp.HttpError(resp.HTTPResponse().StatusCode))
|
||||
}
|
||||
|
||||
// extract header data
|
||||
mt := resp.HTTPResponse().Header.Get("Content-Type")
|
||||
|
||||
return manifest.New(mt, []byte{}, r, resp.HTTPResponse().Header)
|
||||
return manifest.New(
|
||||
manifest.WithRef(r),
|
||||
manifest.WithHeader(resp.HTTPResponse().Header),
|
||||
)
|
||||
}
|
||||
|
||||
// ManifestPut uploads a manifest to a registry
|
||||
|
@ -106,7 +106,7 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error {
|
||||
// create manifest with config, matching the original tag manifest type
|
||||
switch curManifest.GetMediaType() {
|
||||
case types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList:
|
||||
tempManifest, err = manifest.FromOrig(ociv1.Manifest{
|
||||
tempManifest, err = manifest.New(manifest.WithOrig(ociv1.Manifest{
|
||||
Versioned: ociv1Specs.Versioned{
|
||||
SchemaVersion: 2,
|
||||
},
|
||||
@ -117,12 +117,12 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error {
|
||||
Size: int64(len(confB)),
|
||||
},
|
||||
Layers: []ociv1.Descriptor{},
|
||||
})
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default: // default to the docker v2 schema
|
||||
tempManifest, err = manifest.FromOrig(dockerSchema2.Manifest{
|
||||
tempManifest, err = manifest.New(manifest.WithOrig(dockerSchema2.Manifest{
|
||||
Versioned: dockerManifest.Versioned{
|
||||
SchemaVersion: 2,
|
||||
MediaType: types.MediaTypeDocker2Manifest,
|
||||
@ -133,7 +133,7 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error {
|
||||
Size: int64(len(confB)),
|
||||
},
|
||||
Layers: []dockerDistribution.Descriptor{},
|
||||
})
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,7 +1,61 @@
|
||||
package blob
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
// Blob interface is used for returning blobs
|
||||
type Blob interface {
|
||||
Common
|
||||
RawBody() ([]byte, error)
|
||||
}
|
||||
|
||||
type BlobConfig struct {
|
||||
desc ociv1.Descriptor
|
||||
header http.Header
|
||||
image ociv1.Image
|
||||
r ref.Ref
|
||||
rc io.ReadCloser
|
||||
resp *http.Response
|
||||
}
|
||||
|
||||
type Opts func(*BlobConfig)
|
||||
|
||||
func WithDesc(d ociv1.Descriptor) Opts {
|
||||
return func(bc *BlobConfig) {
|
||||
bc.desc = d
|
||||
}
|
||||
}
|
||||
func WithHeader(header http.Header) Opts {
|
||||
return func(bc *BlobConfig) {
|
||||
bc.header = header
|
||||
}
|
||||
|
||||
}
|
||||
func WithImage(image ociv1.Image) Opts {
|
||||
return func(bc *BlobConfig) {
|
||||
bc.image = image
|
||||
}
|
||||
}
|
||||
func WithReadCloser(rc io.ReadCloser) Opts {
|
||||
return func(bc *BlobConfig) {
|
||||
bc.rc = rc
|
||||
}
|
||||
}
|
||||
func WithRef(r ref.Ref) Opts {
|
||||
return func(bc *BlobConfig) {
|
||||
bc.r = r
|
||||
}
|
||||
}
|
||||
func WithResp(resp *http.Response) Opts {
|
||||
return func(bc *BlobConfig) {
|
||||
bc.resp = resp
|
||||
if bc.header == nil {
|
||||
bc.header = resp.Header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ package blob
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
@ -15,15 +15,11 @@ type Common interface {
|
||||
MediaType() string
|
||||
Response() *http.Response
|
||||
RawHeaders() http.Header
|
||||
SetMeta(r ref.Ref, d digest.Digest, cl int64)
|
||||
SetResp(resp *http.Response)
|
||||
}
|
||||
|
||||
type common struct {
|
||||
r ref.Ref
|
||||
digest digest.Digest
|
||||
cl int64
|
||||
mt string
|
||||
desc ociv1.Descriptor
|
||||
blobSet bool
|
||||
rawHeader http.Header
|
||||
resp *http.Response
|
||||
@ -31,17 +27,17 @@ type common struct {
|
||||
|
||||
// Digest returns the provided or calculated digest of the blob
|
||||
func (b *common) Digest() digest.Digest {
|
||||
return b.digest
|
||||
return b.desc.Digest
|
||||
}
|
||||
|
||||
// Length returns the provided or calculated length of the blob
|
||||
func (b *common) Length() int64 {
|
||||
return b.cl
|
||||
return b.desc.Size
|
||||
}
|
||||
|
||||
// MediaType returns the Content-Type header received from the registry
|
||||
func (b *common) MediaType() string {
|
||||
return b.mt
|
||||
return b.desc.MediaType
|
||||
}
|
||||
|
||||
// RawHeaders returns the headers received from the registry
|
||||
@ -53,22 +49,3 @@ func (b *common) RawHeaders() http.Header {
|
||||
func (b *common) Response() *http.Response {
|
||||
return b.resp
|
||||
}
|
||||
|
||||
// SetMeta sets the various blob metadata (reference, digest, and content length)
|
||||
func (b *common) SetMeta(ref ref.Ref, d digest.Digest, cl int64) {
|
||||
b.r = ref
|
||||
b.digest = d
|
||||
b.cl = cl
|
||||
}
|
||||
|
||||
// SetResp sets the response header data when pulling from a registry
|
||||
func (b *common) SetResp(resp *http.Response) {
|
||||
if resp == nil {
|
||||
return
|
||||
}
|
||||
b.resp = resp
|
||||
b.rawHeader = resp.Header
|
||||
cl, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
|
||||
b.cl = int64(cl)
|
||||
b.mt = resp.Header.Get("Content-Type")
|
||||
}
|
||||
|
@ -22,13 +22,21 @@ type ociConfig struct {
|
||||
}
|
||||
|
||||
// NewOCIConfig creates a new BlobOCIConfig from an OCI Image
|
||||
func NewOCIConfig(ociImage ociv1.Image) OCIConfig {
|
||||
bc := common{
|
||||
func NewOCIConfig(opts ...Opts) OCIConfig {
|
||||
bc := BlobConfig{}
|
||||
for _, opt := range opts {
|
||||
opt(&bc)
|
||||
}
|
||||
c := common{
|
||||
blobSet: true,
|
||||
desc: bc.desc,
|
||||
r: bc.r,
|
||||
rawHeader: bc.header,
|
||||
resp: bc.resp,
|
||||
}
|
||||
b := ociConfig{
|
||||
common: bc,
|
||||
Image: ociImage,
|
||||
common: c,
|
||||
Image: bc.image,
|
||||
}
|
||||
return &b
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
// Reader is an unprocessed Blob with an available ReadCloser for reading the Blob
|
||||
@ -25,24 +25,41 @@ type reader struct {
|
||||
reader io.Reader
|
||||
origRdr io.ReadCloser
|
||||
digester digest.Digester
|
||||
// io.ReadCloser
|
||||
}
|
||||
|
||||
// NewReader creates a new reader
|
||||
func NewReader(rdr io.ReadCloser) Reader {
|
||||
digester := digest.Canonical.Digester()
|
||||
digestRdr := io.TeeReader(rdr, digester.Hash())
|
||||
bc := common{
|
||||
r: ref.Ref{},
|
||||
func NewReader(opts ...Opts) Reader {
|
||||
bc := BlobConfig{}
|
||||
for _, opt := range opts {
|
||||
opt(&bc)
|
||||
}
|
||||
if rdr != nil {
|
||||
bc.blobSet = true
|
||||
if bc.resp != nil {
|
||||
// extract headers and reader if other fields not passed
|
||||
if bc.desc.MediaType == "" {
|
||||
bc.desc.MediaType = bc.resp.Header.Get("Content-Type")
|
||||
}
|
||||
if bc.desc.Size == 0 {
|
||||
cl, _ := strconv.Atoi(bc.resp.Header.Get("Content-Length"))
|
||||
bc.desc.Size = int64(cl)
|
||||
}
|
||||
if bc.desc.Digest == "" {
|
||||
bc.desc.Digest = digest.FromString(bc.resp.Header.Get("Docker-Content-Digest"))
|
||||
}
|
||||
}
|
||||
c := common{
|
||||
r: bc.r,
|
||||
desc: bc.desc,
|
||||
rawHeader: bc.header,
|
||||
resp: bc.resp,
|
||||
}
|
||||
br := reader{
|
||||
common: bc,
|
||||
reader: digestRdr,
|
||||
origRdr: rdr,
|
||||
digester: digester,
|
||||
common: c,
|
||||
origRdr: bc.rc,
|
||||
}
|
||||
if bc.rc != nil {
|
||||
br.blobSet = true
|
||||
br.digester = digest.Canonical.Digester()
|
||||
br.reader = io.TeeReader(bc.rc, br.digester.Hash())
|
||||
}
|
||||
return &br
|
||||
}
|
||||
@ -65,16 +82,16 @@ func (b *reader) Read(p []byte) (int, error) {
|
||||
b.readBytes = b.readBytes + int64(size)
|
||||
if err == io.EOF {
|
||||
// check/save size
|
||||
if b.cl == 0 {
|
||||
b.cl = b.readBytes
|
||||
} else if b.readBytes != b.cl {
|
||||
err = fmt.Errorf("Expected size mismatch [expected %d, received %d]: %w", b.cl, b.readBytes, err)
|
||||
if b.desc.Size == 0 {
|
||||
b.desc.Size = b.readBytes
|
||||
} else if b.readBytes != b.desc.Size {
|
||||
err = fmt.Errorf("Expected size mismatch [expected %d, received %d]: %w", b.desc.Size, b.readBytes, err)
|
||||
}
|
||||
// check/save digest
|
||||
if b.digest == "" {
|
||||
b.digest = b.digester.Digest()
|
||||
} else if b.digest != b.digester.Digest() {
|
||||
err = fmt.Errorf("Expected digest mismatch [expected %s, calculated %s]: %w", b.digest.String(), b.digester.Digest().String(), err)
|
||||
if b.desc.Digest == "" {
|
||||
b.desc.Digest = b.digester.Digest()
|
||||
} else if b.desc.Digest != b.digester.Digest() {
|
||||
err = fmt.Errorf("Expected digest mismatch [expected %s, calculated %s]: %w", b.desc.Digest.String(), b.digester.Digest().String(), err)
|
||||
}
|
||||
}
|
||||
return size, err
|
||||
|
@ -6,14 +6,14 @@ import (
|
||||
"strings"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/types"
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
type common struct {
|
||||
r ref.Ref
|
||||
digest digest.Digest
|
||||
mt string
|
||||
desc ociv1.Descriptor
|
||||
manifSet bool
|
||||
ratelimit types.RateLimit
|
||||
rawHeader http.Header
|
||||
@ -22,12 +22,12 @@ type common struct {
|
||||
|
||||
// GetDigest returns the digest
|
||||
func (m *common) GetDigest() digest.Digest {
|
||||
return m.digest
|
||||
return m.desc.Digest
|
||||
}
|
||||
|
||||
// GetMediaType returns the media type
|
||||
func (m *common) GetMediaType() string {
|
||||
return m.mt
|
||||
return m.desc.MediaType
|
||||
}
|
||||
|
||||
// GetRateLimit returns the rate limit when the manifest was pulled from a registry.
|
||||
@ -48,7 +48,7 @@ func (m *common) HasRateLimit() bool {
|
||||
|
||||
// IsList indicates if the manifest is a docker Manifest List or OCI Index
|
||||
func (m *common) IsList() bool {
|
||||
switch m.mt {
|
||||
switch m.desc.MediaType {
|
||||
case MediaTypeDocker2ManifestList, MediaTypeOCI1ManifestList:
|
||||
return true
|
||||
default:
|
||||
@ -65,7 +65,7 @@ func (m *common) IsSet() bool {
|
||||
// RawBody returns the raw body from the manifest if available.
|
||||
func (m *common) RawBody() ([]byte, error) {
|
||||
if len(m.rawBody) == 0 {
|
||||
return m.rawBody, ErrUnavailable
|
||||
return m.rawBody, types.ErrUnavailable
|
||||
}
|
||||
return m.rawBody, nil
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/internal/wraperr"
|
||||
"github.com/regclient/regclient/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -28,23 +29,23 @@ type docker1SignedManifest struct {
|
||||
}
|
||||
|
||||
func (m *docker1Manifest) GetConfigDescriptor() (ociv1.Descriptor, error) {
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker1Manifest) GetConfigDigest() (digest.Digest, error) {
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker1SignedManifest) GetConfigDescriptor() (ociv1.Descriptor, error) {
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker1SignedManifest) GetConfigDigest() (digest.Digest, error) {
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *docker1Manifest) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker1SignedManifest) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *docker1Manifest) GetLayers() ([]ociv1.Descriptor, error) {
|
||||
@ -74,22 +75,22 @@ func (m *docker1SignedManifest) GetOrigManifest() interface{} {
|
||||
}
|
||||
|
||||
func (m *docker1Manifest) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker1SignedManifest) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *docker1Manifest) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker1SignedManifest) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *docker1Manifest) MarshalJSON() ([]byte, error) {
|
||||
if !m.manifSet {
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), ErrUnavailable)
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), types.ErrUnavailable)
|
||||
}
|
||||
|
||||
if len(m.rawBody) > 0 {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/internal/wraperr"
|
||||
"github.com/regclient/regclient/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -45,14 +46,14 @@ func (m *docker2Manifest) GetConfigDigest() (digest.Digest, error) {
|
||||
return m.Config.Digest, nil
|
||||
}
|
||||
func (m *docker2ManifestList) GetConfigDescriptor() (ociv1.Descriptor, error) {
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker2ManifestList) GetConfigDigest() (digest.Digest, error) {
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *docker2Manifest) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker2ManifestList) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
dl := []ociv1.Descriptor{}
|
||||
@ -70,7 +71,7 @@ func (m *docker2Manifest) GetLayers() ([]ociv1.Descriptor, error) {
|
||||
return dl, nil
|
||||
}
|
||||
func (m *docker2ManifestList) GetLayers() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Layers are not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Layers are not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *docker2Manifest) GetOrigManifest() interface{} {
|
||||
@ -81,7 +82,7 @@ func (m *docker2ManifestList) GetOrigManifest() interface{} {
|
||||
}
|
||||
|
||||
func (m *docker2Manifest) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker2ManifestList) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
dl, err := m.GetDescriptorList()
|
||||
@ -92,7 +93,7 @@ func (m *docker2ManifestList) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descrip
|
||||
}
|
||||
|
||||
func (m *docker2Manifest) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *docker2ManifestList) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
dl, err := m.GetDescriptorList()
|
||||
@ -104,7 +105,7 @@ func (m *docker2ManifestList) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
|
||||
func (m *docker2Manifest) MarshalJSON() ([]byte, error) {
|
||||
if !m.manifSet {
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), ErrUnavailable)
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), types.ErrUnavailable)
|
||||
}
|
||||
|
||||
if len(m.rawBody) > 0 {
|
||||
@ -115,7 +116,7 @@ func (m *docker2Manifest) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
func (m *docker2ManifestList) MarshalJSON() ([]byte, error) {
|
||||
if !m.manifSet {
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), ErrUnavailable)
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), types.ErrUnavailable)
|
||||
}
|
||||
|
||||
if len(m.rawBody) > 0 {
|
||||
@ -142,8 +143,8 @@ func (m *docker2ManifestList) MarshalPretty() ([]byte, error) {
|
||||
if m.r.Reference != "" {
|
||||
fmt.Fprintf(tw, "Name:\t%s\n", m.r.Reference)
|
||||
}
|
||||
fmt.Fprintf(tw, "MediaType:\t%s\n", m.mt)
|
||||
fmt.Fprintf(tw, "Digest:\t%s\n", m.digest.String())
|
||||
fmt.Fprintf(tw, "MediaType:\t%s\n", m.desc.MediaType)
|
||||
fmt.Fprintf(tw, "Digest:\t%s\n", m.desc.Digest.String())
|
||||
fmt.Fprintf(tw, "\t\n")
|
||||
fmt.Fprintf(tw, "Manifests:\t\n")
|
||||
for _, d := range m.Manifests {
|
||||
|
@ -1,14 +0,0 @@
|
||||
package manifest
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrNotFound isn't there, search for your value elsewhere
|
||||
ErrNotFound = errors.New("Not found")
|
||||
// ErrNotImplemented returned when method has not been implemented yet
|
||||
ErrNotImplemented = errors.New("Not implemented")
|
||||
// ErrUnavailable when a requested value is not available
|
||||
ErrUnavailable = errors.New("Unavailable")
|
||||
// ErrUnsupportedMediaType returned when media type is unknown or unsupported
|
||||
ErrUnsupportedMediaType = errors.New("Unsupported media type")
|
||||
)
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
dockerDistribution "github.com/docker/distribution"
|
||||
@ -38,42 +39,75 @@ type Manifest interface {
|
||||
RawHeaders() (http.Header, error)
|
||||
}
|
||||
|
||||
// New creates a new manifest from an unparsed raw manifest
|
||||
// mediaType: should be a known media-type. If empty, resp headers will be checked
|
||||
// raw: body of the manifest. If empty, unset manifest for a HEAD request is returned
|
||||
// ref: reference, may be unset
|
||||
// header: headers from request, used to extract content type, digest, and rate limits
|
||||
func New(mediaType string, raw []byte, r ref.Ref, header http.Header) (Manifest, error) {
|
||||
mc := common{
|
||||
r: r,
|
||||
mt: mediaType,
|
||||
rawBody: raw,
|
||||
}
|
||||
if header != nil {
|
||||
mc.rawHeader = header
|
||||
if mc.mt == "" {
|
||||
mc.mt = header.Get("Content-Type")
|
||||
}
|
||||
mc.digest, _ = digest.Parse(header.Get("Docker-Content-Digest"))
|
||||
mc.setRateLimit(header)
|
||||
}
|
||||
return fromCommon(mc)
|
||||
type ManifestConfig struct {
|
||||
r ref.Ref
|
||||
desc ociv1.Descriptor
|
||||
raw []byte
|
||||
orig interface{}
|
||||
header http.Header
|
||||
}
|
||||
type Opts func(*ManifestConfig)
|
||||
|
||||
// FromDescriptor creates a new manifest from a descriptor and the raw manifest bytes.
|
||||
func FromDescriptor(desc ociv1.Descriptor, mBytes []byte) (Manifest, error) {
|
||||
mc := common{
|
||||
digest: desc.Digest,
|
||||
mt: desc.MediaType,
|
||||
manifSet: true,
|
||||
rawBody: mBytes,
|
||||
// New creates a new manifest based on provided options
|
||||
func New(opts ...Opts) (Manifest, error) {
|
||||
mc := ManifestConfig{}
|
||||
for _, opt := range opts {
|
||||
opt(&mc)
|
||||
}
|
||||
c := common{
|
||||
r: mc.r,
|
||||
desc: mc.desc,
|
||||
rawBody: mc.raw,
|
||||
rawHeader: mc.header,
|
||||
}
|
||||
// extract fields from header where available
|
||||
if mc.header != nil {
|
||||
if c.desc.MediaType == "" {
|
||||
c.desc.MediaType = mc.header.Get("Content-Type")
|
||||
}
|
||||
if mc.desc.Size == 0 {
|
||||
cl, _ := strconv.Atoi(mc.header.Get("Content-Length"))
|
||||
mc.desc.Size = int64(cl)
|
||||
}
|
||||
if c.desc.Digest == "" {
|
||||
c.desc.Digest, _ = digest.Parse(mc.header.Get("Docker-Content-Digest"))
|
||||
}
|
||||
c.setRateLimit(mc.header)
|
||||
}
|
||||
if mc.orig != nil {
|
||||
return fromOrig(c, mc.orig)
|
||||
}
|
||||
return fromCommon(c)
|
||||
}
|
||||
func WithDesc(desc ociv1.Descriptor) Opts {
|
||||
return func(mc *ManifestConfig) {
|
||||
mc.desc = desc
|
||||
}
|
||||
}
|
||||
func WithHeader(header http.Header) Opts {
|
||||
return func(mc *ManifestConfig) {
|
||||
mc.header = header
|
||||
}
|
||||
}
|
||||
func WithOrig(orig interface{}) Opts {
|
||||
return func(mc *ManifestConfig) {
|
||||
mc.orig = orig
|
||||
}
|
||||
}
|
||||
func WithRaw(raw []byte) Opts {
|
||||
return func(mc *ManifestConfig) {
|
||||
mc.raw = raw
|
||||
}
|
||||
}
|
||||
func WithRef(r ref.Ref) Opts {
|
||||
return func(mc *ManifestConfig) {
|
||||
mc.r = r
|
||||
}
|
||||
return fromCommon(mc)
|
||||
}
|
||||
|
||||
// FromOrig creates a new manifest from the original upstream manifest type.
|
||||
// This method should be used if you are creating a new manifest rather than pulling one from a registry.
|
||||
func FromOrig(orig interface{}) (Manifest, error) {
|
||||
func fromOrig(c common, orig interface{}) (Manifest, error) {
|
||||
var mt string
|
||||
var m Manifest
|
||||
|
||||
@ -81,156 +115,154 @@ func FromOrig(orig interface{}) (Manifest, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mc := common{
|
||||
digest: digest.FromBytes(mj),
|
||||
rawBody: mj,
|
||||
manifSet: true,
|
||||
c.manifSet = true
|
||||
if len(c.rawBody) == 0 {
|
||||
c.rawBody = mj
|
||||
}
|
||||
if _, ok := orig.(dockerSchema1.SignedManifest); !ok && c.desc.Digest == "" {
|
||||
c.desc.Digest = digest.FromBytes(mj)
|
||||
}
|
||||
if c.desc.Size == 0 {
|
||||
c.desc.Size = int64(len(mj))
|
||||
}
|
||||
// create manifest based on type
|
||||
switch orig.(type) {
|
||||
case dockerSchema1.Manifest:
|
||||
mOrig := orig.(dockerSchema1.Manifest)
|
||||
mt = mOrig.MediaType
|
||||
mc.mt = MediaTypeDocker1Manifest
|
||||
c.desc.MediaType = types.MediaTypeDocker1Manifest
|
||||
m = &docker1Manifest{
|
||||
common: mc,
|
||||
common: c,
|
||||
Manifest: mOrig,
|
||||
}
|
||||
case dockerSchema1.SignedManifest:
|
||||
mOrig := orig.(dockerSchema1.SignedManifest)
|
||||
mt = mOrig.MediaType
|
||||
c.desc.MediaType = types.MediaTypeDocker1ManifestSigned
|
||||
// recompute digest on the canonical data
|
||||
mc.digest = digest.FromBytes(mOrig.Canonical)
|
||||
mc.mt = MediaTypeDocker1ManifestSigned
|
||||
if c.desc.Digest == "" {
|
||||
c.desc.Digest = digest.FromBytes(mOrig.Canonical)
|
||||
}
|
||||
m = &docker1SignedManifest{
|
||||
common: mc,
|
||||
common: c,
|
||||
SignedManifest: mOrig,
|
||||
}
|
||||
case dockerSchema2.Manifest:
|
||||
mOrig := orig.(dockerSchema2.Manifest)
|
||||
mt = mOrig.MediaType
|
||||
mc.mt = MediaTypeDocker2Manifest
|
||||
c.desc.MediaType = types.MediaTypeDocker2Manifest
|
||||
m = &docker2Manifest{
|
||||
common: mc,
|
||||
common: c,
|
||||
Manifest: mOrig,
|
||||
}
|
||||
case dockerManifestList.ManifestList:
|
||||
mOrig := orig.(dockerManifestList.ManifestList)
|
||||
mt = mOrig.MediaType
|
||||
mc.mt = MediaTypeDocker2ManifestList
|
||||
c.desc.MediaType = types.MediaTypeDocker2ManifestList
|
||||
m = &docker2ManifestList{
|
||||
common: mc,
|
||||
common: c,
|
||||
ManifestList: mOrig,
|
||||
}
|
||||
case ociv1.Manifest:
|
||||
mOrig := orig.(ociv1.Manifest)
|
||||
mt = mOrig.MediaType
|
||||
mc.mt = MediaTypeOCI1Manifest
|
||||
c.desc.MediaType = types.MediaTypeOCI1Manifest
|
||||
m = &oci1Manifest{
|
||||
common: mc,
|
||||
common: c,
|
||||
Manifest: mOrig,
|
||||
}
|
||||
case ociv1.Index:
|
||||
mOrig := orig.(ociv1.Index)
|
||||
mt = mOrig.MediaType
|
||||
mc.mt = MediaTypeOCI1ManifestList
|
||||
c.desc.MediaType = types.MediaTypeOCI1ManifestList
|
||||
m = &oci1Index{
|
||||
common: mc,
|
||||
common: c,
|
||||
Index: orig.(ociv1.Index),
|
||||
}
|
||||
case UnknownData:
|
||||
m = &unknown{
|
||||
common: mc,
|
||||
UnknownData: orig.(UnknownData),
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported type to convert to a manifest: %T", orig)
|
||||
}
|
||||
// verify media type
|
||||
err = verifyMT(mc.mt, mt)
|
||||
err = verifyMT(c.desc.MediaType, mt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func fromCommon(mc common) (Manifest, error) {
|
||||
func fromCommon(c common) (Manifest, error) {
|
||||
var err error
|
||||
var m Manifest
|
||||
var mt string
|
||||
// compute/verify digest
|
||||
if len(mc.rawBody) > 0 {
|
||||
mc.manifSet = true
|
||||
if mc.mt != MediaTypeDocker1ManifestSigned {
|
||||
d := digest.FromBytes(mc.rawBody)
|
||||
if mc.digest == "" {
|
||||
mc.digest = d
|
||||
} else if mc.digest != d {
|
||||
return nil, fmt.Errorf("digest mismatch, expected %s, found %s", mc.digest.String(), d.String())
|
||||
if len(c.rawBody) > 0 {
|
||||
c.manifSet = true
|
||||
if c.desc.MediaType != MediaTypeDocker1ManifestSigned {
|
||||
d := digest.FromBytes(c.rawBody)
|
||||
if c.desc.Digest == "" {
|
||||
c.desc.Digest = d
|
||||
} else if c.desc.Digest != d {
|
||||
return nil, fmt.Errorf("digest mismatch, expected %s, found %s", c.desc.Digest.String(), d.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
switch mc.mt {
|
||||
switch c.desc.MediaType {
|
||||
case MediaTypeDocker1Manifest:
|
||||
var mOrig dockerSchema1.Manifest
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
if len(c.rawBody) > 0 {
|
||||
err = json.Unmarshal(c.rawBody, &mOrig)
|
||||
mt = mOrig.MediaType
|
||||
}
|
||||
m = &docker1Manifest{common: mc, Manifest: mOrig}
|
||||
m = &docker1Manifest{common: c, Manifest: mOrig}
|
||||
case MediaTypeDocker1ManifestSigned:
|
||||
var mOrig dockerSchema1.SignedManifest
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
if len(c.rawBody) > 0 {
|
||||
err = json.Unmarshal(c.rawBody, &mOrig)
|
||||
mt = mOrig.MediaType
|
||||
d := digest.FromBytes(mOrig.Canonical)
|
||||
if mc.digest == "" {
|
||||
mc.digest = d
|
||||
} else if mc.digest != d {
|
||||
return nil, fmt.Errorf("digest mismatch, expected %s, found %s", mc.digest.String(), d.String())
|
||||
if c.desc.Digest == "" {
|
||||
c.desc.Digest = d
|
||||
} else if c.desc.Digest != d {
|
||||
return nil, fmt.Errorf("digest mismatch, expected %s, found %s", c.desc.Digest.String(), d.String())
|
||||
}
|
||||
}
|
||||
m = &docker1SignedManifest{common: mc, SignedManifest: mOrig}
|
||||
m = &docker1SignedManifest{common: c, SignedManifest: mOrig}
|
||||
case MediaTypeDocker2Manifest:
|
||||
var mOrig dockerSchema2.Manifest
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
if len(c.rawBody) > 0 {
|
||||
err = json.Unmarshal(c.rawBody, &mOrig)
|
||||
mt = mOrig.MediaType
|
||||
}
|
||||
m = &docker2Manifest{common: mc, Manifest: mOrig}
|
||||
m = &docker2Manifest{common: c, Manifest: mOrig}
|
||||
case MediaTypeDocker2ManifestList:
|
||||
var mOrig dockerManifestList.ManifestList
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
if len(c.rawBody) > 0 {
|
||||
err = json.Unmarshal(c.rawBody, &mOrig)
|
||||
mt = mOrig.MediaType
|
||||
}
|
||||
m = &docker2ManifestList{common: mc, ManifestList: mOrig}
|
||||
m = &docker2ManifestList{common: c, ManifestList: mOrig}
|
||||
case MediaTypeOCI1Manifest:
|
||||
var mOrig ociv1.Manifest
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
if len(c.rawBody) > 0 {
|
||||
err = json.Unmarshal(c.rawBody, &mOrig)
|
||||
mt = mOrig.MediaType
|
||||
}
|
||||
m = &oci1Manifest{common: mc, Manifest: mOrig}
|
||||
m = &oci1Manifest{common: c, Manifest: mOrig}
|
||||
case MediaTypeOCI1ManifestList:
|
||||
var mOrig ociv1.Index
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
if len(c.rawBody) > 0 {
|
||||
err = json.Unmarshal(c.rawBody, &mOrig)
|
||||
mt = mOrig.MediaType
|
||||
}
|
||||
m = &oci1Index{common: mc, Index: mOrig}
|
||||
m = &oci1Index{common: c, Index: mOrig}
|
||||
default:
|
||||
var mOrig UnknownData
|
||||
if len(mc.rawBody) > 0 {
|
||||
err = json.Unmarshal(mc.rawBody, &mOrig)
|
||||
}
|
||||
m = &unknown{common: mc, UnknownData: mOrig}
|
||||
return nil, fmt.Errorf("%w: \"%s\"", types.ErrUnsupportedMediaType, c.desc.MediaType)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling manifest for %s: %w", mc.r.CommonName(), err)
|
||||
return nil, fmt.Errorf("error unmarshaling manifest for %s: %w", c.r.CommonName(), err)
|
||||
}
|
||||
// verify media type
|
||||
err = verifyMT(mc.mt, mt)
|
||||
err = verifyMT(c.desc.MediaType, mt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -251,7 +283,7 @@ func getPlatformDesc(p *ociv1.Platform, dl []ociv1.Descriptor) (*ociv1.Descripto
|
||||
return &d, nil
|
||||
}
|
||||
}
|
||||
return nil, wraperr.New(fmt.Errorf("Platform not found: %s", platforms.Format(*p)), ErrNotFound)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform not found: %s", platforms.Format(*p)), types.ErrNotFound)
|
||||
}
|
||||
|
||||
func getPlatformList(dl []ociv1.Descriptor) ([]*ociv1.Platform, error) {
|
||||
|
@ -2,6 +2,7 @@ package manifest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
dockerSchema2 "github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/types"
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
@ -279,167 +281,19 @@ var (
|
||||
`)
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
func TestNewManifest(t *testing.T) {
|
||||
func TestNew(t *testing.T) {
|
||||
r, _ := ref.New("localhost:5000/test:latest")
|
||||
digestDockerSchema2 := digest.FromBytes(rawDockerSchema2)
|
||||
digestML := digest.FromBytes(rawDockerSchema2List)
|
||||
digestInvalid := digest.FromString("invalid")
|
||||
r, _ := ref.New("localhost:5000/test:latest")
|
||||
var tests = []struct {
|
||||
name string
|
||||
mt string
|
||||
raw []byte
|
||||
r ref.Ref
|
||||
header http.Header
|
||||
wantE error
|
||||
}{
|
||||
{
|
||||
name: "Docker Schema 2 Manifest",
|
||||
mt: MediaTypeDocker2Manifest,
|
||||
raw: rawDockerSchema2,
|
||||
r: r,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 2 List from Http",
|
||||
header: http.Header{
|
||||
"Content-Type": []string{MediaTypeDocker2ManifestList},
|
||||
"Docker-Content-Digest": []string{digestML.String()},
|
||||
},
|
||||
raw: rawDockerSchema2List,
|
||||
r: r,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 1 Signed",
|
||||
mt: MediaTypeDocker1ManifestSigned,
|
||||
raw: rawDockerSchema1Signed,
|
||||
r: r,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid Http Digest",
|
||||
header: http.Header{
|
||||
"Content-Type": []string{MediaTypeDocker2ManifestList},
|
||||
"Docker-Content-Digest": []string{digestInvalid.String()},
|
||||
},
|
||||
raw: rawDockerSchema2List,
|
||||
r: r,
|
||||
wantE: fmt.Errorf("digest mismatch, expected %s, found %s", digestInvalid, digestML),
|
||||
},
|
||||
{
|
||||
name: "Ambiguous OCI Image",
|
||||
mt: MediaTypeOCI1Manifest,
|
||||
raw: rawAmbiguousOCI,
|
||||
r: r,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Ambiguous OCI Index",
|
||||
mt: MediaTypeOCI1ManifestList,
|
||||
raw: rawAmbiguousOCI,
|
||||
r: r,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid OCI Index",
|
||||
mt: MediaTypeOCI1ManifestList,
|
||||
raw: rawOCIImage,
|
||||
r: r,
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", MediaTypeOCI1ManifestList, MediaTypeOCI1Manifest),
|
||||
},
|
||||
{
|
||||
name: "Invalid OCI Image",
|
||||
mt: MediaTypeOCI1Manifest,
|
||||
raw: rawOCIIndex,
|
||||
r: r,
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", MediaTypeOCI1Manifest, MediaTypeOCI1ManifestList),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := New(tt.mt, tt.raw, tt.r, tt.header)
|
||||
if tt.wantE == nil && err != nil {
|
||||
t.Errorf("failed creating manifest, err: %v", err)
|
||||
} else if tt.wantE != nil && (err == nil || (tt.wantE != err && tt.wantE.Error() != err.Error())) {
|
||||
t.Errorf("expected error not received, expected %v, received %v", tt.wantE, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromDescriptor(t *testing.T) {
|
||||
digestInvalid := digest.FromString("invalid")
|
||||
digestDockerSchema2 := digest.FromBytes(rawDockerSchema2)
|
||||
digestDockerSchema1Signed, err := digest.Parse("sha256:f3ef067962554c3352dc0c659ca563f73cc396fe0dea2a2c23a7964c6290f782")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse docker schema1 signed digest string: %v", err)
|
||||
}
|
||||
digestOCIImage := digest.FromBytes(rawOCIImage)
|
||||
var tests = []struct {
|
||||
name string
|
||||
desc ociv1.Descriptor
|
||||
raw []byte
|
||||
wantE error
|
||||
}{
|
||||
{
|
||||
name: "Docker Schema 2 Manifest",
|
||||
desc: ociv1.Descriptor{
|
||||
MediaType: MediaTypeDocker2Manifest,
|
||||
Digest: digestDockerSchema2,
|
||||
Size: int64(len(rawDockerSchema2)),
|
||||
},
|
||||
raw: rawDockerSchema2,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 1 Signed Manifest",
|
||||
desc: ociv1.Descriptor{
|
||||
MediaType: MediaTypeDocker1ManifestSigned,
|
||||
Digest: digestDockerSchema1Signed,
|
||||
Size: int64(len(rawDockerSchema1Signed)),
|
||||
},
|
||||
raw: rawDockerSchema1Signed,
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid digest",
|
||||
desc: ociv1.Descriptor{
|
||||
MediaType: MediaTypeDocker2Manifest,
|
||||
Digest: digestInvalid,
|
||||
Size: int64(len(rawDockerSchema2)),
|
||||
},
|
||||
raw: rawDockerSchema2,
|
||||
wantE: fmt.Errorf("digest mismatch, expected %s, found %s", digestInvalid, digestDockerSchema2),
|
||||
},
|
||||
{
|
||||
name: "Invalid Media Type",
|
||||
desc: ociv1.Descriptor{
|
||||
MediaType: MediaTypeOCI1ManifestList,
|
||||
Digest: digestOCIImage,
|
||||
Size: int64(len(rawOCIImage)),
|
||||
},
|
||||
raw: rawOCIImage,
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", MediaTypeOCI1ManifestList, MediaTypeOCI1Manifest),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := FromDescriptor(tt.desc, tt.raw)
|
||||
if tt.wantE == nil && err != nil {
|
||||
t.Errorf("failed creating manifest, err: %v", err)
|
||||
} else if tt.wantE != nil && (err == nil || (tt.wantE != err && tt.wantE.Error() != err.Error())) {
|
||||
t.Errorf("expected error not received, expected %v, received %v", tt.wantE, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromOrig(t *testing.T) {
|
||||
var manifestDockerSchema2, manifestInvalid dockerSchema2.Manifest
|
||||
var manifestDockerSchema1Signed dockerSchema1.SignedManifest
|
||||
err := json.Unmarshal(rawDockerSchema2, &manifestDockerSchema2)
|
||||
err = json.Unmarshal(rawDockerSchema2, &manifestDockerSchema2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal docker schema2 json: %v", err)
|
||||
}
|
||||
@ -447,42 +301,228 @@ func TestFromOrig(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal docker schema2 json: %v", err)
|
||||
}
|
||||
manifestInvalid.MediaType = MediaTypeOCI1Manifest
|
||||
manifestInvalid.MediaType = types.MediaTypeOCI1Manifest
|
||||
err = json.Unmarshal(rawDockerSchema1Signed, &manifestDockerSchema1Signed)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal docker schema1 signed json: %v", err)
|
||||
}
|
||||
var tests = []struct {
|
||||
name string
|
||||
orig interface{}
|
||||
opts []Opts
|
||||
wantR ref.Ref
|
||||
wantDesc ociv1.Descriptor
|
||||
wantE error
|
||||
}{
|
||||
{
|
||||
name: "Nil interface",
|
||||
orig: nil,
|
||||
wantE: fmt.Errorf("Unsupported type to convert to a manifest: %v", nil),
|
||||
name: "empty",
|
||||
wantE: fmt.Errorf("%w: \"%s\"", types.ErrUnsupportedMediaType, ""),
|
||||
},
|
||||
{
|
||||
name: "Docker Schema2",
|
||||
orig: manifestDockerSchema2,
|
||||
name: "Docker Schema 2 Manifest",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker2Manifest,
|
||||
}),
|
||||
WithRaw(rawDockerSchema2),
|
||||
},
|
||||
wantR: r,
|
||||
wantDesc: ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker2Manifest,
|
||||
Size: int64(len(rawDockerSchema2)),
|
||||
Digest: digestDockerSchema2,
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 2 Manifest full desc",
|
||||
opts: []Opts{
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker2Manifest,
|
||||
Digest: digestDockerSchema2,
|
||||
Size: int64(len(rawDockerSchema2)),
|
||||
}),
|
||||
WithRaw(rawDockerSchema2),
|
||||
},
|
||||
wantDesc: ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker2Manifest,
|
||||
Size: int64(len(rawDockerSchema2)),
|
||||
Digest: digestDockerSchema2,
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 2 List from Http",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawDockerSchema2List),
|
||||
WithHeader(http.Header{
|
||||
"Content-Type": []string{MediaTypeDocker2ManifestList},
|
||||
"Docker-Content-Digest": []string{digestML.String()},
|
||||
}),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 1 Signed",
|
||||
orig: manifestDockerSchema1Signed,
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawDockerSchema1Signed),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker1ManifestSigned,
|
||||
}),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema 1 Signed Manifest",
|
||||
opts: []Opts{
|
||||
WithRaw(rawDockerSchema1Signed),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker1ManifestSigned,
|
||||
Digest: digestDockerSchema1Signed,
|
||||
Size: int64(len(rawDockerSchema1Signed)),
|
||||
}),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid Http Digest",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawDockerSchema2List),
|
||||
WithHeader(http.Header{
|
||||
"Content-Type": []string{MediaTypeDocker2ManifestList},
|
||||
"Docker-Content-Digest": []string{digestInvalid.String()},
|
||||
}),
|
||||
},
|
||||
wantE: fmt.Errorf("digest mismatch, expected %s, found %s", digestInvalid, digestML),
|
||||
},
|
||||
{
|
||||
name: "Ambiguous OCI Image",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawAmbiguousOCI),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeOCI1Manifest,
|
||||
}),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Ambiguous OCI Index",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawAmbiguousOCI),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeOCI1ManifestList,
|
||||
}),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid OCI Index",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawOCIImage),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeOCI1ManifestList,
|
||||
}),
|
||||
},
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeOCI1ManifestList, types.MediaTypeOCI1Manifest),
|
||||
},
|
||||
{
|
||||
name: "Invalid OCI Image",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawOCIIndex),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeOCI1Manifest,
|
||||
}),
|
||||
},
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList),
|
||||
},
|
||||
{
|
||||
name: "Invalid digest",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawDockerSchema2),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeDocker2Manifest,
|
||||
Digest: digestInvalid,
|
||||
Size: int64(len(rawDockerSchema2)),
|
||||
}),
|
||||
},
|
||||
wantE: fmt.Errorf("digest mismatch, expected %s, found %s", digestInvalid, digestDockerSchema2),
|
||||
},
|
||||
{
|
||||
name: "Invalid Media Type",
|
||||
opts: []Opts{
|
||||
WithRef(r),
|
||||
WithRaw(rawOCIImage),
|
||||
WithDesc(ociv1.Descriptor{
|
||||
MediaType: types.MediaTypeOCI1ManifestList,
|
||||
Digest: digestOCIImage,
|
||||
Size: int64(len(rawOCIImage)),
|
||||
}),
|
||||
},
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeOCI1ManifestList, types.MediaTypeOCI1Manifest),
|
||||
},
|
||||
{
|
||||
name: "Docker Schema2 Orig",
|
||||
opts: []Opts{
|
||||
WithOrig(manifestDockerSchema2),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Docker Schema1 Signed Orig",
|
||||
opts: []Opts{
|
||||
WithOrig(manifestDockerSchema1Signed),
|
||||
},
|
||||
wantE: nil,
|
||||
},
|
||||
{
|
||||
name: "Invalid Media Type",
|
||||
orig: manifestInvalid,
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", MediaTypeDocker2Manifest, MediaTypeOCI1Manifest),
|
||||
opts: []Opts{
|
||||
WithOrig(manifestInvalid),
|
||||
},
|
||||
wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeDocker2Manifest, types.MediaTypeOCI1Manifest),
|
||||
},
|
||||
|
||||
// TODO: add more tests to improve coverage
|
||||
// - test rate limit
|
||||
// - test retrieving descriptor lists from manifest lists
|
||||
// - test retrieving layers from images
|
||||
// - test retrieving config descriptor from image
|
||||
// - test if manifest is set
|
||||
// - test raw body
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := FromOrig(tt.orig)
|
||||
if tt.wantE == nil && err != nil {
|
||||
t.Errorf("failed creating manifest, err: %v", err)
|
||||
} else if tt.wantE != nil && (err == nil || (tt.wantE != err && tt.wantE.Error() != err.Error())) {
|
||||
m, err := New(tt.opts...)
|
||||
if tt.wantE != nil {
|
||||
if err == nil {
|
||||
t.Errorf("did not receive expected error %v", tt.wantE)
|
||||
} else if !errors.Is(err, tt.wantE) && err.Error() != tt.wantE.Error() {
|
||||
t.Errorf("expected error not received, expected %v, received %v", tt.wantE, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("failed running New: %v", err)
|
||||
return
|
||||
}
|
||||
if tt.wantR.Scheme != "" && m.GetRef().CommonName() != tt.wantR.CommonName() {
|
||||
t.Errorf("ref mismatch, expected %s, received %s", tt.wantR.CommonName(), m.GetRef().CommonName())
|
||||
}
|
||||
if tt.wantDesc.Digest != "" && m.GetDigest() != tt.wantDesc.Digest {
|
||||
t.Errorf("digest mismatch, expected %s, received %s", tt.wantDesc.Digest, m.GetDigest())
|
||||
}
|
||||
if tt.wantDesc.MediaType != "" && m.GetMediaType() != tt.wantDesc.MediaType {
|
||||
t.Errorf("media type mismatch, expected %s, received %s", tt.wantDesc.MediaType, m.GetMediaType())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/internal/wraperr"
|
||||
"github.com/regclient/regclient/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -36,14 +37,14 @@ func (m *oci1Manifest) GetConfigDigest() (digest.Digest, error) {
|
||||
return m.Config.Digest, nil
|
||||
}
|
||||
func (m *oci1Index) GetConfigDescriptor() (ociv1.Descriptor, error) {
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *oci1Index) GetConfigDigest() (digest.Digest, error) {
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *oci1Manifest) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *oci1Index) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
return m.Manifests, nil
|
||||
@ -53,7 +54,7 @@ func (m *oci1Manifest) GetLayers() ([]ociv1.Descriptor, error) {
|
||||
return m.Layers, nil
|
||||
}
|
||||
func (m *oci1Index) GetLayers() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Layers are not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Layers are not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *oci1Manifest) GetOrigManifest() interface{} {
|
||||
@ -64,7 +65,7 @@ func (m *oci1Index) GetOrigManifest() interface{} {
|
||||
}
|
||||
|
||||
func (m *oci1Manifest) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *oci1Index) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
dl, err := m.GetDescriptorList()
|
||||
@ -75,7 +76,7 @@ func (m *oci1Index) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error
|
||||
}
|
||||
|
||||
func (m *oci1Manifest) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.desc.MediaType), types.ErrUnsupportedMediaType)
|
||||
}
|
||||
func (m *oci1Index) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
dl, err := m.GetDescriptorList()
|
||||
@ -87,7 +88,7 @@ func (m *oci1Index) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
|
||||
func (m *oci1Manifest) MarshalJSON() ([]byte, error) {
|
||||
if !m.manifSet {
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), ErrUnavailable)
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), types.ErrUnavailable)
|
||||
}
|
||||
|
||||
if len(m.rawBody) > 0 {
|
||||
@ -98,7 +99,7 @@ func (m *oci1Manifest) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
func (m *oci1Index) MarshalJSON() ([]byte, error) {
|
||||
if !m.manifSet {
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), ErrUnavailable)
|
||||
return []byte{}, wraperr.New(fmt.Errorf("Manifest unavailable, perform a ManifestGet first"), types.ErrUnavailable)
|
||||
}
|
||||
|
||||
if len(m.rawBody) > 0 {
|
||||
@ -125,8 +126,8 @@ func (m *oci1Index) MarshalPretty() ([]byte, error) {
|
||||
if m.r.Reference != "" {
|
||||
fmt.Fprintf(tw, "Name:\t%s\n", m.r.Reference)
|
||||
}
|
||||
fmt.Fprintf(tw, "MediaType:\t%s\n", m.mt)
|
||||
fmt.Fprintf(tw, "Digest:\t%s\n", m.digest.String())
|
||||
fmt.Fprintf(tw, "MediaType:\t%s\n", m.desc.MediaType)
|
||||
fmt.Fprintf(tw, "Digest:\t%s\n", m.desc.Digest.String())
|
||||
fmt.Fprintf(tw, "\t\n")
|
||||
fmt.Fprintf(tw, "Manifests:\t\n")
|
||||
for _, d := range m.Manifests {
|
||||
|
@ -1,50 +0,0 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/regclient/regclient/internal/wraperr"
|
||||
)
|
||||
|
||||
type unknown struct {
|
||||
common
|
||||
UnknownData
|
||||
}
|
||||
|
||||
type UnknownData struct {
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
func (m *unknown) GetConfigDescriptor() (ociv1.Descriptor, error) {
|
||||
return ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *unknown) GetConfigDigest() (digest.Digest, error) {
|
||||
return "", wraperr.New(fmt.Errorf("Config digest not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *unknown) GetDescriptorList() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Platform descriptor list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *unknown) GetLayers() ([]ociv1.Descriptor, error) {
|
||||
return []ociv1.Descriptor{}, wraperr.New(fmt.Errorf("Layer list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *unknown) GetOrigManifest() interface{} {
|
||||
return m.UnknownData
|
||||
}
|
||||
|
||||
func (m *unknown) GetPlatformDesc(p *ociv1.Platform) (*ociv1.Descriptor, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform lookup not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *unknown) GetPlatformList() ([]*ociv1.Platform, error) {
|
||||
return nil, wraperr.New(fmt.Errorf("Platform list not available for media type %s", m.mt), ErrUnsupportedMediaType)
|
||||
}
|
||||
|
||||
func (m *unknown) MarshalJSON() ([]byte, error) {
|
||||
return m.rawBody, nil
|
||||
}
|
@ -43,6 +43,7 @@ type tagConfig struct {
|
||||
mt string
|
||||
raw []byte
|
||||
header http.Header
|
||||
tags []string
|
||||
}
|
||||
|
||||
type Opts func(*tagConfig)
|
||||
@ -61,6 +62,9 @@ func New(opts ...Opts) (*TagList, error) {
|
||||
rawHeader: conf.header,
|
||||
rawBody: conf.raw,
|
||||
}
|
||||
if len(conf.tags) > 0 {
|
||||
tl.Tags = conf.tags
|
||||
}
|
||||
mt := strings.Split(conf.mt, ";")[0] // "application/json; charset=utf-8" -> "application/json"
|
||||
switch mt {
|
||||
case "application/json", "text/plain":
|
||||
@ -68,6 +72,8 @@ func New(opts ...Opts) (*TagList, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case types.MediaTypeOCI1ManifestList:
|
||||
// noop
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: media type: %s, reference: %s", types.ErrUnsupportedMediaType, conf.mt, conf.ref.CommonName())
|
||||
}
|
||||
@ -96,6 +102,11 @@ func WithRef(ref ref.Ref) Opts {
|
||||
tConf.ref = ref
|
||||
}
|
||||
}
|
||||
func WithTags(tags []string) Opts {
|
||||
return func(tConf *tagConfig) {
|
||||
tConf.tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
func (t tagCommon) GetOrig() interface{} {
|
||||
return t.orig
|
||||
|
Reference in New Issue
Block a user