diff --git a/blob.go b/blob.go index b927106..c3cd7b6 100644 --- a/blob.go +++ b/blob.go @@ -14,6 +14,8 @@ import ( "github.com/regclient/regclient/scheme" "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -36,12 +38,12 @@ func BlobWithCallback(callback func(kind types.CallbackKind, instance string, st // BlobCopy copies a blob between two locations. // If the blob already exists in the target, the copy is skipped. // A server side cross repository blob mount is attempted. -func (rc *RegClient) BlobCopy(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d types.Descriptor, opts ...BlobOpts) error { +func (rc *RegClient) BlobCopy(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d descriptor.Descriptor, opts ...BlobOpts) error { if !refSrc.IsSetRepo() { - return fmt.Errorf("refSrc is not set: %s%.0w", refSrc.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("refSrc is not set: %s%.0w", refSrc.CommonName(), errs.ErrInvalidReference) } if !refTgt.IsSetRepo() { - return fmt.Errorf("refTgt is not set: %s%.0w", refTgt.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("refTgt is not set: %s%.0w", refTgt.CommonName(), errs.ErrInvalidReference) } var opt blobOpt for _, optFn := range opts { @@ -173,9 +175,9 @@ func (rc *RegClient) BlobCopy(ctx context.Context, refSrc ref.Ref, refTgt ref.Re // BlobDelete removes a blob from the registry. // This method should only be used to repair a damaged registry. // Typically a server side garbage collection should be used to purge unused blobs. -func (rc *RegClient) BlobDelete(ctx context.Context, r ref.Ref, d types.Descriptor) error { +func (rc *RegClient) BlobDelete(ctx context.Context, r ref.Ref, d descriptor.Descriptor) error { if !r.IsSetRepo() { - return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { @@ -186,13 +188,13 @@ func (rc *RegClient) BlobDelete(ctx context.Context, r ref.Ref, d types.Descript // BlobGet retrieves a blob, returning a reader. // This reader must be closed to free up resources that limit concurrent pulls. -func (rc *RegClient) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) { +func (rc *RegClient) BlobGet(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) { data, err := d.GetData() if err == nil { return blob.NewReader(blob.WithDesc(d), blob.WithRef(r), blob.WithReader(bytes.NewReader(data))), nil } if !r.IsSetRepo() { - return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { @@ -202,9 +204,9 @@ func (rc *RegClient) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) } // BlobGetOCIConfig retrieves an OCI config from a blob, automatically extracting the JSON. -func (rc *RegClient) BlobGetOCIConfig(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.OCIConfig, error) { +func (rc *RegClient) BlobGetOCIConfig(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.OCIConfig, error) { if !r.IsSetRepo() { - return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } b, err := rc.BlobGet(ctx, r, d) if err != nil { @@ -214,9 +216,9 @@ func (rc *RegClient) BlobGetOCIConfig(ctx context.Context, r ref.Ref, d types.De } // BlobHead is used to verify if a blob exists and is accessible. -func (rc *RegClient) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) { +func (rc *RegClient) BlobHead(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) { if !r.IsSetRepo() { - return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { @@ -226,12 +228,12 @@ func (rc *RegClient) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor } // BlobMount attempts to perform a server side copy/mount of the blob between repositories. -func (rc *RegClient) BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d types.Descriptor) error { +func (rc *RegClient) BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d descriptor.Descriptor) error { if !refSrc.IsSetRepo() { - return fmt.Errorf("ref is not set: %s%.0w", refSrc.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", refSrc.CommonName(), errs.ErrInvalidReference) } if !refTgt.IsSetRepo() { - return fmt.Errorf("ref is not set: %s%.0w", refTgt.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", refTgt.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(refSrc.Scheme) if err != nil { @@ -247,13 +249,13 @@ func (rc *RegClient) BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.R // This will attempt an anonymous blob mount first which some registries may support. // It will then try doing a full put of the blob without chunking (most widely supported). // If the full put fails, it will fall back to a chunked upload (useful for flaky networks). -func (rc *RegClient) BlobPut(ctx context.Context, r ref.Ref, d types.Descriptor, rdr io.Reader) (types.Descriptor, error) { +func (rc *RegClient) BlobPut(ctx context.Context, r ref.Ref, d descriptor.Descriptor, rdr io.Reader) (descriptor.Descriptor, error) { if !r.IsSetRepo() { - return types.Descriptor{}, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return descriptor.Descriptor{}, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { - return types.Descriptor{}, err + return descriptor.Descriptor{}, err } return schemeAPI.BlobPut(ctx, r, d, rdr) } diff --git a/blob_test.go b/blob_test.go index 20e72e5..c4f9716 100644 --- a/blob_test.go +++ b/blob_test.go @@ -18,7 +18,8 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -184,7 +185,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := rc.BlobGet(ctx, ref, types.Descriptor{Digest: d1, Size: int64(len(blob1))}) + br, err := rc.BlobGet(ctx, ref, descriptor.Descriptor{Digest: d1, Size: int64(len(blob1))}) if err != nil { t.Fatalf("Failed running BlobGet: %v", err) } @@ -204,7 +205,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - desc := types.Descriptor{ + desc := descriptor.Descriptor{ Digest: d1, Size: int64(len(blob1)), Data: blob1, @@ -228,7 +229,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := rc.BlobHead(ctx, ref, types.Descriptor{Digest: d1}) + br, err := rc.BlobHead(ctx, ref, descriptor.Descriptor{Digest: d1}) if err != nil { t.Fatalf("Failed running BlobHead: %v", err) } @@ -243,12 +244,12 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := rc.BlobGet(ctx, ref, types.Descriptor{Digest: dMissing}) + br, err := rc.BlobGet(ctx, ref, descriptor.Descriptor{Digest: dMissing}) if err == nil { defer br.Close() t.Fatalf("Unexpected success running BlobGet") } - if !errors.Is(err, types.ErrNotFound) { + if !errors.Is(err, errs.ErrNotFound) { t.Errorf("Error does not match \"ErrNotFound\": %v", err) } }) @@ -258,7 +259,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := rc.BlobGet(ctx, ref, types.Descriptor{Digest: d2}) + br, err := rc.BlobGet(ctx, ref, descriptor.Descriptor{Digest: d2}) if err != nil { t.Fatalf("Failed running BlobGet: %v", err) } @@ -277,12 +278,12 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := rc.BlobGet(ctx, ref, types.Descriptor{Digest: d1}) + br, err := rc.BlobGet(ctx, ref, descriptor.Descriptor{Digest: d1}) if err == nil { br.Close() t.Fatalf("Unexpected success running BlobGet") } - if !errors.Is(err, types.ErrHTTPUnauthorized) { + if !errors.Is(err, errs.ErrHTTPUnauthorized) { t.Errorf("Error does not match \"ErrUnauthorized\": %v", err) } }) @@ -672,7 +673,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob1) - dp, err := rc.BlobPut(ctx, ref, types.Descriptor{Digest: d1, Size: int64(len(blob1))}, br) + dp, err := rc.BlobPut(ctx, ref, descriptor.Descriptor{Digest: d1, Size: int64(len(blob1))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -691,7 +692,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob2) - dp, err := rc.BlobPut(ctx, ref, types.Descriptor{Digest: d2, Size: int64(len(blob2))}, br) + dp, err := rc.BlobPut(ctx, ref, descriptor.Descriptor{Digest: d2, Size: int64(len(blob2))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -710,7 +711,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob3) - dp, err := rc.BlobPut(ctx, ref, types.Descriptor{Digest: d3, Size: int64(len(blob3))}, br) + dp, err := rc.BlobPut(ctx, ref, descriptor.Descriptor{Digest: d3, Size: int64(len(blob3))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -1163,7 +1164,7 @@ func TestBlobCopy(t *testing.T) { // same repo t.Run("Copy Same Repo", func(t *testing.T) { - err = rc.BlobCopy(ctx, refA, refA, types.Descriptor{Digest: d1}) + err = rc.BlobCopy(ctx, refA, refA, descriptor.Descriptor{Digest: d1}) if err != nil { t.Fatalf("Failed to copy d1 from repo a to a: %v", err) } @@ -1171,7 +1172,7 @@ func TestBlobCopy(t *testing.T) { // copy blob t.Run("Copy Diff Repo", func(t *testing.T) { - err = rc.BlobCopy(ctx, refA, refB, types.Descriptor{Digest: d1}) + err = rc.BlobCopy(ctx, refA, refB, descriptor.Descriptor{Digest: d1}) if err != nil { t.Fatalf("Failed to copy d1 from repo a to b: %v", err) } @@ -1179,7 +1180,7 @@ func TestBlobCopy(t *testing.T) { // blob exists t.Run("Copy Existing Blob", func(t *testing.T) { - err = rc.BlobCopy(ctx, refA, refB, types.Descriptor{Digest: d1}) + err = rc.BlobCopy(ctx, refA, refB, descriptor.Descriptor{Digest: d1}) if err != nil { t.Fatalf("Failed to copy d1 from repo a to b: %v", err) } @@ -1187,7 +1188,7 @@ func TestBlobCopy(t *testing.T) { // copy fails on get, retry succeeds t.Run("Copy Flaky Get", func(t *testing.T) { - err = rc.BlobCopy(ctx, refA, refB, types.Descriptor{Digest: d2}) + err = rc.BlobCopy(ctx, refA, refB, descriptor.Descriptor{Digest: d2}) if err != nil { t.Fatalf("Failed to copy d2 from repo a to b: %v", err) } @@ -1195,7 +1196,7 @@ func TestBlobCopy(t *testing.T) { // copy fails on put, retry succeeds t.Run("Copy Flaky Put", func(t *testing.T) { - err = rc.BlobCopy(ctx, refA, refB, types.Descriptor{Digest: d3}) + err = rc.BlobCopy(ctx, refA, refB, descriptor.Descriptor{Digest: d3}) if err != nil { t.Fatalf("Failed to copy d3 from repo a to b: %v", err) } diff --git a/cmd/regbot/sandbox/blob.go b/cmd/regbot/sandbox/blob.go index 92f942f..ee1ea16 100644 --- a/cmd/regbot/sandbox/blob.go +++ b/cmd/regbot/sandbox/blob.go @@ -14,8 +14,8 @@ import ( "github.com/sirupsen/logrus" lua "github.com/yuin/gopher-lua" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/ref" ) @@ -113,7 +113,7 @@ func (s *Sandbox) blobGet(ls *lua.LState) int { "ref": r.r.CommonName(), "digest": d, }).Debug("Retrieve blob") - b, err := s.rc.BlobGet(s.ctx, r.r, types.Descriptor{Digest: digest.Digest(d)}) + b, err := s.rc.BlobGet(s.ctx, r.r, descriptor.Descriptor{Digest: digest.Digest(d)}) if err != nil { ls.RaiseError("Failed retrieving \"%s\" blob \"%s\": %v", r.r.CommonName(), d, err) } @@ -141,7 +141,7 @@ func (s *Sandbox) blobHead(ls *lua.LState) int { "ref": r.r.CommonName(), "digest": d, }).Debug("Retrieve blob") - b, err := s.rc.BlobHead(s.ctx, r.r, types.Descriptor{Digest: digest.Digest(d)}) + b, err := s.rc.BlobHead(s.ctx, r.r, descriptor.Descriptor{Digest: digest.Digest(d)}) if err != nil { ls.RaiseError("Failed retrieving \"%s\" blob \"%s\": %v", r.r.CommonName(), d, err) } @@ -192,7 +192,7 @@ func (s *Sandbox) blobPut(ls *lua.LState) int { ls.ArgError(2, "blob content expected") } - dOut, err := s.rc.BlobPut(s.ctx, r.r, types.Descriptor{Digest: d}, rdr) + dOut, err := s.rc.BlobPut(s.ctx, r.r, descriptor.Descriptor{Digest: d}, rdr) if err != nil { ls.RaiseError("Failed to put blob: %v", err) } diff --git a/cmd/regctl/artifact.go b/cmd/regctl/artifact.go index 851da82..ceaeb1b 100644 --- a/cmd/regctl/artifact.go +++ b/cmd/regctl/artifact.go @@ -22,7 +22,10 @@ import ( "github.com/regclient/regclient/pkg/template" "github.com/regclient/regclient/scheme" "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -36,7 +39,7 @@ const ( ) var manifestKnownTypes = []string{ - types.MediaTypeOCI1Manifest, + mediatype.OCI1Manifest, } var artifactFileKnownTypes = []string{ "application/octet-stream", @@ -197,7 +200,7 @@ regctl artifact tree --digest-tags ghcr.io/regclient/regsync:latest`, artifactListCmd.Flags().StringVar(&artifactOpts.sortAnnot, "sort-annotation", "", "Annotation used for sorting results") artifactListCmd.Flags().BoolVar(&artifactOpts.sortDesc, "sort-desc", false, "Sort in descending order") - artifactPutCmd.Flags().StringVarP(&artifactOpts.artifactMT, "media-type", "", types.MediaTypeOCI1Manifest, "EXPERIMENTAL: Manifest media-type") + artifactPutCmd.Flags().StringVarP(&artifactOpts.artifactMT, "media-type", "", mediatype.OCI1Manifest, "EXPERIMENTAL: Manifest media-type") _ = artifactPutCmd.RegisterFlagCompletionFunc("media-type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return manifestKnownTypes, cobra.ShellCompDirectiveNoFileComp }) @@ -263,7 +266,7 @@ func (artifactOpts *artifactCmd) runArtifactGet(cmd *cobra.Command, args []strin } r := ref.Ref{} - matchOpts := types.MatchOpt{ + matchOpts := descriptor.MatchOpt{ ArtifactType: artifactOpts.filterAT, SortAnnotation: artifactOpts.sortAnnot, SortDesc: artifactOpts.sortDesc, @@ -335,13 +338,13 @@ func (artifactOpts *artifactCmd) runArtifactGet(cmd *cobra.Command, args []strin if m.IsList() { mi, ok := m.(manifest.Indexer) if !ok { - return fmt.Errorf("manifest list does not support index methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest list does not support index methods%.0w", errs.ErrUnsupportedMediaType) } dl, err := mi.GetManifestList() if err != nil { return fmt.Errorf("failed to get descriptor list: %w", err) } - d, err := types.DescriptorListSearch(dl, matchOpts) + d, err := descriptor.DescriptorListSearch(dl, matchOpts) if err != nil { return fmt.Errorf("no matching artifacts found in index: %w", err) } @@ -353,7 +356,7 @@ func (artifactOpts *artifactCmd) runArtifactGet(cmd *cobra.Command, args []strin } mi, ok := m.(manifest.Imager) if !ok { - return fmt.Errorf("manifest does not support image methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support image methods%.0w", errs.ErrUnsupportedMediaType) } // if config-file defined, create file as writer, perform a blob get @@ -527,7 +530,7 @@ func (artifactOpts *artifactCmd) runArtifactList(cmd *cobra.Command, args []stri rc := artifactOpts.rootOpts.newRegClient() defer rc.Close(ctx, rSubject) - matchOpts := types.MatchOpt{ + matchOpts := descriptor.MatchOpt{ ArtifactType: artifactOpts.filterAT, SortAnnotation: artifactOpts.sortAnnot, SortDesc: artifactOpts.sortDesc, @@ -613,10 +616,10 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin var err error switch artifactOpts.artifactMT { - case types.MediaTypeOCI1Artifact: + case mediatype.OCI1Artifact: log.Warnf("changing media-type is experimental and non-portable") hasConfig = false - case "", types.MediaTypeOCI1Manifest: + case "", mediatype.OCI1Manifest: hasConfig = true default: return fmt.Errorf("unsupported manifest media type: %s", artifactOpts.artifactMT) @@ -655,7 +658,7 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin // validate/set artifactType and config.mediaType if hasConfig && artifactOpts.artifactConfigMT == "" { if artifactOpts.artifactConfig == "" { - artifactOpts.artifactConfigMT = types.MediaTypeOCI1Empty + artifactOpts.artifactConfigMT = mediatype.OCI1Empty } else { if artifactOpts.artifactType != "" { artifactOpts.artifactConfigMT = artifactOpts.artifactType @@ -666,10 +669,10 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin } } if !hasConfig && (artifactOpts.artifactConfig != "" || artifactOpts.artifactConfigMT != "") { - return fmt.Errorf("cannot set config-type or config-file on %s%.0w", artifactOpts.artifactMT, types.ErrUnsupportedMediaType) + return fmt.Errorf("cannot set config-type or config-file on %s%.0w", artifactOpts.artifactMT, errs.ErrUnsupportedMediaType) } if artifactOpts.artifactType == "" { - if !hasConfig || artifactOpts.artifactConfigMT == types.MediaTypeOCI1Empty { + if !hasConfig || artifactOpts.artifactConfigMT == mediatype.OCI1Empty { log.Warnf("using default value for artifact-type is not recommended") artifactOpts.artifactType = defaultMTArtifact } @@ -704,7 +707,7 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin rc := artifactOpts.rootOpts.newRegClient() defer rc.Close(ctx, r) - var subjectDesc *types.Descriptor + var subjectDesc *descriptor.Descriptor if rSubject.IsSet() { smh, err := rc.ManifestHead(ctx, rSubject, regclient.WithManifestRequireDigest()) if err != nil { @@ -723,21 +726,21 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin if err != nil { return fmt.Errorf("failed to get platform descriptor: %w", err) } - subjectDesc = &types.Descriptor{MediaType: d.MediaType, Digest: d.Digest, Size: d.Size} + subjectDesc = &descriptor.Descriptor{MediaType: d.MediaType, Digest: d.Digest, Size: d.Size} } else { d := smh.GetDescriptor() - subjectDesc = &types.Descriptor{MediaType: d.MediaType, Digest: d.Digest, Size: d.Size} + subjectDesc = &descriptor.Descriptor{MediaType: d.MediaType, Digest: d.Digest, Size: d.Size} } } // read config, or initialize to an empty json config - confDesc := types.Descriptor{} + confDesc := descriptor.Descriptor{} if hasConfig { var configBytes []byte var configDigest digest.Digest if artifactOpts.artifactConfig == "" { - configBytes = types.EmptyData - configDigest = types.EmptyDigest + configBytes = descriptor.EmptyData + configDigest = descriptor.EmptyDigest } else { var err error configBytes, err = os.ReadFile(artifactOpts.artifactConfig) @@ -747,19 +750,19 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin configDigest = digest.FromBytes(configBytes) } // push config to registry - _, err = rc.BlobPut(ctx, r, types.Descriptor{Digest: configDigest, Size: int64(len(configBytes))}, bytes.NewReader(configBytes)) + _, err = rc.BlobPut(ctx, r, descriptor.Descriptor{Digest: configDigest, Size: int64(len(configBytes))}, bytes.NewReader(configBytes)) if err != nil { return err } // save config descriptor to manifest - confDesc = types.Descriptor{ + confDesc = descriptor.Descriptor{ MediaType: artifactOpts.artifactConfigMT, Digest: configDigest, Size: int64(len(configBytes)), } } - blobs := []types.Descriptor{} + blobs := []descriptor.Descriptor{} if len(artifactOpts.artifactFile) > 0 { // if files were passed for i, f := range artifactOpts.artifactFile { @@ -804,7 +807,7 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin } d := digester.Digest() // add layer to manifest - desc := types.Descriptor{ + desc := descriptor.Descriptor{ MediaType: mt, Digest: d, Size: l, @@ -825,7 +828,7 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin } blobs = append(blobs, desc) // if blob already exists, skip Put - bRdr, err := rc.BlobHead(ctx, r, types.Descriptor{Digest: d}) + bRdr, err := rc.BlobHead(ctx, r, descriptor.Descriptor{Digest: d}) if err == nil { _ = bRdr.Close() return nil @@ -835,7 +838,7 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin if err != nil { return err } - _, err = rc.BlobPut(ctx, r, types.Descriptor{Digest: d, Size: l}, rdr) + _, err = rc.BlobPut(ctx, r, descriptor.Descriptor{Digest: d, Size: l}, rdr) if err != nil { return err } @@ -851,7 +854,7 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin if len(artifactOpts.artifactFileMT) > 0 { mt = artifactOpts.artifactFileMT[0] } - d, err := rc.BlobPut(ctx, r, types.Descriptor{}, cmd.InOrStdin()) + d, err := rc.BlobPut(ctx, r, descriptor.Descriptor{}, cmd.InOrStdin()) if err != nil { return err } @@ -860,19 +863,19 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin } switch artifactOpts.artifactMT { - case types.MediaTypeOCI1Artifact: + case mediatype.OCI1Artifact: m := v1.ArtifactManifest{ - MediaType: types.MediaTypeOCI1Artifact, + MediaType: mediatype.OCI1Artifact, ArtifactType: artifactOpts.artifactType, Blobs: blobs, Annotations: annotations, Subject: subjectDesc, } mOpts = append(mOpts, manifest.WithOrig(m)) - case "", types.MediaTypeOCI1Manifest: + case "", mediatype.OCI1Manifest: m := v1.Manifest{ Versioned: v1.ManifestSchemaVersion, - MediaType: types.MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, ArtifactType: artifactOpts.artifactType, Config: confDesc, Layers: blobs, @@ -942,8 +945,8 @@ func (artifactOpts *artifactCmd) runArtifactPut(cmd *cobra.Command, args []strin // create a new index mii := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{d}, + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{d}, } mi, err := manifest.New(manifest.WithOrig(mii)) if err != nil { @@ -981,7 +984,7 @@ func (artifactOpts *artifactCmd) runArtifactTree(cmd *cobra.Command, args []stri referrerOpts := []scheme.ReferrerOpts{} if artifactOpts.filterAT != "" { - referrerOpts = append(referrerOpts, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: artifactOpts.filterAT})) + referrerOpts = append(referrerOpts, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: artifactOpts.filterAT})) } if artifactOpts.filterAnnot != nil { af := map[string]string{} @@ -993,7 +996,7 @@ func (artifactOpts *artifactCmd) runArtifactTree(cmd *cobra.Command, args []stri af[kv] = "" } } - referrerOpts = append(referrerOpts, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: af})) + referrerOpts = append(referrerOpts, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: af})) } // include digest tags if requested diff --git a/cmd/regctl/artifact_test.go b/cmd/regctl/artifact_test.go index 88be549..c049ca8 100644 --- a/cmd/regctl/artifact_test.go +++ b/cmd/regctl/artifact_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestArtifactGet(t *testing.T) { @@ -28,12 +28,12 @@ func TestArtifactGet(t *testing.T) { { name: "Invalid ref", args: []string{"artifact", "get", "invalid*ref"}, - expectErr: types.ErrInvalidReference, + expectErr: errs.ErrInvalidReference, }, { name: "Missing manifest", args: []string{"artifact", "get", "ocidir://../../testdata/testrepo:missing"}, - expectErr: types.ErrNotFound, + expectErr: errs.ErrNotFound, }, { name: "By Manifest", @@ -88,12 +88,12 @@ func TestArtifactList(t *testing.T) { { name: "Invalid ref", args: []string{"artifact", "list", "invalid*ref"}, - expectErr: types.ErrInvalidReference, + expectErr: errs.ErrInvalidReference, }, { name: "Missing manifest", args: []string{"artifact", "list", "ocidir://../../testdata/testrepo:missing"}, - expectErr: types.ErrNotFound, + expectErr: errs.ErrNotFound, }, { name: "No referrers", @@ -176,7 +176,7 @@ func TestArtifactPut(t *testing.T) { { name: "Invalid ref", args: []string{"artifact", "put", "invalid*ref"}, - expectErr: types.ErrInvalidReference, + expectErr: errs.ErrInvalidReference, }, { name: "Put artifact", @@ -265,12 +265,12 @@ func TestArtifactTree(t *testing.T) { { name: "Invalid ref", args: []string{"artifact", "tree", "invalid*ref"}, - expectErr: types.ErrInvalidReference, + expectErr: errs.ErrInvalidReference, }, { name: "Missing manifest", args: []string{"artifact", "tree", "ocidir://../../testdata/testrepo:missing"}, - expectErr: types.ErrNotFound, + expectErr: errs.ErrNotFound, }, { name: "No referrers", diff --git a/cmd/regctl/blob.go b/cmd/regctl/blob.go index 52cf9db..04c5985 100644 --- a/cmd/regctl/blob.go +++ b/cmd/regctl/blob.go @@ -20,7 +20,7 @@ import ( "github.com/regclient/regclient/internal/diff" "github.com/regclient/regclient/pkg/template" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/ref" ) @@ -224,7 +224,7 @@ func (blobOpts *blobCmd) runBlobDelete(cmd *cobra.Command, args []string) error "repository": r.Repository, "digest": args[1], }).Debug("Deleting blob") - return rc.BlobDelete(ctx, r, types.Descriptor{Digest: d}) + return rc.BlobDelete(ctx, r, descriptor.Descriptor{Digest: d}) } func (blobOpts *blobCmd) runBlobDiffConfig(cmd *cobra.Command, args []string) error { @@ -251,7 +251,7 @@ func (blobOpts *blobCmd) runBlobDiffConfig(cmd *cobra.Command, args []string) er if err != nil { return err } - c1, err := rc.BlobGetOCIConfig(ctx, r1, types.Descriptor{Digest: d1}) + c1, err := rc.BlobGetOCIConfig(ctx, r1, descriptor.Descriptor{Digest: d1}) if err != nil { return err } @@ -264,7 +264,7 @@ func (blobOpts *blobCmd) runBlobDiffConfig(cmd *cobra.Command, args []string) er if err != nil { return err } - c2, err := rc.BlobGetOCIConfig(ctx, r2, types.Descriptor{Digest: d2}) + c2, err := rc.BlobGetOCIConfig(ctx, r2, descriptor.Descriptor{Digest: d2}) if err != nil { return err } @@ -305,7 +305,7 @@ func (blobOpts *blobCmd) runBlobDiffLayer(cmd *cobra.Command, args []string) err if err != nil { return err } - b1, err := rc.BlobGet(ctx, r1, types.Descriptor{Digest: d1}) + b1, err := rc.BlobGet(ctx, r1, descriptor.Descriptor{Digest: d1}) if err != nil { return err } @@ -331,7 +331,7 @@ func (blobOpts *blobCmd) runBlobDiffLayer(cmd *cobra.Command, args []string) err if err != nil { return err } - b2, err := rc.BlobGet(ctx, r2, types.Descriptor{Digest: d2}) + b2, err := rc.BlobGet(ctx, r2, descriptor.Descriptor{Digest: d2}) if err != nil { return err } @@ -382,7 +382,7 @@ func (blobOpts *blobCmd) runBlobGet(cmd *cobra.Command, args []string) error { "repository": r.Repository, "digest": args[1], }).Debug("Pulling blob") - blob, err := rc.BlobGet(ctx, r, types.Descriptor{Digest: d}) + blob, err := rc.BlobGet(ctx, r, descriptor.Descriptor{Digest: d}) if err != nil { return err } @@ -424,7 +424,7 @@ func (blobOpts *blobCmd) runBlobGetFile(cmd *cobra.Command, args []string) error "digest": args[1], "filename": filename, }).Debug("Get file") - blob, err := rc.BlobGet(ctx, r, types.Descriptor{Digest: d}) + blob, err := rc.BlobGet(ctx, r, descriptor.Descriptor{Digest: d}) if err != nil { return err } @@ -486,7 +486,7 @@ func (blobOpts *blobCmd) runBlobHead(cmd *cobra.Command, args []string) error { "repository": r.Repository, "digest": args[1], }).Debug("Blob head") - blob, err := rc.BlobHead(ctx, r, types.Descriptor{Digest: d}) + blob, err := rc.BlobHead(ctx, r, descriptor.Descriptor{Digest: d}) if err != nil { return err } @@ -518,7 +518,7 @@ func (blobOpts *blobCmd) runBlobPut(cmd *cobra.Command, args []string) error { "repository": r.Repository, "digest": blobOpts.digest, }).Debug("Pushing blob") - dOut, err := rc.BlobPut(ctx, r, types.Descriptor{Digest: digest.Digest(blobOpts.digest)}, cmd.InOrStdin()) + dOut, err := rc.BlobPut(ctx, r, descriptor.Descriptor{Digest: digest.Digest(blobOpts.digest)}, cmd.InOrStdin()) if err != nil { return err } @@ -556,7 +556,7 @@ func (blobOpts *blobCmd) runBlobCopy(cmd *cobra.Command, args []string) error { "target": rTgt.CommonName(), "digest": args[2], }).Debug("Blob copy") - err = rc.BlobCopy(ctx, rSrc, rTgt, types.Descriptor{Digest: d}) + err = rc.BlobCopy(ctx, rSrc, rTgt, descriptor.Descriptor{Digest: d}) if err != nil { return err } diff --git a/cmd/regctl/completion.go b/cmd/regctl/completion.go index 34dd764..ce6aa13 100644 --- a/cmd/regctl/completion.go +++ b/cmd/regctl/completion.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -107,12 +107,12 @@ func completeArgPlatform(cmd *cobra.Command, args []string, toComplete string) ( func completeArgMediaTypeManifest(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{ - types.MediaTypeDocker2Manifest, - types.MediaTypeDocker2ManifestList, - types.MediaTypeOCI1Manifest, - types.MediaTypeOCI1ManifestList, - types.MediaTypeDocker1Manifest, - types.MediaTypeDocker1ManifestSigned, + mediatype.Docker2Manifest, + mediatype.Docker2ManifestList, + mediatype.OCI1Manifest, + mediatype.OCI1ManifestList, + mediatype.Docker1Manifest, + mediatype.Docker1ManifestSigned, }, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/regctl/image.go b/cmd/regctl/image.go index cd6dc12..601484d 100644 --- a/cmd/regctl/image.go +++ b/cmd/regctl/image.go @@ -24,6 +24,7 @@ import ( "github.com/regclient/regclient/pkg/template" "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" @@ -842,7 +843,7 @@ func (imageOpts *imageCmd) runImageCheckBase(cmd *cobra.Command, args []string) if err == nil { log.Info("base image matches") return nil - } else if errors.Is(err, types.ErrMismatch) { + } else if errors.Is(err, errs.ErrMismatch) { log.WithFields(logrus.Fields{ "err": err, }).Info("base image mismatch") @@ -1200,7 +1201,7 @@ func (imageOpts *imageCmd) runImageGetFile(cmd *cobra.Command, args []string) er } th, rdr, err := btr.ReadFile(filename) if err != nil { - if errors.Is(err, types.ErrFileNotFound) { + if errors.Is(err, errs.ErrFileNotFound) { if err := btr.Close(); err != nil { return err } @@ -1244,7 +1245,7 @@ func (imageOpts *imageCmd) runImageGetFile(cmd *cobra.Command, args []string) er return nil } // all layers exhausted, not found or deleted - return types.ErrNotFound + return errs.ErrNotFound } func (imageOpts *imageCmd) runImageImport(cmd *cobra.Command, args []string) error { @@ -1294,7 +1295,7 @@ func (imageOpts *imageCmd) runImageInspect(cmd *cobra.Command, args []string) er } mi, ok := m.(manifest.Imager) if !ok { - return fmt.Errorf("manifest does not support image methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support image methods%.0w", errs.ErrUnsupportedMediaType) } cd, err := mi.GetConfig() if err != nil { diff --git a/cmd/regctl/index.go b/cmd/regctl/index.go index 515bdb5..eb49464 100644 --- a/cmd/regctl/index.go +++ b/cmd/regctl/index.go @@ -10,17 +10,19 @@ import ( "github.com/regclient/regclient" "github.com/regclient/regclient/pkg/template" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" ) var indexKnownTypes = []string{ - types.MediaTypeOCI1ManifestList, - types.MediaTypeDocker2ManifestList, + mediatype.OCI1ManifestList, + mediatype.Docker2ManifestList, } type indexCmd struct { @@ -116,7 +118,7 @@ regctl index delete registry.example.org/repo:v1 \ indexCreateCmd.Flags().StringVar(&indexOpts.format, "format", "", "Format output with go template syntax") indexCreateCmd.Flags().BoolVar(&indexOpts.incDigestTags, "digest-tags", false, "Include digest tags") indexCreateCmd.Flags().BoolVar(&indexOpts.incReferrers, "referrers", false, "Include referrers") - indexCreateCmd.Flags().StringVarP(&indexOpts.mediaType, "media-type", "m", types.MediaTypeOCI1ManifestList, "Media-type for manifest list or OCI Index") + indexCreateCmd.Flags().StringVarP(&indexOpts.mediaType, "media-type", "m", mediatype.OCI1ManifestList, "Media-type for manifest list or OCI Index") indexCreateCmd.Flags().StringVar(&indexOpts.subject, "subject", "", "Specify a subject tag or digest (this manifest must already exist in the repo)") indexCreateCmd.Flags().StringArrayVar(&indexOpts.refs, "ref", []string{}, "References to include in new index") indexCreateCmd.Flags().StringArrayVar(&indexOpts.platforms, "platform", []string{}, "Platforms to include from ref") @@ -153,7 +155,7 @@ func (indexOpts *indexCmd) runIndexAdd(cmd *cobra.Command, args []string) error } mi, ok := m.(manifest.Indexer) if !ok { - return fmt.Errorf("current manifest is not an index/manifest list, \"%s\": %w", m.GetDescriptor().MediaType, types.ErrUnsupportedMediaType) + return fmt.Errorf("current manifest is not an index/manifest list, \"%s\": %w", m.GetDescriptor().MediaType, errs.ErrUnsupportedMediaType) } curDesc, err := mi.GetManifestList() if err != nil { @@ -199,8 +201,8 @@ func (indexOpts *indexCmd) runIndexCreate(cmd *cobra.Command, args []string) err ctx := cmd.Context() // validate media type - if indexOpts.mediaType != types.MediaTypeOCI1ManifestList && indexOpts.mediaType != types.MediaTypeDocker2ManifestList { - return fmt.Errorf("unsupported manifest media type: %s%.0w", indexOpts.mediaType, types.ErrUnsupportedMediaType) + if indexOpts.mediaType != mediatype.OCI1ManifestList && indexOpts.mediaType != mediatype.Docker2ManifestList { + return fmt.Errorf("unsupported manifest media type: %s%.0w", indexOpts.mediaType, errs.ErrUnsupportedMediaType) } // parse ref @@ -231,8 +233,8 @@ func (indexOpts *indexCmd) runIndexCreate(cmd *cobra.Command, args []string) err } descList = indexDescListRmDup(descList) - var subj *types.Descriptor - if indexOpts.subject != "" && indexOpts.mediaType == types.MediaTypeOCI1ManifestList { + var subj *descriptor.Descriptor + if indexOpts.subject != "" && indexOpts.mediaType == mediatype.OCI1ManifestList { var rSubj ref.Ref dig, err := digest.Parse(indexOpts.subject) if err == nil { @@ -252,10 +254,10 @@ func (indexOpts *indexCmd) runIndexCreate(cmd *cobra.Command, args []string) err // build the index mOpts := []manifest.Opts{} switch indexOpts.mediaType { - case types.MediaTypeOCI1ManifestList: + case mediatype.OCI1ManifestList: m := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, ArtifactType: indexOpts.artifactType, Manifests: descList, Subject: subj, @@ -264,7 +266,7 @@ func (indexOpts *indexCmd) runIndexCreate(cmd *cobra.Command, args []string) err m.Annotations = annotations } mOpts = append(mOpts, manifest.WithOrig(m)) - case types.MediaTypeDocker2ManifestList: + case mediatype.Docker2ManifestList: m := schema2.ManifestList{ Versioned: schema2.ManifestListSchemaVersion, Manifests: descList, @@ -321,7 +323,7 @@ func (indexOpts *indexCmd) runIndexDelete(cmd *cobra.Command, args []string) err } mi, ok := m.(manifest.Indexer) if !ok { - return fmt.Errorf("current manifest is not an index/manifest list, \"%s\": %w", m.GetDescriptor().MediaType, types.ErrUnsupportedMediaType) + return fmt.Errorf("current manifest is not an index/manifest list, \"%s\": %w", m.GetDescriptor().MediaType, errs.ErrUnsupportedMediaType) } curDesc, err := mi.GetManifestList() if err != nil { @@ -387,7 +389,7 @@ func (indexOpts *indexCmd) runIndexDelete(cmd *cobra.Command, args []string) err return template.Writer(cmd.OutOrStdout(), indexOpts.format, result) } -func (indexOpts *indexCmd) indexBuildDescList(ctx context.Context, rc *regclient.RegClient, r ref.Ref) ([]types.Descriptor, error) { +func (indexOpts *indexCmd) indexBuildDescList(ctx context.Context, rc *regclient.RegClient, r ref.Ref) ([]descriptor.Descriptor, error) { imgCopyOpts := []regclient.ImageOpts{ regclient.ImageWithChild(), } @@ -467,7 +469,7 @@ func (indexOpts *indexCmd) indexBuildDescList(ctx context.Context, rc *regclient } // parse each digest, pull manifest, get config, append to list of descriptors - descList := []types.Descriptor{} + descList := []descriptor.Descriptor{} for _, dig := range indexOpts.digests { rDig := r.SetDigest(dig) mDig, err := rc.ManifestHead(ctx, rDig, regclient.WithManifestRequireDigest()) @@ -526,7 +528,7 @@ func indexGetPlatform(ctx context.Context, rc *regclient.RegClient, r ref.Ref, m return nil, nil } -func indexDescListRmDup(dl []types.Descriptor) []types.Descriptor { +func indexDescListRmDup(dl []descriptor.Descriptor) []descriptor.Descriptor { i := 0 for i < len(dl)-1 { j := len(dl) - 1 diff --git a/cmd/regctl/manifest.go b/cmd/regctl/manifest.go index 983443f..c5aa654 100644 --- a/cmd/regctl/manifest.go +++ b/cmd/regctl/manifest.go @@ -14,7 +14,7 @@ import ( "github.com/regclient/regclient" "github.com/regclient/regclient/internal/diff" "github.com/regclient/regclient/pkg/template" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -374,7 +374,7 @@ func (manifestOpts *manifestCmd) runManifestPut(cmd *cobra.Command, args []strin manifest.WithRaw(raw), } if manifestOpts.contentType != "" { - opts = append(opts, manifest.WithDesc(types.Descriptor{ + opts = append(opts, manifest.WithDesc(descriptor.Descriptor{ MediaType: manifestOpts.contentType, })) } @@ -432,8 +432,8 @@ func getManifest(ctx context.Context, rc *regclient.RegClient, r ref.Ref, pStr s return m, nil } -func getPlatformDesc(ctx context.Context, rc *regclient.RegClient, m manifest.Manifest, pStr string) (*types.Descriptor, error) { - var desc *types.Descriptor +func getPlatformDesc(ctx context.Context, rc *regclient.RegClient, m manifest.Manifest, pStr string) (*descriptor.Descriptor, error) { + var desc *descriptor.Descriptor var err error if !m.IsList() { return desc, fmt.Errorf("%w: manifest is not a list", ErrInvalidInput) diff --git a/cmd/regctl/manifest_test.go b/cmd/regctl/manifest_test.go index fb4a587..61d3d0f 100644 --- a/cmd/regctl/manifest_test.go +++ b/cmd/regctl/manifest_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestManifestHead(t *testing.T) { @@ -25,12 +25,12 @@ func TestManifestHead(t *testing.T) { { name: "Invalid ref", args: []string{"manifest", "head", "invalid*ref"}, - expectErr: types.ErrInvalidReference, + expectErr: errs.ErrInvalidReference, }, { name: "Missing manifest", args: []string{"manifest", "head", "ocidir://../../testdata/testrepo:missing"}, - expectErr: types.ErrNotFound, + expectErr: errs.ErrNotFound, }, { name: "Digest", diff --git a/cmd/regctl/tag_test.go b/cmd/regctl/tag_test.go index b606ff7..77846ed 100644 --- a/cmd/regctl/tag_test.go +++ b/cmd/regctl/tag_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestTagList(t *testing.T) { @@ -26,7 +26,7 @@ func TestTagList(t *testing.T) { { name: "Invalid ref", args: []string{"tag", "ls", "invalid*ref"}, - expectErr: types.ErrInvalidReference, + expectErr: errs.ErrInvalidReference, }, { name: "Missing repo", diff --git a/cmd/regsync/config.go b/cmd/regsync/config.go index 822a760..11118ff 100644 --- a/cmd/regsync/config.go +++ b/cmd/regsync/config.go @@ -10,16 +10,16 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/pkg/template" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/mediatype" ) // delay checking for at least 5 minutes when rate limit is exceeded var rateLimitRetryMin = time.Minute * 5 var defaultMediaTypes = []string{ - types.MediaTypeDocker2Manifest, - types.MediaTypeDocker2ManifestList, - types.MediaTypeOCI1Manifest, - types.MediaTypeOCI1ManifestList, + mediatype.Docker2Manifest, + mediatype.Docker2ManifestList, + mediatype.OCI1Manifest, + mediatype.OCI1ManifestList, } // Config is parsed configuration file for regsync diff --git a/cmd/regsync/regsync_test.go b/cmd/regsync/regsync_test.go index 4a38c52..2846ce1 100644 --- a/cmd/regsync/regsync_test.go +++ b/cmd/regsync/regsync_test.go @@ -12,7 +12,8 @@ import ( "github.com/regclient/regclient/internal/rwfs" "github.com/regclient/regclient/internal/throttle" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -98,12 +99,12 @@ func TestProcess(t *testing.T) { t.Fatalf("failed to get platform ") } d2AMD := desc2AMD.Digest - desc2SBOM, err := rc.ReferrerList(ctx, r2, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: "application/example.sbom"})) + desc2SBOM, err := rc.ReferrerList(ctx, r2, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: "application/example.sbom"})) if err != nil || len(desc2SBOM.Descriptors) == 0 { t.Fatalf("failed to get SBOM for v2: %v", err) } d2SBOM := desc2SBOM.Descriptors[0].Digest - desc2Sig, err := rc.ReferrerList(ctx, r2, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: "application/example.signature"})) + desc2Sig, err := rc.ReferrerList(ctx, r2, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: "application/example.signature"})) if err != nil || len(desc2Sig.Descriptors) == 0 { t.Fatalf("failed to get signature for v2: %v", err) } @@ -580,7 +581,7 @@ func TestProcess(t *testing.T) { }, action: actionCopy, desired: []string{}, - expErr: types.ErrInvalidReference, + expErr: errs.ErrInvalidReference, }, { name: "InvalidTargetImage", @@ -591,7 +592,7 @@ func TestProcess(t *testing.T) { }, action: actionCopy, desired: []string{}, - expErr: types.ErrInvalidReference, + expErr: errs.ErrInvalidReference, }, { name: "InvalidSourceRepository", @@ -602,7 +603,7 @@ func TestProcess(t *testing.T) { }, action: actionCopy, desired: []string{}, - expErr: types.ErrInvalidReference, + expErr: errs.ErrInvalidReference, }, { name: "InvalidTargetRepository", @@ -613,7 +614,7 @@ func TestProcess(t *testing.T) { }, action: actionCopy, desired: []string{}, - expErr: types.ErrInvalidReference, + expErr: errs.ErrInvalidReference, }, { name: "InvalidType", @@ -701,7 +702,7 @@ func TestProcessRef(t *testing.T) { }{ { name: "empty", - expErr: types.ErrNotFound, + expErr: errs.ErrNotFound, }, { name: "check v1", diff --git a/cmd/regsync/root.go b/cmd/regsync/root.go index df50de1..8e11f1a 100644 --- a/cmd/regsync/root.go +++ b/cmd/regsync/root.go @@ -26,7 +26,8 @@ import ( "github.com/regclient/regclient/pkg/template" "github.com/regclient/regclient/scheme" "github.com/regclient/regclient/scheme/reg" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -611,7 +612,7 @@ func (rootOpts *rootCmd) processImage(ctx context.Context, s ConfigSync, src, tg // process a sync step func (rootOpts *rootCmd) processRef(ctx context.Context, s ConfigSync, src, tgt ref.Ref, action actionType) error { mSrc, err := rc.ManifestHead(ctx, src, regclient.WithManifestRequireDigest()) - if err != nil && errors.Is(err, types.ErrUnsupportedAPI) { + if err != nil && errors.Is(err, errs.ErrUnsupportedAPI) { mSrc, err = rc.ManifestGet(ctx, src) } if err != nil { @@ -831,10 +832,10 @@ func (rootOpts *rootCmd) processRef(ctx context.Context, s ConfigSync, src, tgt for _, filter := range s.ReferrerFilters { rOpts := []scheme.ReferrerOpts{} if filter.ArtifactType != "" { - rOpts = append(rOpts, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: filter.ArtifactType})) + rOpts = append(rOpts, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: filter.ArtifactType})) } if filter.Annotations != nil { - rOpts = append(rOpts, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: filter.Annotations})) + rOpts = append(rOpts, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: filter.Annotations})) } opts = append(opts, regclient.ImageWithReferrers(rOpts...)) } diff --git a/image.go b/image.go index c40b384..4148c1c 100644 --- a/image.go +++ b/image.go @@ -24,8 +24,11 @@ import ( "github.com/regclient/regclient/pkg/archive" "github.com/regclient/regclient/scheme" "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -46,8 +49,8 @@ type dockerTarManifest struct { Config string RepoTags []string Layers []string - Parent digest.Digest `json:",omitempty"` - LayerSources map[digest.Digest]types.Descriptor `json:",omitempty"` + Parent digest.Digest `json:",omitempty"` + LayerSources map[digest.Digest]descriptor.Descriptor `json:",omitempty"` } type tarFileHandler func(header *tar.Header, trd *tarReadData) error @@ -222,7 +225,7 @@ func ImageWithReferrers(rOpts ...scheme.ReferrerOpts) ImageOpts { } // ImageCheckBase returns nil if the base image is unchanged. -// A base image mismatch returns an error that wraps types.ErrMismatch. +// A base image mismatch returns an error that wraps errs.ErrMismatch. func (rc *RegClient) ImageCheckBase(ctx context.Context, r ref.Ref, opts ...ImageOpts) error { var opt imageOpt for _, optFn := range opts { @@ -239,7 +242,7 @@ func (rc *RegClient) ImageCheckBase(ctx context.Context, r ref.Ref, opts ...Imag } ma, ok := m.(manifest.Annotator) if !ok { - return fmt.Errorf("image does not support annotations, base image must be provided%.0w", types.ErrMissingAnnotation) + return fmt.Errorf("image does not support annotations, base image must be provided%.0w", errs.ErrMissingAnnotation) } annot, err := ma.GetAnnotations() if err != nil { @@ -248,7 +251,7 @@ func (rc *RegClient) ImageCheckBase(ctx context.Context, r ref.Ref, opts ...Imag if baseName, ok := annot[types.AnnotationBaseImageName]; ok { opt.checkBaseRef = baseName } else { - return fmt.Errorf("image does not have a base annotation, base image must be provided%.0w", types.ErrMissingAnnotation) + return fmt.Errorf("image does not have a base annotation, base image must be provided%.0w", errs.ErrMissingAnnotation) } if baseDig, ok := annot[types.AnnotationBaseImageDigest]; ok { opt.checkBaseDigest = baseDig @@ -283,7 +286,7 @@ func (rc *RegClient) ImageCheckBase(ctx context.Context, r ref.Ref, opts ...Imag "expected": expectDig.String(), }).Debug("base image digest changed") return fmt.Errorf("base digest changed, %s, expected %s, received %s%.0w", - baseR.CommonName(), expectDig.String(), baseMH.GetDescriptor().Digest.String(), types.ErrMismatch) + baseR.CommonName(), expectDig.String(), baseMH.GetDescriptor().Digest.String(), errs.ErrMismatch) } } @@ -381,7 +384,7 @@ func (rc *RegClient) ImageCheckBase(ctx context.Context, r ref.Ref, opts ...Imag "digest": baseLayers[i].Digest.String(), }).Debug("image layer changed") return fmt.Errorf("base layer changed, %s[%d], expected %s, received %s%.0w", - baseR.CommonName(), i, layers[i].Digest.String(), baseLayers[i].Digest.String(), types.ErrMismatch) + baseR.CommonName(), i, layers[i].Digest.String(), baseLayers[i].Digest.String(), errs.ErrMismatch) } } @@ -423,7 +426,7 @@ func (rc *RegClient) ImageCheckBase(ctx context.Context, r ref.Ref, opts ...Imag "history": baseConfOCI.History[i], }).Debug("image history changed") return fmt.Errorf("base history changed, %s[%d], expected %v, received %v%.0w", - baseR.CommonName(), i, confOCI.History[i], baseConfOCI.History[i], types.ErrMismatch) + baseR.CommonName(), i, confOCI.History[i], baseConfOCI.History[i], errs.ErrMismatch) } } @@ -460,7 +463,7 @@ func (rc *RegClient) ImageCopy(ctx context.Context, refSrc ref.Ref, refTgt ref.R defer tgtGCLocker.GCUnlock(refTgt) } // run the copy of manifests and blobs recursively - err = rc.imageCopyOpt(ctx, refSrc, refTgt, types.Descriptor{}, opt.child, []digest.Digest{}, &opt) + err = rc.imageCopyOpt(ctx, refSrc, refTgt, descriptor.Descriptor{}, opt.child, []digest.Digest{}, &opt) if err != nil { return err } @@ -475,7 +478,7 @@ func (rc *RegClient) ImageCopy(ctx context.Context, refSrc ref.Ref, refTgt ref.R } // imageCopyOpt is a thread safe copy of a manifest and nested content. -func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d types.Descriptor, child bool, parents []digest.Digest, opt *imageOpt) (err error) { +func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d descriptor.Descriptor, child bool, parents []digest.Digest, opt *imageOpt) (err error) { var mSrc, mTgt manifest.Manifest var sDig digest.Digest seenCB := func(error) {} @@ -595,14 +598,14 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re entrySrc := refSrc.SetDigest(dEntry.Digest.String()) entryTgt := refTgt.SetDigest(dEntry.Digest.String()) switch dEntry.MediaType { - case types.MediaTypeDocker1Manifest, types.MediaTypeDocker1ManifestSigned, - types.MediaTypeDocker2Manifest, types.MediaTypeDocker2ManifestList, - types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList: + case mediatype.Docker1Manifest, mediatype.Docker1ManifestSigned, + mediatype.Docker2Manifest, mediatype.Docker2ManifestList, + mediatype.OCI1Manifest, mediatype.OCI1ManifestList: // known manifest media type err = rc.imageCopyOpt(ctx, entrySrc, entryTgt, dEntry, true, parentsNew, opt) - case types.MediaTypeDocker2ImageConfig, types.MediaTypeOCI1ImageConfig, - types.MediaTypeDocker2LayerGzip, types.MediaTypeOCI1Layer, types.MediaTypeOCI1LayerGzip, - types.MediaTypeBuildkitCacheConfig: + case mediatype.Docker2ImageConfig, mediatype.OCI1ImageConfig, + mediatype.Docker2LayerGzip, mediatype.OCI1Layer, mediatype.OCI1LayerGzip, + mediatype.BuildkitCacheConfig: // known blob media type err = rc.imageCopyBlob(ctx, entrySrc, entryTgt, dEntry, opt, bOpt...) default: @@ -626,7 +629,7 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re return err } referrerTags = append(referrerTags, rl.Tags...) - descList := []types.Descriptor{} + descList := []descriptor.Descriptor{} if len(opt.referrerConfs) == 0 { descList = rl.Descriptors } else { @@ -648,7 +651,7 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re waitCount++ go func() { err := rc.imageCopyOpt(ctx, referrerSrc, referrerTgt, rDesc, true, parentsNew, opt) - if errors.Is(err, types.ErrLoopDetected) { + if errors.Is(err, errs.ErrLoopDetected) { // if a loop is detected, push the referrers copy to the end opt.mu.Lock() opt.finalFn = append(opt.finalFn, func(ctx context.Context) error { @@ -718,12 +721,12 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re tag := tag waitCount++ go func() { - err := rc.imageCopyOpt(ctx, refTagSrc, refTagTgt, types.Descriptor{}, false, parentsNew, opt) - if errors.Is(err, types.ErrLoopDetected) { + err := rc.imageCopyOpt(ctx, refTagSrc, refTagTgt, descriptor.Descriptor{}, false, parentsNew, opt) + if errors.Is(err, errs.ErrLoopDetected) { // if a loop is detected, push the digest tag copy back to the end opt.mu.Lock() opt.finalFn = append(opt.finalFn, func(ctx context.Context) error { - return rc.imageCopyOpt(ctx, refTagSrc, refTagTgt, types.Descriptor{}, false, []digest.Digest{}, opt) + return rc.imageCopyOpt(ctx, refTagSrc, refTagTgt, descriptor.Descriptor{}, false, []digest.Digest{}, opt) }) opt.mu.Unlock() waitCh <- nil @@ -781,7 +784,7 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re cd, err := mSrcImg.GetConfig() if err != nil { // docker schema v1 does not have a config object, ignore if it's missing - if !errors.Is(err, types.ErrUnsupportedMediaType) { + if !errors.Is(err, errs.ErrUnsupportedMediaType) { rc.log.WithFields(logrus.Fields{ "ref": refSrc.Reference, "err": err, @@ -897,7 +900,7 @@ func (rc *RegClient) imageCopyOpt(ctx context.Context, refSrc ref.Ref, refTgt re return nil } -func (rc *RegClient) imageCopyBlob(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d types.Descriptor, opt *imageOpt, bOpt ...BlobOpts) error { +func (rc *RegClient) imageCopyBlob(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d descriptor.Descriptor, opt *imageOpt, bOpt ...BlobOpts) error { seenCB, err := imageSeenOrWait(ctx, opt, "", d.Digest, []digest.Digest{}) if seenCB == nil { return err @@ -931,7 +934,7 @@ func imageSeenOrWait(ctx context.Context, opt *imageOpt, tag string, dig digest. // look for loops in parents for _, p := range parents { if key == tag+":"+p.String() { - return nil, types.ErrLoopDetected + return nil, errs.ErrLoopDetected } } // wait for copy to finish or context to cancel @@ -971,7 +974,7 @@ func imageSeenOrWait(ctx context.Context, opt *imageOpt, tag string, dig digest. // [OCI Layout]: https://github.com/opencontainers/image-spec/blob/master/image-layout.md func (rc *RegClient) ImageExport(ctx context.Context, r ref.Ref, outStream io.Writer, opts ...ImageOpts) error { if !r.IsSet() { - return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } var ociIndex v1.Index @@ -1026,7 +1029,7 @@ func (rc *RegClient) ImageExport(ctx context.Context, r ref.Ref, outStream io.Wr // generate/write an OCI index ociIndex.Versioned = v1.IndexSchemaVersion - ociIndex.Manifests = []types.Descriptor{mDesc} // initialize with the descriptor to the manifest list + ociIndex.Manifests = []descriptor.Descriptor{mDesc} // initialize with the descriptor to the manifest list err = twd.tarWriteFileJSON(ociIndexFilename, ociIndex) if err != nil { return err @@ -1049,7 +1052,7 @@ func (rc *RegClient) ImageExport(ctx context.Context, r ref.Ref, outStream io.Wr RepoTags: []string{refTag.CommonName()}, Config: tarOCILayoutDescPath(conf), Layers: []string{}, - LayerSources: map[digest.Digest]types.Descriptor{}, + LayerSources: map[digest.Digest]descriptor.Descriptor{}, } dl, err := mi.GetLayers() if err != nil { @@ -1077,14 +1080,14 @@ func (rc *RegClient) ImageExport(ctx context.Context, r ref.Ref, outStream io.Wr } // imageExportDescriptor pulls a manifest or blob, outputs to a tar file, and recursively processes any nested manifests or blobs -func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc types.Descriptor, twd *tarWriteData) error { +func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc descriptor.Descriptor, twd *tarWriteData) error { tarFilename := tarOCILayoutDescPath(desc) if twd.files[tarFilename] { // blob has already been imported into tar, skip return nil } switch desc.MediaType { - case types.MediaTypeDocker1Manifest, types.MediaTypeDocker1ManifestSigned, types.MediaTypeDocker2Manifest, types.MediaTypeOCI1Manifest: + case mediatype.Docker1Manifest, mediatype.Docker1ManifestSigned, mediatype.Docker2Manifest, mediatype.OCI1Manifest: // Handle single platform manifests // retrieve manifest m, err := rc.ManifestGet(ctx, r, WithManifestDesc(desc)) @@ -1093,7 +1096,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc } mi, ok := m.(manifest.Imager) if !ok { - return fmt.Errorf("manifest doesn't support image methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest doesn't support image methods%.0w", errs.ErrUnsupportedMediaType) } // write manifest body by digest mBody, err := m.RawBody() @@ -1112,7 +1115,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc // add config confD, err := mi.GetConfig() // ignore unsupported media type errors - if err != nil && !errors.Is(err, types.ErrUnsupportedMediaType) { + if err != nil && !errors.Is(err, errs.ErrUnsupportedMediaType) { return err } if err == nil { @@ -1125,7 +1128,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc // loop over layers layerDL, err := mi.GetLayers() // ignore unsupported media type errors - if err != nil && !errors.Is(err, types.ErrUnsupportedMediaType) { + if err != nil && !errors.Is(err, errs.ErrUnsupportedMediaType) { return err } if err == nil { @@ -1137,7 +1140,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc } } - case types.MediaTypeDocker2ManifestList, types.MediaTypeOCI1ManifestList: + case mediatype.Docker2ManifestList, mediatype.OCI1ManifestList: // handle OCI index and Docker manifest list // retrieve manifest m, err := rc.ManifestGet(ctx, r, WithManifestDesc(desc)) @@ -1146,7 +1149,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc } mi, ok := m.(manifest.Indexer) if !ok { - return fmt.Errorf("manifest doesn't support index methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest doesn't support index methods%.0w", errs.ErrUnsupportedMediaType) } // write manifest body by digest mBody, err := m.RawBody() @@ -1200,7 +1203,7 @@ func (rc *RegClient) imageExportDescriptor(ctx context.Context, r ref.Ref, desc // ImageImport pushes an image from a tar file (ImageExport) to a registry. func (rc *RegClient) ImageImport(ctx context.Context, r ref.Ref, rs io.ReadSeeker, opts ...ImageOpts) error { if !r.IsSetRepo() { - return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } var opt imageOpt for _, optFn := range opts { @@ -1223,7 +1226,7 @@ func (rc *RegClient) ImageImport(ctx context.Context, r ref.Ref, rs io.ReadSeeke // process tar file looking for oci-layout and index.json, load manifests/blobs on success err := trd.tarReadAll(rs) - if err != nil && errors.Is(err, types.ErrNotFound) && trd.dockerManifestFound { + if err != nil && errors.Is(err, errs.ErrNotFound) && trd.dockerManifestFound { // import failed but manifest.json found, fall back to manifest.json processing // add handlers for the docker manifest layers rc.imageImportDockerAddLayerHandlers(ctx, r, trd) @@ -1254,7 +1257,7 @@ func (rc *RegClient) ImageImport(ctx context.Context, r ref.Ref, rs io.ReadSeeke return nil } -func (rc *RegClient) imageImportBlob(ctx context.Context, r ref.Ref, desc types.Descriptor, trd *tarReadData) error { +func (rc *RegClient) imageImportBlob(ctx context.Context, r ref.Ref, desc descriptor.Descriptor, trd *tarReadData) error { // skip if blob already exists _, err := rc.BlobHead(ctx, r, desc) if err == nil { @@ -1314,13 +1317,13 @@ func (rc *RegClient) imageImportDockerAddLayerHandlers(ctx context.Context, r re // make a docker v2 manifest from first json array entry (can only tag one image) trd.dockerManifest.SchemaVersion = 2 - trd.dockerManifest.MediaType = types.MediaTypeDocker2Manifest - trd.dockerManifest.Layers = make([]types.Descriptor, len(trd.dockerManifestList[index].Layers)) + trd.dockerManifest.MediaType = mediatype.Docker2Manifest + trd.dockerManifest.Layers = make([]descriptor.Descriptor, len(trd.dockerManifestList[index].Layers)) // add handler for config trd.handlers[filepath.Clean(trd.dockerManifestList[index].Config)] = func(header *tar.Header, trd *tarReadData) error { // upload blob, digest is unknown - d, err := rc.BlobPut(ctx, r, types.Descriptor{Size: header.Size}, trd.tr) + d, err := rc.BlobPut(ctx, r, descriptor.Descriptor{Size: header.Size}, trd.tr) if err != nil { return err } @@ -1328,7 +1331,7 @@ func (rc *RegClient) imageImportDockerAddLayerHandlers(ctx context.Context, r re if od, ok := trd.dockerManifestList[index].LayerSources[d.Digest]; ok { trd.dockerManifest.Config = od } else { - d.MediaType = types.MediaTypeDocker2ImageConfig + d.MediaType = mediatype.Docker2ImageConfig trd.dockerManifest.Config = d } return nil @@ -1343,7 +1346,7 @@ func (rc *RegClient) imageImportDockerAddLayerHandlers(ctx context.Context, r re return err } // upload blob, digest and size is unknown - d, err := rc.BlobPut(ctx, r, types.Descriptor{}, gzipR) + d, err := rc.BlobPut(ctx, r, descriptor.Descriptor{}, gzipR) if err != nil { return err } @@ -1351,7 +1354,7 @@ func (rc *RegClient) imageImportDockerAddLayerHandlers(ctx context.Context, r re if od, ok := trd.dockerManifestList[index].LayerSources[d.Digest]; ok { trd.dockerManifest.Layers[i] = od } else { - d.MediaType = types.MediaTypeDocker2LayerGzip + d.MediaType = mediatype.Docker2LayerGzip trd.dockerManifest.Layers[i] = d } return nil @@ -1427,7 +1430,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref // cache the manifest to avoid needing to pull again later, this is used if index.json is a wrapper around some other manifest trd.manifests[m.GetDescriptor().Digest] = m - handleManifest := func(d types.Descriptor, child bool) { + handleManifest := func(d descriptor.Descriptor, child bool) { filename := tarOCILayoutDescPath(d) if !trd.processed[filename] && trd.handlers[filename] == nil { trd.handlers[filename] = func(header *tar.Header, trd *tarReadData) error { @@ -1436,18 +1439,18 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref return err } switch d.MediaType { - case types.MediaTypeDocker1Manifest, types.MediaTypeDocker1ManifestSigned, - types.MediaTypeDocker2Manifest, types.MediaTypeDocker2ManifestList, - types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList: + case mediatype.Docker1Manifest, mediatype.Docker1ManifestSigned, + mediatype.Docker2Manifest, mediatype.Docker2ManifestList, + mediatype.OCI1Manifest, mediatype.OCI1ManifestList: // known manifest media types md, err := manifest.New(manifest.WithDesc(d), manifest.WithRaw(b)) if err != nil { return err } return rc.imageImportOCIHandleManifest(ctx, r, md, trd, true, child) - case types.MediaTypeDocker2ImageConfig, types.MediaTypeOCI1ImageConfig, - types.MediaTypeDocker2LayerGzip, types.MediaTypeOCI1Layer, types.MediaTypeOCI1LayerGzip, - types.MediaTypeBuildkitCacheConfig: + case mediatype.Docker2ImageConfig, mediatype.OCI1ImageConfig, + mediatype.Docker2LayerGzip, mediatype.OCI1Layer, mediatype.OCI1LayerGzip, + mediatype.BuildkitCacheConfig: // known blob media types return rc.imageImportBlob(ctx, r, d, trd) default: @@ -1465,7 +1468,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref if !push { mi, ok := m.(manifest.Indexer) if !ok { - return fmt.Errorf("manifest doesn't support image methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest doesn't support image methods%.0w", errs.ErrUnsupportedMediaType) } // for root index, add handler for matching reference (or only reference) dl, err := mi.GetManifestList() @@ -1473,7 +1476,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref return err } // locate the digest in the index - var d types.Descriptor + var d descriptor.Descriptor if len(dl) == 1 { d = dl[0] } else if r.Digest != "" { @@ -1516,7 +1519,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref // for index/manifest lists, add handlers for each embedded manifest mi, ok := m.(manifest.Indexer) if !ok { - return fmt.Errorf("manifest doesn't support index methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest doesn't support index methods%.0w", errs.ErrUnsupportedMediaType) } dl, err := mi.GetManifestList() if err != nil { @@ -1529,14 +1532,14 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref // else if a single image/manifest mi, ok := m.(manifest.Imager) if !ok { - return fmt.Errorf("manifest doesn't support image methods%.0w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest doesn't support image methods%.0w", errs.ErrUnsupportedMediaType) } // add handler for the config descriptor if it's defined cd, err := mi.GetConfig() if err == nil { filename := tarOCILayoutDescPath(cd) if !trd.processed[filename] && trd.handlers[filename] == nil { - func(cd types.Descriptor) { + func(cd descriptor.Descriptor) { trd.handlers[filename] = func(header *tar.Header, trd *tarReadData) error { return rc.imageImportBlob(ctx, r, cd, trd) } @@ -1551,7 +1554,7 @@ func (rc *RegClient) imageImportOCIHandleManifest(ctx context.Context, r ref.Ref for _, d := range layers { filename := tarOCILayoutDescPath(d) if !trd.processed[filename] && trd.handlers[filename] == nil { - func(d types.Descriptor) { + func(d descriptor.Descriptor) { trd.handlers[filename] = func(header *tar.Header, trd *tarReadData) error { return rc.imageImportBlob(ctx, r, d, trd) } @@ -1700,7 +1703,7 @@ func (trd *tarReadData) tarReadAll(rs io.ReadSeeker) error { } // if entire file read without adding a new handler, fail if !trd.handleAdded { - return fmt.Errorf("unable to read all files from tar: %w", types.ErrNotFound) + return fmt.Errorf("unable to read all files from tar: %w", errs.ErrNotFound) } } } @@ -1793,6 +1796,6 @@ func (td *tarWriteData) tarWriteFileJSON(filename string, data interface{}) erro return nil } -func tarOCILayoutDescPath(d types.Descriptor) string { +func tarOCILayoutDescPath(d descriptor.Descriptor) string { return filepath.Clean(fmt.Sprintf("blobs/%s/%s", d.Digest.Algorithm(), d.Digest.Encoded())) } diff --git a/image_test.go b/image_test.go index 71d171d..104a833 100644 --- a/image_test.go +++ b/image_test.go @@ -17,7 +17,7 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/rwfs" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -72,17 +72,17 @@ func TestImageCheckBase(t *testing.T) { { name: "missing annotation", r: r1, - expectErr: types.ErrMissingAnnotation, + expectErr: errs.ErrMissingAnnotation, }, { name: "annotation v2", r: r2, - expectErr: types.ErrMismatch, + expectErr: errs.ErrMismatch, }, { name: "annotation v3", r: r3, - expectErr: types.ErrMismatch, + expectErr: errs.ErrMismatch, }, { name: "manual v2, b1", @@ -93,13 +93,13 @@ func TestImageCheckBase(t *testing.T) { name: "manual v2, b2", r: r2, opts: []ImageOpts{ImageWithCheckBaseRef(rb2.CommonName())}, - expectErr: types.ErrMismatch, + expectErr: errs.ErrMismatch, }, { name: "manual v2, b3", r: r2, opts: []ImageOpts{ImageWithCheckBaseRef(rb3.CommonName())}, - expectErr: types.ErrMismatch, + expectErr: errs.ErrMismatch, }, { name: "manual v3, b1", @@ -203,7 +203,7 @@ func TestCopy(t *testing.T) { name: "ocidir to read-only registry", src: "ocidir://./testdata/testrepo:v1", tgt: tsROHost + "/dest-ocidir:v1", - expectErr: types.ErrHTTPStatus, + expectErr: errs.ErrHTTPStatus, }, { name: "ocidir to ocidir", diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 6015ca3..3da1461 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -16,7 +16,7 @@ import ( "github.com/sirupsen/logrus" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) type charLU byte @@ -490,7 +490,7 @@ func (b *BasicHandler) ProcessChallenge(c Challenge) error { func (b *BasicHandler) GenerateAuth() (string, error) { cred := b.credsFn(b.host) if cred.User == "" || cred.Password == "" { - return "", fmt.Errorf("no credentials available: %w", types.ErrHTTPUnauthorized) + return "", fmt.Errorf("no credentials available: %w", errs.ErrHTTPUnauthorized) } auth := base64.StdEncoding.EncodeToString([]byte(cred.User + ":" + cred.Password)) return fmt.Sprintf("Basic %s", auth), nil @@ -608,14 +608,14 @@ func (b *BearerHandler) GenerateAuth() (string, error) { if err := b.tryPost(); err == nil { return fmt.Sprintf("Bearer %s", b.token.Token), nil } else if err != ErrUnauthorized { - return "", fmt.Errorf("failed to request auth token (post): %w%.0w", err, types.ErrHTTPUnauthorized) + return "", fmt.Errorf("failed to request auth token (post): %w%.0w", err, errs.ErrHTTPUnauthorized) } // attempt a get (with basic auth if user/pass available) if err := b.tryGet(); err == nil { return fmt.Sprintf("Bearer %s", b.token.Token), nil } else if err != ErrUnauthorized { - return "", fmt.Errorf("failed to request auth token (get): %w%.0w", err, types.ErrHTTPUnauthorized) + return "", fmt.Errorf("failed to request auth token (get): %w%.0w", err, errs.ErrHTTPUnauthorized) } return "", ErrUnauthorized diff --git a/internal/auth/error.go b/internal/auth/error.go index 72572f5..2ba7e3b 100644 --- a/internal/auth/error.go +++ b/internal/auth/error.go @@ -1,24 +1,40 @@ package auth import ( - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) var ( // ErrEmptyChallenge indicates an issue with the received challenge in the WWW-Authenticate header - ErrEmptyChallenge = types.ErrEmptyChallenge + // + // Deprecated: replace with [errs.ErrEmptyChallenge]. + ErrEmptyChallenge = errs.ErrEmptyChallenge // ErrInvalidChallenge indicates an issue with the received challenge in the WWW-Authenticate header - ErrInvalidChallenge = types.ErrInvalidChallenge + // + // Deprecated: replace with [errs.ErrInvalidChallenge]. + ErrInvalidChallenge = errs.ErrInvalidChallenge // ErrNoNewChallenge indicates a challenge update did not result in any change - ErrNoNewChallenge = types.ErrNoNewChallenge + // + // Deprecated: replace with [errs.ErrNoNewChallenge]. + ErrNoNewChallenge = errs.ErrNoNewChallenge // ErrNotFound indicates no credentials found for basic auth - ErrNotFound = types.ErrNotFound + // + // Deprecated: replace with [errs.ErrNotFound]. + ErrNotFound = errs.ErrNotFound // ErrNotImplemented returned when method has not been implemented yet - ErrNotImplemented = types.ErrNotImplemented + // + // Deprecated: replace with [errs.ErrNotImplemented]. + ErrNotImplemented = errs.ErrNotImplemented // ErrParseFailure indicates the WWW-Authenticate header could not be parsed - ErrParseFailure = types.ErrParsingFailed + // + // Deprecated: replace with [errs.ErrParseFailure]. + ErrParseFailure = errs.ErrParsingFailed // ErrUnauthorized request was not authorized - ErrUnauthorized = types.ErrHTTPUnauthorized + // + // Deprecated: replace with [errs.ErrUnauthorized]. + ErrUnauthorized = errs.ErrHTTPUnauthorized // ErrUnsupported indicates the request was unsupported - ErrUnsupported = types.ErrUnsupported + // + // Deprecated: replace with [errs.ErrUnsupported]. + ErrUnsupported = errs.ErrUnsupported ) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index ca0653a..4bc4523 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) type Cache[k comparable, v any] struct { @@ -105,7 +105,7 @@ func (c *Cache[k, v]) Set(key k, val v) { func (c *Cache[k, v]) Get(key k) (v, error) { if c == nil { var val v - return val, types.ErrNotFound + return val, errs.ErrNotFound } c.mu.Lock() defer c.mu.Unlock() @@ -119,7 +119,7 @@ func (c *Cache[k, v]) Get(key k) (v, error) { } } var val v - return val, types.ErrNotFound + return val, errs.ErrNotFound } func (c *Cache[k, v]) prune() { diff --git a/internal/httplink/httplink.go b/internal/httplink/httplink.go index 777e656..b747cfd 100644 --- a/internal/httplink/httplink.go +++ b/internal/httplink/httplink.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) type Links []Link @@ -75,7 +75,7 @@ func Parse(headers []string) (Links, error) { // noop } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } case "uri": // parse tokens until space or comma @@ -90,7 +90,7 @@ func Parse(headers []string) (Links, error) { endLink() } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } case "uriQuoted": // parse tokens until quote @@ -109,7 +109,7 @@ func Parse(headers []string) (Links, error) { // noop } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } case "parmName": if len(pnb) > 0 && b == '=' { @@ -122,14 +122,14 @@ func Parse(headers []string) (Links, error) { // noop } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } case "parmNameStar": if b == '=' { state = "parmValue" } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } case "parmValue": if len(pvb) == 0 { @@ -139,7 +139,7 @@ func Parse(headers []string) (Links, error) { state = "parmValueQuoted" } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } } else { if charLUs[b]&isToken != 0 { @@ -156,7 +156,7 @@ func Parse(headers []string) (Links, error) { endLink() } else { // unknown character - return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unknown character in position %d of %s: %w", i, h, errs.ErrParsingFailed) } } case "parmValueQuoted": @@ -178,7 +178,7 @@ func Parse(headers []string) (Links, error) { case "init": // noop default: - return nil, fmt.Errorf("unexpected end state %s for header %s: %w", state, h, types.ErrParsingFailed) + return nil, fmt.Errorf("unexpected end state %s for header %s: %w", state, h, errs.ErrParsingFailed) } } @@ -192,5 +192,5 @@ func (links Links) Get(parm, val string) (Link, error) { return link, nil } } - return Link{}, types.ErrNotFound + return Link{}, errs.ErrNotFound } diff --git a/internal/httplink/httplink_test.go b/internal/httplink/httplink_test.go index 61b6477..6c8283b 100644 --- a/internal/httplink/httplink_test.go +++ b/internal/httplink/httplink_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestParseErr(t *testing.T) { @@ -122,7 +122,7 @@ func TestParseGet(t *testing.T) { } link, err := links.Get(tt.parm, tt.val) if tt.expectMissing { - if err == nil || !errors.Is(err, types.ErrNotFound) { + if err == nil || !errors.Is(err, errs.ErrNotFound) { t.Errorf("did not find missing error: %v, %v", link, err) } return diff --git a/internal/limitread/limitread.go b/internal/limitread/limitread.go index 99f55a2..77115ef 100644 --- a/internal/limitread/limitread.go +++ b/internal/limitread/limitread.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) type LimitRead struct { @@ -15,7 +15,7 @@ type LimitRead struct { func (lr *LimitRead) Read(p []byte) (int, error) { if lr.Limit < 0 { - return 0, fmt.Errorf("read limit exceeded%.0w", types.ErrSizeLimitExceeded) + return 0, fmt.Errorf("read limit exceeded%.0w", errs.ErrSizeLimitExceeded) } if int64(len(p)) > lr.Limit+1 { p = p[0 : lr.Limit+1] @@ -23,7 +23,7 @@ func (lr *LimitRead) Read(p []byte) (int, error) { n, err := lr.Reader.Read(p) lr.Limit -= int64(n) if lr.Limit < 0 { - return n, fmt.Errorf("read limit exceeded%.0w", types.ErrSizeLimitExceeded) + return n, fmt.Errorf("read limit exceeded%.0w", errs.ErrSizeLimitExceeded) } return n, err } diff --git a/internal/limitread/limitread_test.go b/internal/limitread/limitread_test.go index 31253a6..e6aa870 100644 --- a/internal/limitread/limitread_test.go +++ b/internal/limitread/limitread_test.go @@ -6,7 +6,7 @@ import ( "io" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestLimitRead(t *testing.T) { @@ -64,7 +64,7 @@ func TestLimitRead(t *testing.T) { limit: 9, src: byte10, try: 10, - expectErr: types.ErrSizeLimitExceeded, + expectErr: errs.ErrSizeLimitExceeded, }, } for _, tc := range tt { diff --git a/internal/reghttp/http.go b/internal/reghttp/http.go index 1df3893..e40a7b5 100644 --- a/internal/reghttp/http.go +++ b/internal/reghttp/http.go @@ -31,7 +31,7 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/auth" "github.com/regclient/regclient/internal/throttle" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/warning" ) @@ -266,7 +266,7 @@ func (resp *clientResp) Next() error { if err != nil { return err } - return types.ErrAllRequestsFailed + return errs.ErrAllRequestsFailed } if curHost >= len(hosts) { curHost = 0 @@ -295,14 +295,14 @@ func (resp *clientResp) Next() error { var err error if !okAPI { dropHost = true - return fmt.Errorf("failed looking up api \"%s\" for host \"%s\": %w", h.config.API, h.config.Name, types.ErrAPINotFound) + return fmt.Errorf("failed looking up api \"%s\" for host \"%s\": %w", h.config.API, h.config.Name, errs.ErrAPINotFound) } if api.Method == "HEAD" && h.config.APIOpts != nil { var disableHead bool disableHead, err = strconv.ParseBool(h.config.APIOpts["disableHead"]) if err == nil && disableHead { dropHost = true - return fmt.Errorf("head requests disabled for host \"%s\": %w", h.config.Name, types.ErrUnsupportedAPI) + return fmt.Errorf("head requests disabled for host \"%s\": %w", h.config.Name, errs.ErrUnsupportedAPI) } } @@ -349,7 +349,7 @@ func (resp *clientResp) Next() error { }).Warn("Sleeping for backoff") select { case <-resp.ctx.Done(): - return types.ErrCanceled + return errs.ErrCanceled case <-time.After(sleepTime): } } @@ -402,7 +402,7 @@ func (resp *clientResp) Next() error { // add auth headers err = hAuth.UpdateRequest(httpReq) if err != nil { - if errors.Is(err, types.ErrHTTPUnauthorized) { + if errors.Is(err, errs.ErrHTTPUnauthorized) { dropHost = true } else { backoff = true @@ -453,7 +453,7 @@ func (resp *clientResp) Next() error { err = fmt.Errorf("authentication handler unavailable") } if err != nil { - if errors.Is(err, types.ErrEmptyChallenge) || errors.Is(err, types.ErrNoNewChallenge) || errors.Is(err, types.ErrHTTPUnauthorized) { + if errors.Is(err, errs.ErrEmptyChallenge) || errors.Is(err, errs.ErrNoNewChallenge) || errors.Is(err, errs.ErrHTTPUnauthorized) { c.log.WithFields(logrus.Fields{ "URL": u.String(), "Err": err, @@ -535,7 +535,7 @@ func (resp *clientResp) Next() error { } } // when error does not allow retries, abort with the last known err value - if err != nil && errors.Is(loopErr, types.ErrNotRetryable) { + if err != nil && errors.Is(loopErr, errs.ErrNotRetryable) { return err } err = loopErr @@ -556,7 +556,7 @@ func (resp *clientResp) Read(b []byte) (int, error) { return 0, io.EOF } if resp.resp == nil { - return 0, types.ErrNotFound + return 0, errs.ErrNotFound } // perform the read i, err := resp.reader.Read(b) @@ -595,7 +595,7 @@ func (resp *clientResp) Read(b []byte) (int, error) { }).Warn("Digest mismatch") _ = resp.backoffSet() resp.done = true - return i, fmt.Errorf("%w, expected %s, computed %s", types.ErrDigestMismatch, + return i, fmt.Errorf("%w, expected %s, computed %s", errs.ErrDigestMismatch, resp.digest.String(), resp.digester.Digest().String()) } } @@ -612,7 +612,7 @@ func (resp *clientResp) Close() error { resp.throttle = nil } if resp.resp == nil { - return types.ErrNotFound + return errs.ErrNotFound } if !resp.done { resp.backoffClear() @@ -695,7 +695,7 @@ func (resp *clientResp) backoffSet() error { ch.backoffUntil = time.Now().Add(sleepTime) if ch.backoffCur >= c.retryLimit { - return fmt.Errorf("%w: backoffs %d", types.ErrBackoffLimit, ch.backoffCur) + return fmt.Errorf("%w: backoffs %d", errs.ErrBackoffLimit, ch.backoffCur) } return nil @@ -831,15 +831,15 @@ func (ch *clientHost) AuthCreds() func(h string) auth.Cred { func HTTPError(statusCode int) error { switch statusCode { case 401: - return fmt.Errorf("%w [http %d]", types.ErrHTTPUnauthorized, statusCode) + return fmt.Errorf("%w [http %d]", errs.ErrHTTPUnauthorized, statusCode) case 403: - return fmt.Errorf("%w [http %d]", types.ErrHTTPUnauthorized, statusCode) + return fmt.Errorf("%w [http %d]", errs.ErrHTTPUnauthorized, statusCode) case 404: - return fmt.Errorf("%w [http %d]", types.ErrNotFound, statusCode) + return fmt.Errorf("%w [http %d]", errs.ErrNotFound, statusCode) case 429: - return fmt.Errorf("%w [http %d]", types.ErrHTTPRateLimit, statusCode) + return fmt.Errorf("%w [http %d]", errs.ErrHTTPRateLimit, statusCode) default: - return fmt.Errorf("%w: %s [http %d]", types.ErrHTTPStatus, http.StatusText(statusCode), statusCode) + return fmt.Errorf("%w: %s [http %d]", errs.ErrHTTPStatus, http.StatusText(statusCode), statusCode) } } diff --git a/internal/reghttp/http_test.go b/internal/reghttp/http_test.go index 475744d..71b044e 100644 --- a/internal/reghttp/http_test.go +++ b/internal/reghttp/http_test.go @@ -19,7 +19,7 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/auth" "github.com/regclient/regclient/internal/reqresp" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/warning" ) @@ -893,7 +893,7 @@ func TestRegHttp(t *testing.T) { body, err := io.ReadAll(resp) if err == nil { t.Errorf("body read unexpectedly succeeded: %s", body) - } else if !errors.Is(err, types.ErrDigestMismatch) { + } else if !errors.Is(err, errs.ErrDigestMismatch) { t.Errorf("unexpected error from digest mismatch: %v", err) } err = resp.Close() @@ -926,7 +926,7 @@ func TestRegHttp(t *testing.T) { body, err := io.ReadAll(resp) if err == nil { t.Errorf("body read unexpectedly succeeded: %s", body) - } else if !errors.Is(err, types.ErrDigestMismatch) { + } else if !errors.Is(err, errs.ErrDigestMismatch) { t.Errorf("unexpected error from digest mismatch: %v", err) } err = resp.Close() @@ -1064,8 +1064,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success with bad password") - } else if !errors.Is(err, auth.ErrUnauthorized) { - t.Errorf("expected error %v, received error %v", auth.ErrUnauthorized, err) + } else if !errors.Is(err, errs.ErrHTTPUnauthorized) { + t.Errorf("expected error %v, received error %v", errs.ErrHTTPUnauthorized, err) } }) t.Run("Bad auth", func(t *testing.T) { @@ -1086,8 +1086,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success with bad auth header") - } else if !errors.Is(err, types.ErrParsingFailed) { - t.Errorf("expected error %v, received error %v", types.ErrParsingFailed, err) + } else if !errors.Is(err, errs.ErrParsingFailed) { + t.Errorf("expected error %v, received error %v", errs.ErrParsingFailed, err) } }) t.Run("Missing auth", func(t *testing.T) { @@ -1108,8 +1108,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success with missing auth header") - } else if !errors.Is(err, types.ErrEmptyChallenge) { - t.Errorf("expected error %v, received error %v", types.ErrEmptyChallenge, err) + } else if !errors.Is(err, errs.ErrEmptyChallenge) { + t.Errorf("expected error %v, received error %v", errs.ErrEmptyChallenge, err) } }) // test repoauth @@ -1408,8 +1408,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success on get for missing manifest") - } else if !errors.Is(err, types.ErrNotFound) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrNotFound, err) + } else if !errors.Is(err, errs.ErrNotFound) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrNotFound, err) } }) t.Run("Forbidden", func(t *testing.T) { @@ -1430,8 +1430,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success on get for missing manifest") - } else if !errors.Is(err, types.ErrHTTPUnauthorized) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrHTTPUnauthorized, err) + } else if !errors.Is(err, errs.ErrHTTPUnauthorized) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrHTTPUnauthorized, err) } }) t.Run("Bad GW", func(t *testing.T) { @@ -1452,8 +1452,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success on get for missing manifest") - } else if !errors.Is(err, types.ErrHTTPStatus) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrHTTPStatus, err) + } else if !errors.Is(err, errs.ErrHTTPStatus) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrHTTPStatus, err) } }) t.Run("GW Timeout", func(t *testing.T) { @@ -1474,8 +1474,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success on get for missing manifest") - } else if !errors.Is(err, types.ErrHTTPStatus) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrHTTPStatus, err) + } else if !errors.Is(err, errs.ErrHTTPStatus) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrHTTPStatus, err) } }) t.Run("Server error", func(t *testing.T) { @@ -1496,8 +1496,8 @@ func TestRegHttp(t *testing.T) { if err == nil { resp.Close() t.Fatalf("unexpected success on get for missing manifest") - } else if !errors.Is(err, types.ErrHTTPStatus) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrHTTPStatus, err) + } else if !errors.Is(err, errs.ErrHTTPStatus) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrHTTPStatus, err) } }) // test context expire during retries diff --git a/internal/strparse/strparse.go b/internal/strparse/strparse.go index 64fa7f8..f67fdaf 100644 --- a/internal/strparse/strparse.go +++ b/internal/strparse/strparse.go @@ -4,7 +4,7 @@ package strparse import ( "fmt" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) // SplitCSKV splits a comma separated key=value list into a map @@ -85,7 +85,7 @@ func SplitCSKV(s string) (map[string]string, error) { case "val", "key": procKV() default: - return nil, fmt.Errorf("string parsing failed, end state: %s%.0w", state, types.ErrParsingFailed) + return nil, fmt.Errorf("string parsing failed, end state: %s%.0w", state, errs.ErrParsingFailed) } return result, nil } diff --git a/internal/strparse/strparse_test.go b/internal/strparse/strparse_test.go index b5d25e3..c164059 100644 --- a/internal/strparse/strparse_test.go +++ b/internal/strparse/strparse_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestSplitCSKV(t *testing.T) { @@ -60,22 +60,22 @@ func TestSplitCSKV(t *testing.T) { { name: "errEscapeKey", str: "a\\", - err: types.ErrParsingFailed, + err: errs.ErrParsingFailed, }, { name: "errEscapeVal", str: "a=x\\", - err: types.ErrParsingFailed, + err: errs.ErrParsingFailed, }, { name: "errQuoteKey", str: "a\"", - err: types.ErrParsingFailed, + err: errs.ErrParsingFailed, }, { name: "errQuoteVal", str: "a=b\"", - err: types.ErrParsingFailed, + err: errs.ErrParsingFailed, }, } for _, tc := range tt { diff --git a/manifest.go b/manifest.go index c05793c..2aa767e 100644 --- a/manifest.go +++ b/manifest.go @@ -5,13 +5,14 @@ import ( "fmt" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/ref" ) type manifestOpt struct { - d types.Descriptor + d descriptor.Descriptor schemeOpts []scheme.ManifestOpts requireDigest bool } @@ -44,7 +45,7 @@ func WithManifestChild() ManifestOpts { // WithManifestDesc includes the descriptor for ManifestGet. // This is used to automatically extract a Data field if available. -func WithManifestDesc(d types.Descriptor) ManifestOpts { +func WithManifestDesc(d descriptor.Descriptor) ManifestOpts { return func(opts *manifestOpt) { opts.d = d } @@ -62,7 +63,7 @@ func WithManifestRequireDigest() ManifestOpts { // All tags pointing to the manifest will be deleted. func (rc *RegClient) ManifestDelete(ctx context.Context, r ref.Ref, opts ...ManifestOpts) error { if !r.IsSet() { - return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}} for _, fn := range opts { @@ -78,7 +79,7 @@ func (rc *RegClient) ManifestDelete(ctx context.Context, r ref.Ref, opts ...Mani // ManifestGet retrieves a manifest. func (rc *RegClient) ManifestGet(ctx context.Context, r ref.Ref, opts ...ManifestOpts) (manifest.Manifest, error) { if !r.IsSet() { - return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}} for _, fn := range opts { @@ -105,7 +106,7 @@ func (rc *RegClient) ManifestGet(ctx context.Context, r ref.Ref, opts ...Manifes // ManifestHead queries for the existence of a manifest and returns metadata (digest, media-type, size). func (rc *RegClient) ManifestHead(ctx context.Context, r ref.Ref, opts ...ManifestOpts) (manifest.Manifest, error) { if !r.IsSet() { - return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}} for _, fn := range opts { @@ -129,7 +130,7 @@ func (rc *RegClient) ManifestHead(ctx context.Context, r ref.Ref, opts ...Manife // Any descriptors referenced by the manifest typically need to be pushed first. func (rc *RegClient) ManifestPut(ctx context.Context, r ref.Ref, m manifest.Manifest, opts ...ManifestOpts) error { if !r.IsSetRepo() { - return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}} for _, fn := range opts { diff --git a/manifest_test.go b/manifest_test.go index efb6192..3e87b5d 100644 --- a/manifest_test.go +++ b/manifest_test.go @@ -18,9 +18,11 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -35,14 +37,14 @@ func TestManifest(t *testing.T) { digest1 := digest.FromString("example1") digest2 := digest.FromString("example2") m := schema2.Manifest{ - Config: types.Descriptor{ - MediaType: types.MediaTypeDocker2ImageConfig, + Config: descriptor.Descriptor{ + MediaType: mediatype.Docker2ImageConfig, Size: 8, Digest: digest1, }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeDocker2LayerGzip, + MediaType: mediatype.Docker2LayerGzip, Size: 8, Digest: digest2, }, @@ -67,7 +69,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -83,7 +85,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -99,7 +101,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, }, @@ -114,7 +116,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -130,7 +132,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, }, }, }, @@ -144,7 +146,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -223,7 +225,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestGet: %v", err) } - if manifest.GetMediaType(mGet) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mGet) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mGet)) } if mGet.GetDescriptor().Digest != mDigest { @@ -239,7 +241,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestHead: %v", err) } - if manifest.GetMediaType(mHead) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mHead) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mHead)) } if mHead.GetDescriptor().Digest != mDigest { @@ -255,7 +257,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestHead: %v", err) } - if manifest.GetMediaType(mHead) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mHead) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mHead)) } if mHead.GetDescriptor().Digest != mDigest { @@ -270,8 +272,8 @@ func TestManifest(t *testing.T) { mNohead, err := rc.ManifestHead(ctx, noheadRef) if err == nil { t.Errorf("Unexpected successful head on \"no head\" registry: %v", mNohead) - } else if !errors.Is(err, types.ErrUnsupportedAPI) { - t.Errorf("Expected error, expected %v, received %v", types.ErrUnsupportedAPI, err) + } else if !errors.Is(err, errs.ErrUnsupportedAPI) { + t.Errorf("Expected error, expected %v, received %v", errs.ErrUnsupportedAPI, err) } }) t.Run("Get No Head", func(t *testing.T) { @@ -283,7 +285,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestGet: %v", err) } - if manifest.GetMediaType(mNohead) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mNohead) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mNohead)) } if mNohead.GetDescriptor().Digest != mDigest { @@ -306,8 +308,8 @@ func TestManifest(t *testing.T) { if err != nil { t.Errorf("Failed creating getRef: %v", err) } - d := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + d := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(mLen), Digest: mDigest, Data: mBody, @@ -329,8 +331,8 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed creating getRef: %v", err) } - d := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + d := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(mLen), Digest: mDigest, Data: []byte("invalid data"), @@ -346,8 +348,8 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed creating missingRef: %v", err) } - d := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + d := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(mLen), Digest: mDigest, Data: []byte("invalid data"), @@ -363,8 +365,8 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed creating missingRef: %v", err) } - d := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + d := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(mLen), Digest: missingDigest, Data: []byte("invalid data"), @@ -381,11 +383,11 @@ func TestManifest(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } _, err = rc.ManifestGet(ctx, r) - if !errors.Is(err, types.ErrInvalidReference) { + if !errors.Is(err, errs.ErrInvalidReference) { t.Errorf("ManifestGet did not respond with invalid ref: %v", err) } _, err = rc.ManifestHead(ctx, r) - if !errors.Is(err, types.ErrInvalidReference) { + if !errors.Is(err, errs.ErrInvalidReference) { t.Errorf("ManifestGet did not respond with invalid ref: %v", err) } }) diff --git a/mod/config.go b/mod/config.go index 5a27b53..0575db8 100644 --- a/mod/config.go +++ b/mod/config.go @@ -9,7 +9,7 @@ import ( "time" "github.com/regclient/regclient" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -97,7 +97,7 @@ func WithConfigTimestamp(optTime OptTime) Opts { } // offset startHistory from base image history if !optTime.BaseRef.IsZero() { - var d types.Descriptor + var d descriptor.Descriptor for { mOpts := []regclient.ManifestOpts{} if d.Digest != "" { diff --git a/mod/dag.go b/mod/dag.go index 3402675..04d34e5 100644 --- a/mod/dag.go +++ b/mod/dag.go @@ -11,8 +11,9 @@ import ( "github.com/opencontainers/go-digest" "github.com/regclient/regclient" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" @@ -39,8 +40,8 @@ type dagConfig struct { type dagManifest struct { mod changes top bool // indicates the top level manifest (needed for manifest lists) - origDesc types.Descriptor - newDesc types.Descriptor + origDesc descriptor.Descriptor + newDesc descriptor.Descriptor m manifest.Manifest config *dagOCIConfig layers []*dagLayer @@ -50,19 +51,19 @@ type dagManifest struct { type dagOCIConfig struct { modified bool - newDesc types.Descriptor + newDesc descriptor.Descriptor oc blob.OCIConfig } type dagLayer struct { mod changes - newDesc types.Descriptor + newDesc descriptor.Descriptor ucDigest digest.Digest // uncompressed descriptor - desc types.Descriptor + desc descriptor.Descriptor rSrc ref.Ref } -func dagGet(ctx context.Context, rc *regclient.RegClient, rSrc ref.Ref, d types.Descriptor) (*dagManifest, error) { +func dagGet(ctx context.Context, rc *regclient.RegClient, rSrc ref.Ref, d descriptor.Descriptor) (*dagManifest, error) { var err error getOpts := []regclient.ManifestOpts{} if d.Digest != "" { @@ -92,7 +93,7 @@ func dagGet(ctx context.Context, rc *regclient.RegClient, rSrc ref.Ref, d types. // pull config doc := dagOCIConfig{} cd, err := mi.GetConfig() - if err != nil && !errors.Is(err, types.ErrUnsupportedMediaType) { + if err != nil && !errors.Is(err, errs.ErrUnsupportedMediaType) { return nil, err } else if err == nil && inListStr(cd.MediaType, mtWLConfig) { oc, err := rc.BlobGetOCIConfig(ctx, rSrc, cd) diff --git a/mod/manifest.go b/mod/manifest.go index 2e952d5..367b8bd 100644 --- a/mod/manifest.go +++ b/mod/manifest.go @@ -10,7 +10,9 @@ import ( "github.com/regclient/regclient" "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" ) @@ -180,7 +182,7 @@ func WithAnnotationPromoteCommon() Opts { } mAnnot, ok := child.m.(manifest.Annotator) if !ok { - return fmt.Errorf("manifest does not support annotations: %s%.0w", child.m.GetDescriptor().Digest.String(), types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support annotations: %s%.0w", child.m.GetDescriptor().Digest.String(), errs.ErrUnsupportedMediaType) } cur, err := mAnnot.GetAnnotations() if err != nil { @@ -289,7 +291,7 @@ func WithManifestToDocker() Opts { changed := false om := dm.m.GetOrig() if dm.m.IsList() { - if dm.m.GetDescriptor().MediaType != types.MediaTypeDocker2ManifestList { + if dm.m.GetDescriptor().MediaType != mediatype.Docker2ManifestList { ociM, err := manifest.OCIIndexFromAny(om) if err != nil { return err @@ -307,22 +309,22 @@ func WithManifestToDocker() Opts { if err != nil { return err } - if dm.m.GetDescriptor().MediaType != types.MediaTypeDocker2Manifest { + if dm.m.GetDescriptor().MediaType != mediatype.Docker2Manifest { changed = true } if ociM.ArtifactType != "" { - return fmt.Errorf("unable to convert artifactType to docker manifest, ref %s%.0w", rSrc.CommonName(), types.ErrUnsupportedMediaType) + return fmt.Errorf("unable to convert artifactType to docker manifest, ref %s%.0w", rSrc.CommonName(), errs.ErrUnsupportedMediaType) } - if ociM.Config.MediaType == types.MediaTypeOCI1ImageConfig { - ociM.Config.MediaType = types.MediaTypeDocker2ImageConfig + if ociM.Config.MediaType == mediatype.OCI1ImageConfig { + ociM.Config.MediaType = mediatype.Docker2ImageConfig changed = true } for i, l := range ociM.Layers { - if l.MediaType == types.MediaTypeOCI1LayerGzip { - ociM.Layers[i].MediaType = types.MediaTypeDocker2LayerGzip + if l.MediaType == mediatype.OCI1LayerGzip { + ociM.Layers[i].MediaType = mediatype.Docker2LayerGzip changed = true - } else if l.MediaType == types.MediaTypeOCI1ForeignLayerGzip { - ociM.Layers[i].MediaType = types.MediaTypeDocker2ForeignLayer + } else if l.MediaType == mediatype.OCI1ForeignLayerGzip { + ociM.Layers[i].MediaType = mediatype.Docker2ForeignLayer changed = true } } @@ -365,7 +367,7 @@ func WithManifestToOCI() Opts { if err != nil { return err } - if dm.m.GetDescriptor().MediaType != types.MediaTypeOCI1ManifestList { + if dm.m.GetDescriptor().MediaType != mediatype.OCI1ManifestList { changed = true om = ociM } @@ -374,19 +376,19 @@ func WithManifestToOCI() Opts { if err != nil { return err } - if dm.m.GetDescriptor().MediaType != types.MediaTypeOCI1Manifest { + if dm.m.GetDescriptor().MediaType != mediatype.OCI1Manifest { changed = true } - if ociM.Config.MediaType == types.MediaTypeDocker2ImageConfig { - ociM.Config.MediaType = types.MediaTypeOCI1ImageConfig + if ociM.Config.MediaType == mediatype.Docker2ImageConfig { + ociM.Config.MediaType = mediatype.OCI1ImageConfig changed = true } for i, l := range ociM.Layers { - if l.MediaType == types.MediaTypeDocker2LayerGzip { - ociM.Layers[i].MediaType = types.MediaTypeOCI1LayerGzip + if l.MediaType == mediatype.Docker2LayerGzip { + ociM.Layers[i].MediaType = mediatype.OCI1LayerGzip changed = true - } else if l.MediaType == types.MediaTypeDocker2ForeignLayer { - ociM.Layers[i].MediaType = types.MediaTypeOCI1ForeignLayerGzip + } else if l.MediaType == mediatype.Docker2ForeignLayer { + ociM.Layers[i].MediaType = mediatype.OCI1ForeignLayerGzip changed = true } } @@ -514,14 +516,14 @@ func WithExternalURLsRm() Opts { ociOM.Layers[i].URLs = []string{} mt := ociOM.Layers[i].MediaType switch mt { - case types.MediaTypeDocker2ForeignLayer: - mt = types.MediaTypeDocker2LayerGzip - case types.MediaTypeOCI1ForeignLayer: - mt = types.MediaTypeOCI1Layer - case types.MediaTypeOCI1ForeignLayerGzip: - mt = types.MediaTypeOCI1LayerGzip - case types.MediaTypeOCI1ForeignLayerZstd: - mt = types.MediaTypeOCI1LayerZstd + case mediatype.Docker2ForeignLayer: + mt = mediatype.Docker2LayerGzip + case mediatype.OCI1ForeignLayer: + mt = mediatype.OCI1Layer + case mediatype.OCI1ForeignLayerGzip: + mt = mediatype.OCI1LayerGzip + case mediatype.OCI1ForeignLayerZstd: + mt = mediatype.OCI1LayerZstd } ociOM.Layers[i].MediaType = mt changed = true @@ -572,11 +574,11 @@ func WithRebase() Opts { } baseName, ok := annot[types.AnnotationBaseImageName] if !ok { - return fmt.Errorf("annotation for base image is missing (%s or %s)%.0w", types.AnnotationBaseImageName, types.AnnotationBaseImageDigest, types.ErrMissingAnnotation) + return fmt.Errorf("annotation for base image is missing (%s or %s)%.0w", types.AnnotationBaseImageName, types.AnnotationBaseImageDigest, errs.ErrMissingAnnotation) } baseDigest, ok := annot[types.AnnotationBaseImageDigest] if !ok { - return fmt.Errorf("annotation for base image is missing (%s or %s)%.0w", types.AnnotationBaseImageName, types.AnnotationBaseImageDigest, types.ErrMissingAnnotation) + return fmt.Errorf("annotation for base image is missing (%s or %s)%.0w", types.AnnotationBaseImageName, types.AnnotationBaseImageDigest, errs.ErrMissingAnnotation) } rNew, err := ref.New(baseName) if err != nil { @@ -712,15 +714,15 @@ func rebaseAddStep(dc *dagConfig, rBaseOld, rBaseNew ref.Ref) error { // validate current base if len(layersOld) > len(layers) { - return fmt.Errorf("base image has more layers than modified image%.0w", types.ErrMismatch) + return fmt.Errorf("base image has more layers than modified image%.0w", errs.ErrMismatch) } for i := range layersOld { if !layers[i].Same(layersOld[i]) { - return fmt.Errorf("old base image does not match image layers, layer %d, base %v, image %v%.0w", i, layersOld[i], layers[i], types.ErrMismatch) + return fmt.Errorf("old base image does not match image layers, layer %d, base %v, image %v%.0w", i, layersOld[i], layers[i], errs.ErrMismatch) } } if len(confOCIOld.History) > len(confOCI.History) { - return fmt.Errorf("base image has more history entries than modified image%.0w", types.ErrMismatch) + return fmt.Errorf("base image has more history entries than modified image%.0w", errs.ErrMismatch) } historyLayers := 0 for i := range confOCIOld.History { @@ -729,21 +731,21 @@ func rebaseAddStep(dc *dagConfig, rBaseOld, rBaseNew ref.Ref) error { !confOCI.History[i].Created.Equal(*confOCIOld.History[i].Created) || confOCI.History[i].CreatedBy != confOCIOld.History[i].CreatedBy || confOCI.History[i].EmptyLayer != confOCIOld.History[i].EmptyLayer { - return fmt.Errorf("old base image does not match image history, entry %d, base %v, image %v%.0w", i, confOCIOld.History[i], confOCI.History[i], types.ErrMismatch) + return fmt.Errorf("old base image does not match image history, entry %d, base %v, image %v%.0w", i, confOCIOld.History[i], confOCI.History[i], errs.ErrMismatch) } if !confOCIOld.History[i].EmptyLayer { historyLayers++ } } if len(layersOld) != historyLayers || len(layersOld) != len(confOCIOld.RootFS.DiffIDs) { - return fmt.Errorf("old base image layer count doesn't match history%.0w", types.ErrMismatch) + return fmt.Errorf("old base image layer count doesn't match history%.0w", errs.ErrMismatch) } if len(confOCIOld.RootFS.DiffIDs) > len(confOCI.RootFS.DiffIDs) { - return fmt.Errorf("base image has more rootfs entries than modified image%.0w", types.ErrMismatch) + return fmt.Errorf("base image has more rootfs entries than modified image%.0w", errs.ErrMismatch) } for i := range confOCIOld.RootFS.DiffIDs { if confOCI.RootFS.DiffIDs[i] != confOCIOld.RootFS.DiffIDs[i] { - return fmt.Errorf("old base image does not match image rootfs, entry %d, base %s, image %s%.0w", i, confOCIOld.RootFS.DiffIDs[i].String(), confOCI.RootFS.DiffIDs[i].String(), types.ErrMismatch) + return fmt.Errorf("old base image does not match image rootfs, entry %d, base %s, image %s%.0w", i, confOCIOld.RootFS.DiffIDs[i].String(), confOCI.RootFS.DiffIDs[i].String(), errs.ErrMismatch) } } // validate new base diff --git a/mod/mod.go b/mod/mod.go index 6abb2e5..5314b80 100644 --- a/mod/mod.go +++ b/mod/mod.go @@ -14,7 +14,8 @@ import ( "github.com/regclient/regclient" "github.com/regclient/regclient/pkg/archive" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -33,14 +34,14 @@ type OptTime struct { var ( // whitelist of tar media types mtWLTar = []string{ - types.MediaTypeDocker2LayerGzip, - types.MediaTypeOCI1Layer, - types.MediaTypeOCI1LayerGzip, - types.MediaTypeOCI1LayerZstd, + mediatype.Docker2LayerGzip, + mediatype.OCI1Layer, + mediatype.OCI1LayerGzip, + mediatype.OCI1LayerZstd, } mtWLConfig = []string{ - types.MediaTypeDocker2ImageConfig, - types.MediaTypeOCI1ImageConfig, + mediatype.Docker2ImageConfig, + mediatype.OCI1ImageConfig, } ) @@ -54,7 +55,7 @@ func Apply(ctx context.Context, rc *regclient.RegClient, rSrc ref.Ref, opts ...O // do I need to store a DAG in memory with pointers back to parents and modified bool, so change to digest can be rippled up and modified objects are pushed? // pull the image metadata into a DAG - dm, err := dagGet(ctx, rc, rSrc, types.Descriptor{}) + dm, err := dagGet(ctx, rc, rSrc, descriptor.Descriptor{}) if err != nil { return rSrc, err } @@ -143,7 +144,7 @@ func Apply(ctx context.Context, rc *regclient.RegClient, rSrc ref.Ref, opts ...O var gw *gzip.Writer digRaw := digest.Canonical.Digester() // raw/compressed digest digUC := digest.Canonical.Digester() // uncompressed digest - if dl.desc.MediaType == types.MediaTypeDocker2LayerGzip || dl.desc.MediaType == types.MediaTypeOCI1LayerGzip { + if dl.desc.MediaType == mediatype.Docker2LayerGzip || dl.desc.MediaType == mediatype.OCI1LayerGzip { cw := io.MultiWriter(fh, digRaw.Hash()) gw = gzip.NewWriter(cw) defer gw.Close() diff --git a/mod/mod_test.go b/mod/mod_test.go index 5788c58..e5528d6 100644 --- a/mod/mod_test.go +++ b/mod/mod_test.go @@ -18,8 +18,9 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/rwfs" "github.com/regclient/regclient/scheme/reg" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" ) @@ -739,7 +740,7 @@ func TestMod(t *testing.T) { WithRebase(), }, ref: "ocidir://testrepo:v1", - wantErr: types.ErrMissingAnnotation, + wantErr: errs.ErrMissingAnnotation, }, { name: "Rebase refs", @@ -755,7 +756,7 @@ func TestMod(t *testing.T) { WithRebaseRefs(rb2, rb3), }, ref: "ocidir://testrepo:v3", - wantErr: types.ErrMismatch, + wantErr: errs.ErrMismatch, }, { name: "Rebase mismatch", @@ -763,7 +764,7 @@ func TestMod(t *testing.T) { WithRebaseRefs(rb3, rb2), }, ref: "ocidir://testrepo:v3", - wantErr: types.ErrMismatch, + wantErr: errs.ErrMismatch, }, { name: "Rebase and backdate", @@ -852,12 +853,12 @@ func TestMod(t *testing.T) { func TestInList(t *testing.T) { t.Parallel() t.Run("match", func(t *testing.T) { - if !inListStr(types.MediaTypeDocker2LayerGzip, mtWLTar) { + if !inListStr(mediatype.Docker2LayerGzip, mtWLTar) { t.Errorf("did not find docker layer in tar whitelist") } }) t.Run("mismatch", func(t *testing.T) { - if inListStr(types.MediaTypeDocker2LayerGzip, mtWLConfig) { + if inListStr(mediatype.Docker2LayerGzip, mtWLConfig) { t.Errorf("found docker layer in config whitelist") } }) diff --git a/referrer.go b/referrer.go index c41467c..a4411bb 100644 --- a/referrer.go +++ b/referrer.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" "github.com/regclient/regclient/types/referrer" ) @@ -14,7 +14,7 @@ import ( // The descriptor list should contain manifests that each have a subject field matching the requested ref. func (rc *RegClient) ReferrerList(ctx context.Context, r ref.Ref, opts ...scheme.ReferrerOpts) (referrer.ReferrerList, error) { if !r.IsSet() { - return referrer.ReferrerList{}, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return referrer.ReferrerList{}, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { diff --git a/regclient/blob/blob.go b/regclient/blob/blob.go index 501db3c..9222fbb 100644 --- a/regclient/blob/blob.go +++ b/regclient/blob/blob.go @@ -8,12 +8,32 @@ import ( topBlob "github.com/regclient/regclient/types/blob" ) -type Blob = topBlob.Blob -type OCIConfig = topBlob.OCIConfig -type Common = topBlob.Common -type Reader = topBlob.Reader +type ( + // Blob specifies a generic blob. + // + // Deprecated: replace with [blob.Blob]. + Blob = topBlob.Blob + // OCIConfig is an interface for an OCI Config. + // + // Deprecated: replace with [blob.OCIConfig]. + OCIConfig = topBlob.OCIConfig + // Common is an interface of common methods for blobs. + // + // Deprecated: replace with [blob.Common]. + Common = topBlob.Common + // Reader is an interface for blob reader methods. + // + // Deprecated: replace with [blob.Reader]. + Reader = topBlob.Reader +) var ( + // NewOCIConfig + // + // Deprecated: replace with [blob.NewOCIConfig]. NewOCIConfig = topBlob.NewOCIConfig - NewReader = topBlob.NewReader + // NewReader + // + // Deprecated: replace with [blob.NewReader]. + NewReader = topBlob.NewReader ) diff --git a/regclient/config.go b/regclient/config.go index d0503fe..af477d3 100644 --- a/regclient/config.go +++ b/regclient/config.go @@ -9,14 +9,39 @@ import ( "github.com/regclient/regclient/config" ) -type ConfigHost = config.Host -type TLSConf = config.TLSConf +type ( + // ConfigHost defines settings for connecting to a registry. + // + // Deprecated: replace with [config.Host]. + ConfigHost = config.Host + // TLSConf specifies whether TLS is enabled and verified for a host. + // + // Deprecated: replace with [config.TLSConf]. + TLSConf = config.TLSConf +) -var ConfigHostNewName = config.HostNewName +var ( + // ConfigHostNewName + // + // Deprecated: replace with [config.ConfigHostNewName]. + ConfigHostNewName = config.HostNewName +) const ( + // TLSUndefined + // + // Deprecated: replace with [config.TLSUndefined]. TLSUndefined = config.TLSUndefined - TLSEnabled = config.TLSEnabled - TLSInsecure = config.TLSInsecure - TLSDisabled = config.TLSDisabled + // TLSEnabled + // + // Deprecated: replace with [config.TLSEnabled]. + TLSEnabled = config.TLSEnabled + // TLSInsecure + // + // Deprecated: replace with [config.TLSInsecure]. + TLSInsecure = config.TLSInsecure + // TLSDisabled + // + // Deprecated: replace with [config.TLSDisabled]. + TLSDisabled = config.TLSDisabled ) diff --git a/regclient/config/host.go b/regclient/config/host.go index 3e005c1..ea53528 100644 --- a/regclient/config/host.go +++ b/regclient/config/host.go @@ -8,18 +8,55 @@ import ( topConfig "github.com/regclient/regclient/config" ) -type TLSConf = topConfig.TLSConf -type Host = topConfig.Host - -const ( - TLSUndefined = topConfig.TLSUndefined - TLSEnabled = topConfig.TLSEnabled - TLSInsecure = topConfig.TLSInsecure - TLSDisabled = topConfig.TLSDisabled - DockerRegistry = topConfig.DockerRegistry - DockerRegistryAuth = topConfig.DockerRegistryAuth - DockerRegistryDNS = topConfig.DockerRegistryDNS +type ( + // TLSConf defines the TLS enumerated values. + // + // Deprecated: replace with [config.TLSConf]. + TLSConf = topConfig.TLSConf + // Host defines a registry configuration. + // + // Deprecated: replace with [config.Host]. + Host = topConfig.Host ) -var HostNew = topConfig.HostNew -var HostNewName = topConfig.HostNewName +const ( + // TLSUndefined + // + // Deprecated: replace with [config.TLSUndefined]. + TLSUndefined = topConfig.TLSUndefined + // TLSEnabled + // + // Deprecated: replace with [config.TLSEnabled]. + TLSEnabled = topConfig.TLSEnabled + // TLSInsecure + // + // Deprecated: replace with [config.TLSInsecure]. + TLSInsecure = topConfig.TLSInsecure + // TLSDisabled + // + // Deprecated: replace with [config.TLSDisabled]. + TLSDisabled = topConfig.TLSDisabled + // DockerRegistry + // + // Deprecated: replace with [config.DockerRegistry]. + DockerRegistry = topConfig.DockerRegistry + // DockerRegistryAuth + // + // Deprecated: replace with [config.DockerRegistryAuth]. + DockerRegistryAuth = topConfig.DockerRegistryAuth + // DockerRegistryDNS + // + // Deprecated: replace with [config.DockerRegistryDNS]. + DockerRegistryDNS = topConfig.DockerRegistryDNS +) + +var ( + // HostNew + // + // Deprecated: replace with [config.HostNew]. + HostNew = topConfig.HostNew + // HostNewName + // + // Deprecated: replace with [config.HostNewName]. + HostNewName = topConfig.HostNewName +) diff --git a/regclient/error.go b/regclient/error.go index 248d8cc..97b088f 100644 --- a/regclient/error.go +++ b/regclient/error.go @@ -6,27 +6,81 @@ package regclient import ( - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) var ( - ErrAPINotFound = types.ErrAPINotFound - ErrCanceled = types.ErrCanceled + // ErrAPINotFound + // + // Deprecated: replace with [errs.ErrAPINotFound]. + ErrAPINotFound = errs.ErrAPINotFound + // ErrCanceled + // + // Deprecated: replace with [errs.ErrCanceled]. + ErrCanceled = errs.ErrCanceled //lint:ignore ST1003 exported field cannot be changed for legacy reasons - ErrHttpStatus = types.ErrHTTPStatus - ErrMissingDigest = types.ErrMissingDigest - ErrMissingLocation = types.ErrMissingLocation - ErrMissingName = types.ErrMissingName - ErrMissingTag = types.ErrMissingTag - ErrMissingTagOrDigest = types.ErrMissingTagOrDigest - ErrMountReturnedLocation = types.ErrMountReturnedLocation - ErrNotFound = types.ErrNotFound - ErrNotImplemented = types.ErrNotImplemented - ErrParsingFailed = types.ErrParsingFailed - ErrRateLimit = types.ErrHTTPRateLimit - ErrUnavailable = types.ErrUnavailable - ErrUnauthorized = types.ErrHTTPUnauthorized - ErrUnsupportedAPI = types.ErrUnsupportedAPI - ErrUnsupportedConfigVersion = types.ErrUnsupportedConfigVersion - ErrUnsupportedMediaType = types.ErrUnsupportedMediaType + // ErrHttpStatus + // + // Deprecated: replace with [errs.ErrHttpStatus]. + ErrHttpStatus = errs.ErrHTTPStatus + // ErrMissingDigest + // + // Deprecated: replace with [errs.ErrMissingDigest]. + ErrMissingDigest = errs.ErrMissingDigest + // ErrMissingLocation + // + // Deprecated: replace with [errs.ErrMissingLocation]. + ErrMissingLocation = errs.ErrMissingLocation + // ErrMissingName + // + // Deprecated: replace with [errs.ErrMissingName]. + ErrMissingName = errs.ErrMissingName + // ErrMissingTag + // + // Deprecated: replace with [errs.ErrMissingTag]. + ErrMissingTag = errs.ErrMissingTag + // ErrMissingTagOrDigest + // + // Deprecated: replace with [errs.ErrMissingTagOrDigest]. + ErrMissingTagOrDigest = errs.ErrMissingTagOrDigest + // ErrMountReturnedLocation + // + // Deprecated: replace with [errs.ErrMountReturnedLocation]. + ErrMountReturnedLocation = errs.ErrMountReturnedLocation + // ErrNotFound + // + // Deprecated: replace with [errs.ErrNotFound]. + ErrNotFound = errs.ErrNotFound + // ErrNotImplemented + // + // Deprecated: replace with [errs.ErrNotImplemented]. + ErrNotImplemented = errs.ErrNotImplemented + // ErrParsingFailed + // + // Deprecated: replace with [errs.ErrParsingFailed]. + ErrParsingFailed = errs.ErrParsingFailed + // ErrRateLimit + // + // Deprecated: replace with [errs.ErrRateLimit]. + ErrRateLimit = errs.ErrHTTPRateLimit + // ErrUnavailable + // + // Deprecated: replace with [errs.ErrUnavailable]. + ErrUnavailable = errs.ErrUnavailable + // ErrUnauthorized + // + // Deprecated: replace with [errs.ErrUnauthorized]. + ErrUnauthorized = errs.ErrHTTPUnauthorized + // ErrUnsupportedAPI + // + // Deprecated: replace with [errs.ErrUnsupportedAPI]. + ErrUnsupportedAPI = errs.ErrUnsupportedAPI + // ErrUnsupportedConfigVersion + // + // Deprecated: replace with [errs.ErrUnsupportedConfigVersion]. + ErrUnsupportedConfigVersion = errs.ErrUnsupportedConfigVersion + // ErrUnsupportedMediaType + // + // Deprecated: replace with [errs.ErrUnsupportedMediaType]. + ErrUnsupportedMediaType = errs.ErrUnsupportedMediaType ) diff --git a/regclient/manifest/manifest.go b/regclient/manifest/manifest.go index f6edf9a..ba5100e 100644 --- a/regclient/manifest/manifest.go +++ b/regclient/manifest/manifest.go @@ -8,34 +8,93 @@ import ( "net/http" topTypes "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" topManifest "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) const ( - MediaTypeDocker1Manifest = topTypes.MediaTypeDocker1Manifest - MediaTypeDocker1ManifestSigned = topTypes.MediaTypeDocker1ManifestSigned - MediaTypeDocker2Manifest = topTypes.MediaTypeDocker2Manifest - MediaTypeDocker2ManifestList = topTypes.MediaTypeDocker2ManifestList - MediaTypeDocker2ImageConfig = topTypes.MediaTypeDocker2ImageConfig - MediaTypeOCI1Manifest = topTypes.MediaTypeOCI1Manifest - MediaTypeOCI1ManifestList = topTypes.MediaTypeOCI1ManifestList - MediaTypeOCI1ImageConfig = topTypes.MediaTypeOCI1ImageConfig - MediaTypeDocker2Layer = topTypes.MediaTypeDocker2LayerGzip - MediaTypeOCI1Layer = topTypes.MediaTypeOCI1Layer - MediaTypeOCI1LayerGzip = topTypes.MediaTypeOCI1LayerGzip - MediaTypeBuildkitCacheConfig = topTypes.MediaTypeBuildkitCacheConfig + // MediaTypeDocker1Manifest + // + // Deprecated: replace with [mediatype.Docker1Manifest]. + MediaTypeDocker1Manifest = mediatype.Docker1Manifest + // MediaTypeDocker1ManifestSigned + // + // Deprecated: replace with [mediatype.Docker1ManifestSigned]. + MediaTypeDocker1ManifestSigned = mediatype.Docker1ManifestSigned + // MediaTypeDocker2Manifest + // + // Deprecated: replace with [mediatype.Docker2Manifest]. + MediaTypeDocker2Manifest = mediatype.Docker2Manifest + // MediaTypeDocker2ManifestList + // + // Deprecated: replace with [mediatype.Docker2ManifestList]. + MediaTypeDocker2ManifestList = mediatype.Docker2ManifestList + // MediaTypeDocker2ImageConfig + // + // Deprecated: replace with [mediatype.Docker2ImageConfig]. + MediaTypeDocker2ImageConfig = mediatype.Docker2ImageConfig + // MediaTypeOCI1Manifest + // + // Deprecated: replace with [mediatype.OCI1Manifest]. + MediaTypeOCI1Manifest = mediatype.OCI1Manifest + // MediaTypeOCI1ManifestList + // + // Deprecated: replace with [mediatype.OCI1ManifestList]. + MediaTypeOCI1ManifestList = mediatype.OCI1ManifestList + // MediaTypeOCI1ImageConfig + // + // Deprecated: replace with [mediatype.OCI1ImageConfig]. + MediaTypeOCI1ImageConfig = mediatype.OCI1ImageConfig + // MediaTypeDocker2Layer + // + // Deprecated: replace with [mediatype.Docker2Layer]. + MediaTypeDocker2Layer = mediatype.Docker2LayerGzip + // MediaTypeOCI1Layer + // + // Deprecated: replace with [mediatype.OCI1Layer]. + MediaTypeOCI1Layer = mediatype.OCI1Layer + // MediaTypeOCI1LayerGzip + // + // Deprecated: replace with [mediatype.OCI1LayerGzip]. + MediaTypeOCI1LayerGzip = mediatype.OCI1LayerGzip + // MediaTypeBuildkitCacheConfig + // + // Deprecated: replace with [mediatype.BuildkitCacheConfig]. + MediaTypeBuildkitCacheConfig = mediatype.BuildkitCacheConfig ) -type Manifest = topManifest.Manifest +type ( + // Manifest interface is implemented by all supported manifests but + // many calls are only supported by certain underlying media types. + // + // Deprecated: replace with [manifest.Manifest]. + Manifest = topManifest.Manifest +) var ( - ErrNotFound = topTypes.ErrNotFound - ErrNotImplemented = topTypes.ErrNotImplemented - ErrUnavailable = topTypes.ErrUnavailable - ErrUnsupportedMediaType = topTypes.ErrUnsupported + // ErrNotFound + // + // Deprecated: replace with [errs.ErrNotFound]. + ErrNotFound = errs.ErrNotFound + // ErrNotImplemented + // + // Deprecated: replace with [errs.ErrNotImplemented]. + ErrNotImplemented = errs.ErrNotImplemented + // ErrUnavailable + // + // Deprecated: replace with [errs.ErrUnavailable]. + ErrUnavailable = errs.ErrUnavailable + // ErrUnsupportedMediaType + // + // Deprecated: replace with [errs.ErrUnsupportedMediaType]. + ErrUnsupportedMediaType = errs.ErrUnsupported ) +// New creates a new manifest. +// +// Deprecated: replace with [manifest.New]. func New(mediaType string, raw []byte, r ref.Ref, header http.Header) (Manifest, error) { return topManifest.New( topManifest.WithDesc(topTypes.Descriptor{ @@ -47,6 +106,9 @@ func New(mediaType string, raw []byte, r ref.Ref, header http.Header) (Manifest, ) } +// FromDescriptor creates a manifest from a descriptor. +// +// Deprecated: replace with [manifest.New]. func FromDescriptor(desc topTypes.Descriptor, mBytes []byte) (Manifest, error) { return topManifest.New( topManifest.WithDesc(desc), @@ -54,6 +116,9 @@ func FromDescriptor(desc topTypes.Descriptor, mBytes []byte) (Manifest, error) { ) } +// FromOrig creates a manifest from an underlying manifest struct. +// +// Deprecated: replace with [manifest.New]. func FromOrig(orig interface{}) (Manifest, error) { return topManifest.New(topManifest.WithOrig(orig)) } diff --git a/regclient/mediatype.go b/regclient/mediatype.go index 8cfcce9..be08280 100644 --- a/regclient/mediatype.go +++ b/regclient/mediatype.go @@ -5,19 +5,55 @@ package regclient -import "github.com/regclient/regclient/types" +import "github.com/regclient/regclient/types/mediatype" var ( - MediaTypeDocker1Manifest = types.MediaTypeDocker1Manifest - MediaTypeDocker1ManifestSigned = types.MediaTypeDocker1ManifestSigned - MediaTypeDocker2Manifest = types.MediaTypeDocker2Manifest - MediaTypeDocker2ManifestList = types.MediaTypeDocker2ManifestList - MediaTypeDocker2ImageConfig = types.MediaTypeDocker2ImageConfig - MediaTypeOCI1Manifest = types.MediaTypeOCI1Manifest - MediaTypeOCI1ManifestList = types.MediaTypeOCI1ManifestList - MediaTypeOCI1ImageConfig = types.MediaTypeOCI1ImageConfig - MediaTypeDocker2Layer = types.MediaTypeDocker2LayerGzip - MediaTypeOCI1Layer = types.MediaTypeOCI1Layer - MediaTypeOCI1LayerGzip = types.MediaTypeOCI1LayerGzip - MediaTypeBuildkitCacheConfig = types.MediaTypeBuildkitCacheConfig + // MediaTypeDocker1Manifest + // + // Deprecated: replace with [mediatype.Docker1Manifest]. + MediaTypeDocker1Manifest = mediatype.Docker1Manifest + // MediaTypeDocker1ManifestSigned + // + // Deprecated: replace with [mediatype.Docker1ManifestSigned] + MediaTypeDocker1ManifestSigned = mediatype.Docker1ManifestSigned + // MediaTypeDocker2Manifest + // + // Deprecated: replace with [mediatype.Docker2Manifest]. + MediaTypeDocker2Manifest = mediatype.Docker2Manifest + // MediaTypeDocker2ManifestList + // + // Deprecated: replace with [mediatype.Docker2ManifestList]. + MediaTypeDocker2ManifestList = mediatype.Docker2ManifestList + // MediaTypeDocker2ImageConfig + // + // Deprecated: replace with [mediatype.Docker2ImageConfig]. + MediaTypeDocker2ImageConfig = mediatype.Docker2ImageConfig + // MediaTypeOCI1Manifest + // + // Deprecated: replace with [mediatype.OCI1Manifest]. + MediaTypeOCI1Manifest = mediatype.OCI1Manifest + // MediaTypeOCI1ManifestList + // + // Deprecated: replace with [mediatype.OCI1ManifestList]. + MediaTypeOCI1ManifestList = mediatype.OCI1ManifestList + // MediaTypeOCI1ImageConfig + // + // Deprecated: replace with [mediatype.OCI1ImageConfig]. + MediaTypeOCI1ImageConfig = mediatype.OCI1ImageConfig + // MediaTypeDocker2Layer + // + // Deprecated: replace with [mediatype.Docker2Layer]. + MediaTypeDocker2Layer = mediatype.Docker2LayerGzip + // MediaTypeOCI1Layer + // + // Deprecated: replace with [mediatype.OCI1Layer]. + MediaTypeOCI1Layer = mediatype.OCI1Layer + // MediaTypeOCI1LayerGzip + // + // Deprecated: replace with [mediatype.OCI1LayerGzip]. + MediaTypeOCI1LayerGzip = mediatype.OCI1LayerGzip + // MediaTypeBuildkitCacheConfig + // + // Deprecated: replace with [mediatype.BuildkitCacheConfig]. + MediaTypeBuildkitCacheConfig = mediatype.BuildkitCacheConfig ) diff --git a/regclient/regclient.go b/regclient/regclient.go index e79ca05..002e3d6 100644 --- a/regclient/regclient.go +++ b/regclient/regclient.go @@ -28,37 +28,125 @@ const ( var ( // VCSRef is injected from a build flag, used to version the UserAgent header + // + // Deprecated: this should now be set using github.com/regclient/regclient/internal/version.vcsTag. VCSRef = "unknown" ) -type RegClient = *rcTop.RegClient -type Client = *rcTop.RegClient -type Opt = rcTop.Opt -type ImageOpts = rcTop.ImageOpts -type RepoList = *repo.RepoList -type RepoDockerList = repo.RepoRegistryList -type RepoOpts = scheme.RepoOpts // RepoOpts is a breaking change (struct to func opts) -type TagList = *tag.List -type TagDockerList = tag.DockerList -type TagOpts = scheme.TagOpts +type ( + // RegClient is used to access OCI distribution-spec registries. + // + // Deprecated: replace with [regclient.RegClient] + RegClient = *rcTop.RegClient + // Client is used to access OCI distribution-spec registries. + // + // Deprecated: replace with [regclient.RegClient] + Client = *rcTop.RegClient + // Opt functions are used by [New] to create a [*RegClient]. + // + // Deprecated: replace with [regclient.Opt] + Opt = rcTop.Opt + // ImageOpts functions are used by image methods. + // + // Deprecated: replace with [regclient.ImageOpts] + ImageOpts = rcTop.ImageOpts + // RepoList is the response for a registry listing. + // + // Deprecated: replace with [repo.RepoList] + RepoList = *repo.RepoList + // RepoDockerList is a list of repositories from the _catalog API + // + // Deprecated: replace with [repo.RepoRegistryList + RepoDockerList = repo.RepoRegistryList + // RepoOpts is a breaking change (struct to func opts) + // + // Deprecated: replace with [regclient.RepoOpts] + RepoOpts = scheme.RepoOpts + // TagList contains a tag list. + // + // Deprecated: replace with [tag.List] + TagList = *tag.List + // TagDockerList is returned from registry/2.0 API's + // + // Deprecated: replace with [tag.DockerList] + TagDockerList = tag.DockerList + // TagOpts is used to set options on tag APIs. + // + // Deprecated: replace with [scheme.TagOpts] + TagOpts = scheme.TagOpts +) var ( - NewRegClient = rcTop.New - WithCertDir = rcTop.WithCertDir - WithDockerCerts = rcTop.WithDockerCerts - WithDockerCreds = rcTop.WithDockerCreds - WithConfigHosts = rcTop.WithConfigHosts - WithConfigHost = rcTop.WithConfigHost - WithBlobSize = rcTop.WithBlobSize - WithLog = rcTop.WithLog - WithRetryDelay = rcTop.WithRetryDelay - WithRetryLimit = rcTop.WithRetryLimit - WithUserAgent = rcTop.WithUserAgent + // NewRegClient creates a new regclient instance. + // + // Deprecated: replace with [regclient.New]. + NewRegClient = rcTop.New + // WithCertDir adds a path of certificates to trust similar to Docker's /etc/docker/certs.d. + // + // Deprecated: replace with [regclient.WithCertDir]. + WithCertDir = rcTop.WithCertDir + // WithDockerCerts adds certificates trusted by docker in /etc/docker/certs.d. + // + // Deprecated: replace with [regclient.WithDockerCerts]. + WithDockerCerts = rcTop.WithDockerCerts + // WithDockerCreds adds configuration from users docker config with registry logins. + // + // Deprecated: replace with [regclient.WithDockerCreds]. + WithDockerCreds = rcTop.WithDockerCreds + // WithConfigHosts adds a list of config host settings. + // + // Deprecated: replace with [regclient.WithConfigHosts]. + WithConfigHosts = rcTop.WithConfigHosts + // WithConfigHost adds a list of config host settings. + // + // Deprecated: replace with [regclient.WithConfigHost]. + WithConfigHost = rcTop.WithConfigHost + // WithBlobSize overrides default blob sizes. + // + // Deprecated: replace with [regclient.WithBlobSize]. + WithBlobSize = rcTop.WithBlobSize + // WithLog overrides default logrus Logger. + // + // Deprecated: replace with [regclient.WithLog]. + WithLog = rcTop.WithLog + // WithRetryDelay specifies the time permitted for retry delays. + // + // Deprecated: replace with [regclient.WithRetryDelay]. + WithRetryDelay = rcTop.WithRetryDelay + // WithRetryLimit specifies the number of retries for non-fatal errors. + // + // Deprecated: replace with [regclient.WithRetryLimit]. + WithRetryLimit = rcTop.WithRetryLimit + // WithUserAgent specifies the User-Agent http header. + // + // Deprecated: replace with [regclient.WithUserAgent]. + WithUserAgent = rcTop.WithUserAgent + // ImageWithForceRecursive attempts to copy every manifest and blob even if parent manifests already exist in ImageCopy. + // + // Deprecated: replace with [regclient.ImageWithForceRecursive]. ImageWithForceRecursive = rcTop.ImageWithForceRecursive - ImageWithDigestTags = rcTop.ImageWithDigestTags - ImageWithPlatforms = rcTop.ImageWithPlatforms - WithRepoLast = scheme.WithRepoLast - WithRepoLimit = scheme.WithRepoLimit - TagOptLast = scheme.WithTagLast - TagOptLimit = scheme.WithTagLimit + // ImageWithDigestTags looks for "sha-.*" tags in the repo to copy with any manifest in ImageCopy. + // + // Deprecated: replace with [regclient.ImageWithDigestTags]. + ImageWithDigestTags = rcTop.ImageWithDigestTags + // ImageWithPlatform requests specific platforms from a manifest list in ImageCheckBase. + // + // Deprecated: replace with [regclient.ImageWithPlatforms]. + ImageWithPlatforms = rcTop.ImageWithPlatforms + // WithRepoLast passes the last received repository for requesting the next batch of repositories. + // + // Deprecated: replace with [scheme.WithRepoLast]. + WithRepoLast = scheme.WithRepoLast + // WithRepoLimit passes a maximum number of repositories to return to the repository list API. + // + // Deprecated: replace with [scheme.WithRepoLimit]. + WithRepoLimit = scheme.WithRepoLimit + // TagOptLast passes the last received tag for requesting the next batch of tags. + // + // Deprecated: replace with [scheme.WithTagLast]. + TagOptLast = scheme.WithTagLast + // TagOptLimit passes a maximum number of tags to return to the tag list API. + // + // Deprecated: replace with [scheme.WithTagLimit]. + TagOptLimit = scheme.WithTagLimit ) diff --git a/regclient/template.go b/regclient/template.go index a4027ec..45226bb 100644 --- a/regclient/template.go +++ b/regclient/template.go @@ -9,4 +9,7 @@ import ( gotemplate "text/template" ) +// TemplateFuncs adds functions to a template. +// +// Deprecated: replace with [gotemplate.FuncMap]. var TemplateFuncs = gotemplate.FuncMap{} diff --git a/regclient/types/error.go b/regclient/types/error.go index 16ef924..f4c78ae 100644 --- a/regclient/types/error.go +++ b/regclient/types/error.go @@ -6,35 +6,113 @@ package types import ( - topTypes "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) var ( - ErrAllRequestsFailed = topTypes.ErrAllRequestsFailed - ErrAPINotFound = topTypes.ErrAPINotFound - ErrBackoffLimit = topTypes.ErrBackoffLimit - ErrCanceled = topTypes.ErrCanceled - ErrDigestMismatch = topTypes.ErrDigestMismatch - ErrEmptyChallenge = topTypes.ErrEmptyChallenge + // ErrAllRequestsFailed + // + // Deprecated: replace with [errs.ErrAllRequestsFailed]. + ErrAllRequestsFailed = errs.ErrAllRequestsFailed + // ErrAPINotFound + // + // Deprecated: replace with [errs.ErrAPINotFound]. + ErrAPINotFound = errs.ErrAPINotFound + // ErrBackoffLimit + // + // Deprecated: replace with [errs.ErrBackoffLimit]. + ErrBackoffLimit = errs.ErrBackoffLimit + // ErrCanceled + // + // Deprecated: replace with [errs.ErrCanceled]. + ErrCanceled = errs.ErrCanceled + // ErrDigestMismatch + // + // Deprecated: replace with [errs.ErrDigestMismatch]. + ErrDigestMismatch = errs.ErrDigestMismatch + // ErrEmptyChallenge + // + // Deprecated: replace with [errs.ErrEmptyChallenge]. + ErrEmptyChallenge = errs.ErrEmptyChallenge //lint:ignore ST1003 exported field cannot be changed for legacy reasons - ErrHttpStatus = topTypes.ErrHTTPStatus - ErrInvalidChallenge = topTypes.ErrInvalidChallenge - ErrMissingDigest = topTypes.ErrMissingDigest - ErrMissingLocation = topTypes.ErrMissingLocation - ErrMissingName = topTypes.ErrMissingName - ErrMissingTag = topTypes.ErrMissingTag - ErrMissingTagOrDigest = topTypes.ErrMissingTagOrDigest - ErrMountReturnedLocation = topTypes.ErrMountReturnedLocation - ErrNoNewChallenge = topTypes.ErrNoNewChallenge - ErrNotFound = topTypes.ErrNotFound - ErrNotImplemented = topTypes.ErrNotImplemented - ErrParsingFailed = topTypes.ErrParsingFailed - ErrRateLimit = topTypes.ErrHTTPRateLimit - ErrRetryNeeded = topTypes.ErrRetryNeeded - ErrUnavailable = topTypes.ErrUnavailable - ErrUnauthorized = topTypes.ErrHTTPUnauthorized - ErrUnsupported = topTypes.ErrUnsupported - ErrUnsupportedAPI = topTypes.ErrUnsupportedAPI - ErrUnsupportedConfigVersion = topTypes.ErrUnsupportedConfigVersion - ErrUnsupportedMediaType = topTypes.ErrUnsupportedMediaType + // ErrHttpStatus + // + // Deprecated: replace with [errs.ErrHttpStatus]. + ErrHttpStatus = errs.ErrHTTPStatus + // ErrInvalidChallenge + // + // Deprecated: replace with [errs.ErrInvalidChallenge]. + ErrInvalidChallenge = errs.ErrInvalidChallenge + // ErrMissingDigest + // + // Deprecated: replace with [errs.ErrMissingDigest]. + ErrMissingDigest = errs.ErrMissingDigest + // ErrMissingLocation + // + // Deprecated: replace with [errs.ErrMissingLocation]. + ErrMissingLocation = errs.ErrMissingLocation + // ErrMissingName + // + // Deprecated: replace with [errs.ErrMissingName]. + ErrMissingName = errs.ErrMissingName + // ErrMissingTag + // + // Deprecated: replace with [errs.ErrMissingTag]. + ErrMissingTag = errs.ErrMissingTag + // ErrMissingTagOrDigest + // + // Deprecated: replace with [errs.ErrMissingTagOrDigest]. + ErrMissingTagOrDigest = errs.ErrMissingTagOrDigest + // ErrMountReturnedLocation + // + // Deprecated: replace with [errs.ErrMountReturnedLocation]. + ErrMountReturnedLocation = errs.ErrMountReturnedLocation + // ErrNoNewChallenge + // + // Deprecated: replace with [errs.ErrNoNewChallenge]. + ErrNoNewChallenge = errs.ErrNoNewChallenge + // ErrNotFound + // + // Deprecated: replace with [errs.ErrNotFound]. + ErrNotFound = errs.ErrNotFound + // ErrNotImplemented + // + // Deprecated: replace with [errs.ErrNotImplemented]. + ErrNotImplemented = errs.ErrNotImplemented + // ErrParsingFailed + // + // Deprecated: replace with [errs.ErrParsingFailed]. + ErrParsingFailed = errs.ErrParsingFailed + // ErrRateLimit + // + // Deprecated: replace with [errs.ErrRateLimit]. + ErrRateLimit = errs.ErrHTTPRateLimit + // ErrRetryNeeded + // + // Deprecated: replace with [errs.ErrRetryNeeded]. + ErrRetryNeeded = errs.ErrRetryNeeded + // ErrUnavailable + // + // Deprecated: replace with [errs.ErrUnavailable]. + ErrUnavailable = errs.ErrUnavailable + // ErrUnauthorized + // + // Deprecated: replace with [errs.ErrUnauthorized]. + ErrUnauthorized = errs.ErrHTTPUnauthorized + // ErrUnsupported + // + // Deprecated: replace with [errs.ErrUnsupported]. + ErrUnsupported = errs.ErrUnsupported + // ErrUnsupportedAPI + // + // Deprecated: replace with [errs.ErrUnsupportedAPI]. + ErrUnsupportedAPI = errs.ErrUnsupportedAPI + // ErrUnsupportedConfigVersion + // + // Deprecated: replace with [errs.ErrUnsupportedConfigVersion]. + ErrUnsupportedConfigVersion = errs.ErrUnsupportedConfigVersion + // ErrUnsupportedMediaType + // + // Deprecated: replace with [errs.ErrUnsupportedMediaType]. + ErrUnsupportedMediaType = errs.ErrUnsupportedMediaType ) diff --git a/regclient/types/mediatype.go b/regclient/types/mediatype.go index 178efd4..b01c740 100644 --- a/regclient/types/mediatype.go +++ b/regclient/types/mediatype.go @@ -6,20 +6,56 @@ package types import ( - topTypes "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/mediatype" ) const ( - MediaTypeDocker1Manifest = topTypes.MediaTypeDocker1Manifest - MediaTypeDocker1ManifestSigned = topTypes.MediaTypeDocker1ManifestSigned - MediaTypeDocker2Manifest = topTypes.MediaTypeDocker2Manifest - MediaTypeDocker2ManifestList = topTypes.MediaTypeDocker2ManifestList - MediaTypeDocker2ImageConfig = topTypes.MediaTypeDocker2ImageConfig - MediaTypeOCI1Manifest = topTypes.MediaTypeOCI1Manifest - MediaTypeOCI1ManifestList = topTypes.MediaTypeOCI1ManifestList - MediaTypeOCI1ImageConfig = topTypes.MediaTypeOCI1ImageConfig - MediaTypeDocker2Layer = topTypes.MediaTypeDocker2LayerGzip - MediaTypeOCI1Layer = topTypes.MediaTypeOCI1Layer - MediaTypeOCI1LayerGzip = topTypes.MediaTypeOCI1LayerGzip - MediaTypeBuildkitCacheConfig = topTypes.MediaTypeBuildkitCacheConfig + // MediaTypeDocker1Manifest + // + // Deprecated: replaced by [mediatype.Docker1Manifest]. + MediaTypeDocker1Manifest = mediatype.Docker1Manifest + // MediaTypeDocker1ManifestSigned + // + // Deprecated: replaced by [mediatype.Docker1ManifestSigned] + MediaTypeDocker1ManifestSigned = mediatype.Docker1ManifestSigned + // MediaTypeDocker2Manifest + // + // Deprecated: replaced by [mediatype.Docker2Manifest]. + MediaTypeDocker2Manifest = mediatype.Docker2Manifest + // MediaTypeDocker2ManifestList + // + // Deprecated: replaced by [mediatype.Docker2ManifestList]. + MediaTypeDocker2ManifestList = mediatype.Docker2ManifestList + // MediaTypeDocker2ImageConfig + // + // Deprecated: replaced by [mediatype.Docker2ImageConfig]. + MediaTypeDocker2ImageConfig = mediatype.Docker2ImageConfig + // MediaTypeOCI1Manifest + // + // Deprecated: replaced by [mediatype.OCI1Manifest]. + MediaTypeOCI1Manifest = mediatype.OCI1Manifest + // MediaTypeOCI1ManifestList + // + // Deprecated: replaced by [mediatype.OCI1ManifestList]. + MediaTypeOCI1ManifestList = mediatype.OCI1ManifestList + // MediaTypeOCI1ImageConfig + // + // Deprecated: replaced by [mediatype.OCI1ImageConfig]. + MediaTypeOCI1ImageConfig = mediatype.OCI1ImageConfig + // MediaTypeDocker2Layer + // + // Deprecated: replaced by [mediatype.Docker2Layer]. + MediaTypeDocker2Layer = mediatype.Docker2LayerGzip + // MediaTypeOCI1Layer + // + // Deprecated: replaced by [mediatype.OCI1Layer]. + MediaTypeOCI1Layer = mediatype.OCI1Layer + // MediaTypeOCI1LayerGzip + // + // Deprecated: replaced by [mediatype.OCI1LayerGzip]. + MediaTypeOCI1LayerGzip = mediatype.OCI1LayerGzip + // MediaTypeBuildkitCacheConfig + // + // Deprecated: replaced by [mediatype.BuildkitCacheConfig]. + MediaTypeBuildkitCacheConfig = mediatype.BuildkitCacheConfig ) diff --git a/regclient/types/ratelimit.go b/regclient/types/ratelimit.go index 65cb7e4..7173819 100644 --- a/regclient/types/ratelimit.go +++ b/regclient/types/ratelimit.go @@ -10,4 +10,6 @@ import ( ) // RateLimit is returned from some http requests +// +// Deprecated: replace with [types.RateLimit]. type RateLimit = topTypes.RateLimit diff --git a/regclient/types/ref.go b/regclient/types/ref.go index 69802a8..591c577 100644 --- a/regclient/types/ref.go +++ b/regclient/types/ref.go @@ -9,6 +9,12 @@ import ( "github.com/regclient/regclient/types/ref" ) +// Ref is used for a reference to an image or repository. +// +// Deprecated: replace with [ref.Ref]. type Ref = ref.Ref +// NewRef create a new [Ref]. +// +// Deprecated: replace with [ref.New]. var NewRef = ref.New diff --git a/repo.go b/repo.go index d4ea01e..c2a2d59 100644 --- a/repo.go +++ b/repo.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/repo" ) @@ -19,7 +19,7 @@ type repoLister interface { func (rc *RegClient) RepoList(ctx context.Context, hostname string, opts ...scheme.RepoOpts) (*repo.RepoList, error) { i := strings.Index(hostname, "/") if i > 0 { - return nil, fmt.Errorf("invalid hostname: %s%.0w", hostname, types.ErrParsingFailed) + return nil, fmt.Errorf("invalid hostname: %s%.0w", hostname, errs.ErrParsingFailed) } schemeAPI, err := rc.schemeGet("reg") if err != nil { @@ -27,7 +27,7 @@ func (rc *RegClient) RepoList(ctx context.Context, hostname string, opts ...sche } rl, ok := schemeAPI.(repoLister) if !ok { - return nil, types.ErrNotImplemented + return nil, errs.ErrNotImplemented } return rl.RepoList(ctx, hostname, opts...) } diff --git a/repo_test.go b/repo_test.go index a076dfb..48ae23b 100644 --- a/repo_test.go +++ b/repo_test.go @@ -9,7 +9,7 @@ import ( "github.com/sirupsen/logrus" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestRepoList(t *testing.T) { @@ -27,7 +27,7 @@ func TestRepoList(t *testing.T) { WithRetryDelay(delayInit, delayMax), ) _, err := rc.RepoList(ctx, "registry.example.com/path") - if !errors.Is(err, types.ErrParsingFailed) { - t.Errorf("RepoList unexpected error on hostname with a path: expected %v, received %v", types.ErrParsingFailed, err) + if !errors.Is(err, errs.ErrParsingFailed) { + t.Errorf("RepoList unexpected error on hostname with a path: expected %v, received %v", errs.ErrParsingFailed, err) } } diff --git a/scheme.go b/scheme.go index 8597fdf..a18a7af 100644 --- a/scheme.go +++ b/scheme.go @@ -5,14 +5,14 @@ import ( "fmt" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) func (rc *RegClient) schemeGet(scheme string) (scheme.API, error) { s, ok := rc.schemes[scheme] if !ok { - return nil, fmt.Errorf("%w: unknown scheme \"%s\"", types.ErrNotImplemented, scheme) + return nil, fmt.Errorf("%w: unknown scheme \"%s\"", errs.ErrNotImplemented, scheme) } return s, nil } diff --git a/scheme/ocidir/blob.go b/scheme/ocidir/blob.go index bf93746..785e2de 100644 --- a/scheme/ocidir/blob.go +++ b/scheme/ocidir/blob.go @@ -16,21 +16,22 @@ import ( "github.com/sirupsen/logrus" "github.com/regclient/regclient/internal/rwfs" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) // BlobDelete removes a blob from the repository. // This method does not verify that blobs are unused. // Calling the [OCIDir.Close] method to trigger the garbage collection is preferred. -func (o *OCIDir) BlobDelete(ctx context.Context, r ref.Ref, d types.Descriptor) error { +func (o *OCIDir) BlobDelete(ctx context.Context, r ref.Ref, d descriptor.Descriptor) error { file := path.Join(r.Path, "blobs", d.Digest.Algorithm().String(), d.Digest.Encoded()) return o.fs.Remove(file) } // BlobGet retrieves a blob, returning a reader -func (o *OCIDir) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) { +func (o *OCIDir) BlobGet(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) { file := path.Join(r.Path, "blobs", d.Digest.Algorithm().String(), d.Digest.Encoded()) fd, err := o.fs.Open(file) if err != nil { @@ -57,7 +58,7 @@ func (o *OCIDir) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (bl } // BlobHead verifies the existence of a blob, the reader contains the headers but no body to read -func (o *OCIDir) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) { +func (o *OCIDir) BlobHead(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) { file := path.Join(r.Path, "blobs", d.Digest.Algorithm().String(), d.Digest.Encoded()) fd, err := o.fs.Open(file) if err != nil { @@ -79,12 +80,12 @@ func (o *OCIDir) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (b } // BlobMount attempts to perform a server side copy of the blob -func (o *OCIDir) BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d types.Descriptor) error { - return types.ErrUnsupported +func (o *OCIDir) BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d descriptor.Descriptor) error { + return errs.ErrUnsupported } // BlobPut sends a blob to the repository, returns the digest and size when successful -func (o *OCIDir) BlobPut(ctx context.Context, r ref.Ref, d types.Descriptor, rdr io.Reader) (types.Descriptor, error) { +func (o *OCIDir) BlobPut(ctx context.Context, r ref.Ref, d descriptor.Descriptor, rdr io.Reader) (descriptor.Descriptor, error) { t := o.throttleGet(r, false) err := t.Acquire(ctx) if err != nil { diff --git a/scheme/ocidir/blob_test.go b/scheme/ocidir/blob_test.go index b14d7b4..d025073 100644 --- a/scheme/ocidir/blob_test.go +++ b/scheme/ocidir/blob_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/regclient/regclient/internal/rwfs" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/ref" ) @@ -151,7 +151,7 @@ func TestBlob(t *testing.T) { go func() { defer wg.Done() bRdr := bytes.NewReader(bBytes) - bpd, err := om.BlobPut(ctx, rPut, types.Descriptor{}, bRdr) + bpd, err := om.BlobPut(ctx, rPut, descriptor.Descriptor{}, bRdr) if err != nil { t.Errorf("blob put: %v", err) return diff --git a/scheme/ocidir/manifest.go b/scheme/ocidir/manifest.go index f92160e..c0e6be3 100644 --- a/scheme/ocidir/manifest.go +++ b/scheme/ocidir/manifest.go @@ -18,8 +18,9 @@ import ( "github.com/regclient/regclient/internal/rwfs" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -29,7 +30,7 @@ func (o *OCIDir) ManifestDelete(ctx context.Context, r ref.Ref, opts ...scheme.M defer o.mu.Unlock() if r.Digest == "" { - return fmt.Errorf("digest required to delete manifest, reference %s%.0w", r.CommonName(), types.ErrMissingDigest) + return fmt.Errorf("digest required to delete manifest, reference %s%.0w", r.CommonName(), errs.ErrMissingDigest) } mc := scheme.ManifestConfig{} @@ -51,7 +52,7 @@ func (o *OCIDir) ManifestDelete(ctx context.Context, r ref.Ref, opts ...scheme.M if err == nil && sDesc != nil && sDesc.MediaType != "" && sDesc.Size > 0 { // attempt to delete the referrer, but ignore if the referrer entry wasn't found err = o.referrerDelete(ctx, r, mc.Manifest) - if err != nil && !errors.Is(err, types.ErrNotFound) && !errors.Is(err, fs.ErrNotExist) { + if err != nil && !errors.Is(err, errs.ErrNotFound) && !errors.Is(err, fs.ErrNotExist) { return err } } @@ -114,7 +115,7 @@ func (o *OCIDir) manifestGet(ctx context.Context, r ref.Ref) (manifest.Manifest, } } if desc.Digest == "" { - return nil, types.ErrNotFound + return nil, errs.ErrNotFound } file := path.Join(r.Path, "blobs", desc.Digest.Algorithm().String(), desc.Digest.Encoded()) fd, err := o.fs.Open(file) @@ -158,13 +159,13 @@ func (o *OCIDir) ManifestHead(ctx context.Context, r ref.Ref) (manifest.Manifest } } if desc.Digest == "" { - return nil, types.ErrNotFound + return nil, errs.ErrNotFound } // verify underlying file exists file := path.Join(r.Path, "blobs", desc.Digest.Algorithm().String(), desc.Digest.Encoded()) fi, err := rwfs.Stat(o.fs, file) if err != nil || fi.IsDir() { - return nil, types.ErrNotFound + return nil, errs.ErrNotFound } // if missing, set media type on desc if desc.MediaType == "" { @@ -185,9 +186,9 @@ func (o *OCIDir) ManifestHead(ctx context.Context, r ref.Ref) (manifest.Manifest desc.MediaType = mt.MediaType desc.Size = int64(len(raw)) } else if mt.SchemaVersion == 1 && len(mt.Signatures) > 0 { - desc.MediaType = types.MediaTypeDocker1ManifestSigned + desc.MediaType = mediatype.Docker1ManifestSigned } else if mt.SchemaVersion == 1 { - desc.MediaType = types.MediaTypeDocker1Manifest + desc.MediaType = mediatype.Docker1Manifest desc.Size = int64(len(raw)) } } diff --git a/scheme/ocidir/manifest_test.go b/scheme/ocidir/manifest_test.go index 4ff7224..8259977 100644 --- a/scheme/ocidir/manifest_test.go +++ b/scheme/ocidir/manifest_test.go @@ -12,8 +12,8 @@ import ( "github.com/opencontainers/go-digest" "github.com/regclient/regclient/internal/rwfs" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" ) @@ -44,8 +44,8 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("manifest get: %v", err) } - if manifest.GetMediaType(ml) != types.MediaTypeOCI1ManifestList { - t.Errorf("manifest mt, expected %s, received %s", types.MediaTypeOCI1ManifestList, manifest.GetMediaType(ml)) + if manifest.GetMediaType(ml) != mediatype.OCI1ManifestList { + t.Errorf("manifest mt, expected %s, received %s", mediatype.OCI1ManifestList, manifest.GetMediaType(ml)) } if !ml.IsList() { t.Errorf("expected manifest list") diff --git a/scheme/ocidir/ocidir.go b/scheme/ocidir/ocidir.go index a499d2c..7ea7315 100644 --- a/scheme/ocidir/ocidir.go +++ b/scheme/ocidir/ocidir.go @@ -15,7 +15,9 @@ import ( "github.com/regclient/regclient/internal/rwfs" "github.com/regclient/regclient/internal/throttle" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" ) @@ -210,7 +212,7 @@ func (o *OCIDir) readIndex(r ref.Ref, locked bool) (v1.Index, error) { return index, nil } -func (o *OCIDir) updateIndex(r ref.Ref, d types.Descriptor, child bool, locked bool) error { +func (o *OCIDir) updateIndex(r ref.Ref, d descriptor.Descriptor, child bool, locked bool) error { if !locked { o.mu.Lock() defer o.mu.Unlock() @@ -331,14 +333,14 @@ func (o *OCIDir) refMod(r ref.Ref) { func indexCreate() v1.Index { i := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{}, + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{}, Annotations: map[string]string{}, } return i } -func indexGet(index v1.Index, r ref.Ref) (types.Descriptor, error) { +func indexGet(index v1.Index, r ref.Ref) (descriptor.Descriptor, error) { if r.Digest == "" && r.Tag == "" { r.Tag = "latest" } @@ -361,10 +363,10 @@ func indexGet(index v1.Index, r ref.Ref) (types.Descriptor, error) { } } } - return types.Descriptor{}, types.ErrNotFound + return descriptor.Descriptor{}, errs.ErrNotFound } -func indexSet(index *v1.Index, r ref.Ref, d types.Descriptor) error { +func indexSet(index *v1.Index, r ref.Ref, d descriptor.Descriptor) error { if index == nil { return fmt.Errorf("index is nil") } @@ -375,7 +377,7 @@ func indexSet(index *v1.Index, r ref.Ref, d types.Descriptor) error { d.Annotations[aOCIRefName] = r.Tag } if index.Manifests == nil { - index.Manifests = []types.Descriptor{} + index.Manifests = []descriptor.Descriptor{} } pos := -1 // search for existing diff --git a/scheme/ocidir/ocidir_test.go b/scheme/ocidir/ocidir_test.go index a43bdd8..1665c0f 100644 --- a/scheme/ocidir/ocidir_test.go +++ b/scheme/ocidir/ocidir_test.go @@ -8,7 +8,9 @@ import ( "github.com/regclient/regclient/internal/rwfs" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" ) @@ -61,29 +63,29 @@ func TestIndex(t *testing.T) { if err != nil { t.Errorf("failed to generate ref: %v", err) } - descNoTag := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + descNoTag := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: dig1, } - descA := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + descA := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: dig2, Annotations: map[string]string{ aOCIRefName: "tag-a", }, } - descB := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + descB := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: dig2, Annotations: map[string]string{ aOCIRefName: "tag-b", }, } - descC := types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + descC := descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: dig3, Annotations: map[string]string{ @@ -94,23 +96,23 @@ func TestIndex(t *testing.T) { name string index v1.Index get ref.Ref - expectGet types.Descriptor + expectGet descriptor.Descriptor expectGetErr error set ref.Ref - setDesc types.Descriptor + setDesc descriptor.Descriptor expectLen int }{ { name: "empty", get: rA, - expectGetErr: types.ErrNotFound, + expectGetErr: errs.ErrNotFound, }, { name: "no tag", index: v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ descNoTag, }, }, @@ -124,8 +126,8 @@ func TestIndex(t *testing.T) { name: "tag a", index: v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ descNoTag, descA, }, @@ -140,8 +142,8 @@ func TestIndex(t *testing.T) { name: "tag b", index: v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ descNoTag, descB, }, @@ -156,8 +158,8 @@ func TestIndex(t *testing.T) { name: "tag c", index: v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ descA, descC, }, diff --git a/scheme/ocidir/referrer.go b/scheme/ocidir/referrer.go index ca2354e..a1cfe42 100644 --- a/scheme/ocidir/referrer.go +++ b/scheme/ocidir/referrer.go @@ -6,8 +6,9 @@ import ( "fmt" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -66,11 +67,11 @@ func (o *OCIDir) referrerList(ctx context.Context, r ref.Ref, opts ...scheme.Ref } m, err := o.manifestGet(ctx, rlTag) if err != nil { - if errors.Is(err, types.ErrNotFound) { + if errors.Is(err, errs.ErrNotFound) { // empty list, initialize a new manifest rl.Manifest, err = manifest.New(manifest.WithOrig(v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, })) if err != nil { return rl, err @@ -98,7 +99,7 @@ func (o *OCIDir) referrerDelete(ctx context.Context, r ref.Ref, m manifest.Manif // get refers field mSubject, ok := m.(manifest.Subjecter) if !ok { - return fmt.Errorf("manifest does not support subject: %w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support subject: %w", errs.ErrUnsupportedMediaType) } subject, err := mSubject.GetSubject() if err != nil { @@ -106,7 +107,7 @@ func (o *OCIDir) referrerDelete(ctx context.Context, r ref.Ref, m manifest.Manif } // validate/set subject descriptor if subject == nil || subject.MediaType == "" || subject.Digest == "" || subject.Size <= 0 { - return fmt.Errorf("subject is not set%.0w", types.ErrNotFound) + return fmt.Errorf("subject is not set%.0w", errs.ErrNotFound) } // get descriptor for subject @@ -142,7 +143,7 @@ func (o *OCIDir) referrerPut(ctx context.Context, r ref.Ref, m manifest.Manifest // get subject field mSubject, ok := m.(manifest.Subjecter) if !ok { - return fmt.Errorf("manifest does not support subject: %w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support subject: %w", errs.ErrUnsupportedMediaType) } subject, err := mSubject.GetSubject() if err != nil { @@ -150,7 +151,7 @@ func (o *OCIDir) referrerPut(ctx context.Context, r ref.Ref, m manifest.Manifest } // validate/set subject descriptor if subject == nil || subject.MediaType == "" || subject.Digest == "" || subject.Size <= 0 { - return fmt.Errorf("subject is not set%.0w", types.ErrNotFound) + return fmt.Errorf("subject is not set%.0w", errs.ErrNotFound) } // get descriptor for subject diff --git a/scheme/ocidir/referrer_test.go b/scheme/ocidir/referrer_test.go index 97f8825..d988425 100644 --- a/scheme/ocidir/referrer_test.go +++ b/scheme/ocidir/referrer_test.go @@ -11,8 +11,9 @@ import ( "github.com/regclient/regclient/internal/rwfs" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -75,15 +76,15 @@ func TestReferrer(t *testing.T) { } artifactA := v1.Manifest{ Versioned: v1.ManifestSchemaVersion, - MediaType: types.MediaTypeOCI1Manifest, - Config: types.Descriptor{ + MediaType: mediatype.OCI1Manifest, + Config: descriptor.Descriptor{ MediaType: aType, Size: 8, Digest: digest1, }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 8, Digest: digest2, }, @@ -104,11 +105,11 @@ func TestReferrer(t *testing.T) { timeAnnot: "2021-02-03T04:05:06Z", } artifactB := v1.ArtifactManifest{ - MediaType: types.MediaTypeOCI1Artifact, + MediaType: mediatype.OCI1Artifact, ArtifactType: bType, - Blobs: []types.Descriptor{ + Blobs: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 8, Digest: digest2, }, @@ -125,11 +126,11 @@ func TestReferrer(t *testing.T) { t.Fatalf("failed extracting raw body from artifact: %v", err) } artifactC := v1.ArtifactManifest{ - MediaType: types.MediaTypeOCI1Artifact, + MediaType: mediatype.OCI1Artifact, ArtifactType: cType, - Blobs: []types.Descriptor{ + Blobs: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 8, Digest: digest2, }, @@ -182,7 +183,7 @@ func TestReferrer(t *testing.T) { if err != nil { t.Fatalf("Failed creating getRef: %v", err) } - rl, err := o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{SortAnnotation: timeAnnot})) + rl, err := o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{SortAnnotation: timeAnnot})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } @@ -190,7 +191,7 @@ func TestReferrer(t *testing.T) { t.Fatalf("descriptor list length, expected 2, received %d", len(rl.Descriptors)) } // expecting artifact A in index 0 - if rl.Descriptors[0].MediaType != types.MediaTypeOCI1Manifest || + if rl.Descriptors[0].MediaType != mediatype.OCI1Manifest || rl.Descriptors[0].Size != int64(len(artifactABody)) || rl.Descriptors[0].Digest != artifactAM.GetDescriptor().Digest || rl.Descriptors[0].ArtifactType != aType || @@ -198,7 +199,7 @@ func TestReferrer(t *testing.T) { t.Errorf("returned descriptor A mismatch: %v", rl.Descriptors[0]) } // expecting artifact B in index 1 - if rl.Descriptors[1].MediaType != types.MediaTypeOCI1Artifact || + if rl.Descriptors[1].MediaType != mediatype.OCI1Artifact || rl.Descriptors[1].Size != int64(len(artifactBBody)) || rl.Descriptors[1].Digest != artifactBM.GetDescriptor().Digest || rl.Descriptors[1].ArtifactType != bType || @@ -208,7 +209,7 @@ func TestReferrer(t *testing.T) { if len(rl.Tags) != 1 || rl.Tags[0] != tagRef { t.Errorf("tag list missing entries, received: %v", rl.Tags) } - rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{SortAnnotation: timeAnnot, SortDesc: true})) + rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{SortAnnotation: timeAnnot, SortDesc: true})) if err != nil { t.Fatalf("Failed running ReferrerList reverse: %v", err) } @@ -226,14 +227,14 @@ func TestReferrer(t *testing.T) { if err != nil { t.Fatalf("Failed creating getRef: %v", err) } - rl, err := o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: aType})) + rl, err := o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: aType})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } if len(rl.Descriptors) != 1 { t.Fatalf("descriptor list length, expected 1, received %d", len(rl.Descriptors)) } - rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: "application/vnd.example.unknown"})) + rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: "application/vnd.example.unknown"})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } @@ -246,21 +247,21 @@ func TestReferrer(t *testing.T) { if err != nil { t.Fatalf("Failed creating getRef: %v", err) } - rl, err := o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: map[string]string{extraAnnot: extraValueB}})) + rl, err := o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: map[string]string{extraAnnot: extraValueB}})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } if len(rl.Descriptors) != 1 { t.Fatalf("descriptor list length, expected 1, received %d", len(rl.Descriptors)) } - rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: map[string]string{extraAnnot: "unknown value"}})) + rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: map[string]string{extraAnnot: "unknown value"}})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } if len(rl.Descriptors) > 0 { t.Fatalf("unexpected descriptors") } - rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: map[string]string{extraAnnot: ""}})) + rl, err = o.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: map[string]string{extraAnnot: ""}})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } diff --git a/scheme/ocidir/tag.go b/scheme/ocidir/tag.go index 619aaaf..3fa8a2c 100644 --- a/scheme/ocidir/tag.go +++ b/scheme/ocidir/tag.go @@ -8,7 +8,8 @@ import ( "strings" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" "github.com/regclient/regclient/types/tag" ) @@ -22,7 +23,7 @@ func (o *OCIDir) TagDelete(ctx context.Context, r ref.Ref) error { func (o *OCIDir) tagDelete(ctx context.Context, r ref.Ref) error { if r.Tag == "" { - return types.ErrMissingTag + return errs.ErrMissingTag } // get index index, err := o.readIndex(r, true) @@ -38,7 +39,7 @@ func (o *OCIDir) tagDelete(ctx context.Context, r ref.Ref) error { } } if !changed { - return fmt.Errorf("failed deleting %s: %w", r.CommonName(), types.ErrNotFound) + return fmt.Errorf("failed deleting %s: %w", r.CommonName(), errs.ErrNotFound) } // push manifest back out err = o.writeIndex(r, index, true) @@ -83,7 +84,7 @@ func (o *OCIDir) TagList(ctx context.Context, r ref.Ref, opts ...scheme.TagOpts) t, err := tag.New( tag.WithRaw(ib), tag.WithRef(r), - tag.WithMT(types.MediaTypeOCI1ManifestList), + tag.WithMT(mediatype.OCI1ManifestList), tag.WithLayoutIndex(index), tag.WithTags(tl), ) diff --git a/scheme/ocidir/tag_test.go b/scheme/ocidir/tag_test.go index 994508e..323431e 100644 --- a/scheme/ocidir/tag_test.go +++ b/scheme/ocidir/tag_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/regclient/regclient/internal/rwfs" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -50,7 +50,7 @@ func TestTag(t *testing.T) { exTags := []string{"broken", "v0.3"} rCp.Tag = "missing" err := oMem.TagDelete(ctx, rCp) - if err == nil || !errors.Is(err, types.ErrNotFound) { + if err == nil || !errors.Is(err, errs.ErrNotFound) { t.Errorf("deleting missing tag %s: %v", rCp.CommonName(), err) } rCp.Tag = "latest" diff --git a/scheme/reg/blob.go b/scheme/reg/blob.go index 6949f57..c9d2e7b 100644 --- a/scheme/reg/blob.go +++ b/scheme/reg/blob.go @@ -19,8 +19,9 @@ import ( "github.com/sirupsen/logrus" "github.com/regclient/regclient/internal/reghttp" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -29,7 +30,7 @@ var ( ) // BlobDelete removes a blob from the repository -func (reg *Reg) BlobDelete(ctx context.Context, r ref.Ref, d types.Descriptor) error { +func (reg *Reg) BlobDelete(ctx context.Context, r ref.Ref, d descriptor.Descriptor) error { req := ®http.Req{ Host: r.Registry, APIs: map[string]reghttp.ReqAPI{ @@ -51,7 +52,7 @@ func (reg *Reg) BlobDelete(ctx context.Context, r ref.Ref, d types.Descriptor) e } // BlobGet retrieves a blob from the repository, returning a blob reader -func (reg *Reg) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) { +func (reg *Reg) BlobGet(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) { // build/send request req := ®http.Req{ Host: r.Registry, @@ -99,7 +100,7 @@ func (reg *Reg) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (blo b := blob.NewReader( blob.WithRef(r), blob.WithReader(resp), - blob.WithDesc(types.Descriptor{ + blob.WithDesc(descriptor.Descriptor{ Digest: d.Digest, }), blob.WithResp(resp.HTTPResponse()), @@ -108,7 +109,7 @@ func (reg *Reg) BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (blo } // BlobHead is used to verify if a blob exists and is accessible -func (reg *Reg) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) { +func (reg *Reg) BlobHead(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) { // build/send request req := ®http.Req{ Host: r.Registry, @@ -156,7 +157,7 @@ func (reg *Reg) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (bl b := blob.NewReader( blob.WithRef(r), - blob.WithDesc(types.Descriptor{ + blob.WithDesc(descriptor.Descriptor{ Digest: d.Digest, }), blob.WithResp(resp.HTTPResponse()), @@ -165,7 +166,7 @@ func (reg *Reg) BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (bl } // BlobMount attempts to perform a server side copy/mount of the blob between repositories -func (reg *Reg) BlobMount(ctx context.Context, rSrc ref.Ref, rTgt ref.Ref, d types.Descriptor) error { +func (reg *Reg) BlobMount(ctx context.Context, rSrc ref.Ref, rTgt ref.Ref, d descriptor.Descriptor) error { putURL, _, err := reg.blobMount(ctx, rTgt, d, rSrc) // if mount fails and returns an upload location, cancel that upload if err != nil { @@ -181,7 +182,7 @@ func (reg *Reg) BlobMount(ctx context.Context, rSrc ref.Ref, rTgt ref.Ref, d typ // This will attempt an anonymous blob mount first which some registries may support. // It will then try doing a full put of the blob without chunking (most widely supported). // If the full put fails, it will fall back to a chunked upload (useful for flaky networks). -func (reg *Reg) BlobPut(ctx context.Context, r ref.Ref, d types.Descriptor, rdr io.Reader) (types.Descriptor, error) { +func (reg *Reg) BlobPut(ctx context.Context, r ref.Ref, d descriptor.Descriptor, rdr io.Reader) (descriptor.Descriptor, error) { var putURL *url.URL var err error validDesc := (d.Digest != "" && d.Size > 0) || (d.Size == 0 && d.Digest == zeroDig) @@ -192,7 +193,7 @@ func (reg *Reg) BlobPut(ctx context.Context, r ref.Ref, d types.Descriptor, rdr if err == nil { return d, nil } - if err != types.ErrMountReturnedLocation { + if err != errs.ErrMountReturnedLocation { putURL = nil } } @@ -289,7 +290,7 @@ func (reg *Reg) blobGetUploadURL(ctx context.Context, r ref.Ref) (*url.URL, erro // Extract the location into a new putURL based on whether it's relative, fqdn with a scheme, or without a scheme. location := resp.HTTPResponse().Header.Get("Location") if location == "" { - return nil, fmt.Errorf("failed to send blob post, ref %s: %w", r.CommonName(), types.ErrMissingLocation) + return nil, fmt.Errorf("failed to send blob post, ref %s: %w", r.CommonName(), errs.ErrMissingLocation) } reg.log.WithFields(logrus.Fields{ "location": location, @@ -307,7 +308,7 @@ func (reg *Reg) blobGetUploadURL(ctx context.Context, r ref.Ref) (*url.URL, erro return putURL, nil } -func (reg *Reg) blobMount(ctx context.Context, rTgt ref.Ref, d types.Descriptor, rSrc ref.Ref) (*url.URL, string, error) { +func (reg *Reg) blobMount(ctx context.Context, rTgt ref.Ref, d descriptor.Descriptor, rSrc ref.Ref) (*url.URL, string, error) { // build/send request query := url.Values{} query.Set("mount", d.Digest.String()) @@ -378,14 +379,14 @@ func (reg *Reg) blobMount(ctx context.Context, rTgt ref.Ref, d types.Descriptor, "err": err, }).Warn("Mount location header failed to parse") } else { - return putURL, uuid, types.ErrMountReturnedLocation + return putURL, uuid, errs.ErrMountReturnedLocation } } // all other responses unhandled return nil, "", fmt.Errorf("failed to mount blob, digest %s, ref %s: %w", d.Digest.String(), rTgt.CommonName(), reghttp.HTTPError(resp.HTTPResponse().StatusCode)) } -func (reg *Reg) blobPutUploadFull(ctx context.Context, r ref.Ref, d types.Descriptor, putURL *url.URL, rdr io.Reader) error { +func (reg *Reg) blobPutUploadFull(ctx context.Context, r ref.Ref, d descriptor.Descriptor, putURL *url.URL, rdr io.Reader) error { // append digest to request to use the monolithic upload option if putURL.RawQuery != "" { putURL.RawQuery = putURL.RawQuery + "&digest=" + url.QueryEscape(d.Digest.String()) @@ -400,11 +401,11 @@ func (reg *Reg) blobPutUploadFull(ctx context.Context, r ref.Ref, d types.Descri if readOnce { rdrSeek, ok := rdr.(io.ReadSeeker) if !ok { - return nil, fmt.Errorf("blob source is not a seeker%.0w", types.ErrNotRetryable) + return nil, fmt.Errorf("blob source is not a seeker%.0w", errs.ErrNotRetryable) } _, err := rdrSeek.Seek(0, io.SeekStart) if err != nil { - return nil, fmt.Errorf("seek on blob source failed: %w%.0w", err, types.ErrNotRetryable) + return nil, fmt.Errorf("seek on blob source failed: %w%.0w", err, errs.ErrNotRetryable) } } readOnce = true @@ -445,7 +446,7 @@ func (reg *Reg) blobPutUploadFull(ctx context.Context, r ref.Ref, d types.Descri return nil } -func (reg *Reg) blobPutUploadChunked(ctx context.Context, r ref.Ref, d types.Descriptor, putURL *url.URL, rdr io.Reader) (types.Descriptor, error) { +func (reg *Reg) blobPutUploadChunked(ctx context.Context, r ref.Ref, d descriptor.Descriptor, putURL *url.URL, rdr io.Reader) (descriptor.Descriptor, error) { host := reg.hostGet(r.Registry) bufSize := host.BlobChunk if bufSize <= 0 { @@ -534,7 +535,7 @@ func (reg *Reg) blobPutUploadChunked(ctx context.Context, r ref.Ref, d types.Des NoMirrors: true, } resp, err := reg.reghttp.Do(ctx, req) - if err != nil && !errors.Is(err, types.ErrHTTPStatus) && !errors.Is(err, types.ErrNotFound) { + if err != nil && !errors.Is(err, errs.ErrHTTPStatus) && !errors.Is(err, errs.ErrNotFound) { return d, fmt.Errorf("failed to send blob (chunk), ref %s: http do: %w", r.CommonName(), err) } err = resp.Close() @@ -596,10 +597,10 @@ func (reg *Reg) blobPutUploadChunked(ctx context.Context, r ref.Ref, d types.Des // compute digest dOut := digester.Digest() if d.Digest != "" && dOut != d.Digest { - return d, fmt.Errorf("%w, expected %s, computed %s", types.ErrDigestMismatch, d.Digest.String(), dOut.String()) + return d, fmt.Errorf("%w, expected %s, computed %s", errs.ErrDigestMismatch, d.Digest.String(), dOut.String()) } if d.Size != 0 && chunkStart != d.Size { - return d, fmt.Errorf("blob content size does not match descriptor, expected %d, received %d%.0w", d.Size, chunkStart, types.ErrMismatch) + return d, fmt.Errorf("blob content size does not match descriptor, expected %d, received %d%.0w", d.Size, chunkStart, errs.ErrMismatch) } d.Digest = dOut d.Size = chunkStart diff --git a/scheme/reg/blob_test.go b/scheme/reg/blob_test.go index 4ea5d62..00bb5a8 100644 --- a/scheme/reg/blob_test.go +++ b/scheme/reg/blob_test.go @@ -19,7 +19,8 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -237,7 +238,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobGet(ctx, r, types.Descriptor{Digest: d1}) + br, err := reg.BlobGet(ctx, r, descriptor.Descriptor{Digest: d1}) if err != nil { t.Fatalf("Failed running BlobGet: %v", err) } @@ -256,7 +257,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobHead(ctx, r, types.Descriptor{Digest: d1}) + br, err := reg.BlobHead(ctx, r, descriptor.Descriptor{Digest: d1}) if err != nil { t.Fatalf("Failed running BlobHead: %v", err) } @@ -272,7 +273,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobGet(ctx, r, types.Descriptor{Digest: d1, URLs: []string{tsURL.Scheme + "://" + tsURL.Host + "/external/" + d1.String()}}) + br, err := reg.BlobGet(ctx, r, descriptor.Descriptor{Digest: d1, URLs: []string{tsURL.Scheme + "://" + tsURL.Host + "/external/" + d1.String()}}) if err != nil { t.Fatalf("Failed running external BlobGet: %v", err) } @@ -291,7 +292,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobHead(ctx, r, types.Descriptor{Digest: d1, URLs: []string{tsURL.Scheme + "://" + tsURL.Host + "/external/" + d1.String()}}) + br, err := reg.BlobHead(ctx, r, descriptor.Descriptor{Digest: d1, URLs: []string{tsURL.Scheme + "://" + tsURL.Host + "/external/" + d1.String()}}) if err != nil { t.Fatalf("Failed running external BlobHead: %v", err) } @@ -306,12 +307,12 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobGet(ctx, r, types.Descriptor{Digest: dMissing}) + br, err := reg.BlobGet(ctx, r, descriptor.Descriptor{Digest: dMissing}) if err == nil { defer br.Close() t.Fatalf("Unexpected success running BlobGet") } - if !errors.Is(err, types.ErrNotFound) { + if !errors.Is(err, errs.ErrNotFound) { t.Errorf("Error does not match \"ErrNotFound\": %v", err) } }) @@ -321,7 +322,7 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobGet(ctx, r, types.Descriptor{Digest: d2}) + br, err := reg.BlobGet(ctx, r, descriptor.Descriptor{Digest: d2}) if err != nil { t.Fatalf("Failed running BlobGet: %v", err) } @@ -340,12 +341,12 @@ func TestBlobGet(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - br, err := reg.BlobGet(ctx, r, types.Descriptor{Digest: d1}) + br, err := reg.BlobGet(ctx, r, descriptor.Descriptor{Digest: d1}) if err == nil { defer br.Close() t.Fatalf("Unexpected success running BlobGet") } - if !errors.Is(err, types.ErrHTTPUnauthorized) { + if !errors.Is(err, errs.ErrHTTPUnauthorized) { t.Errorf("Error does not match \"ErrUnauthorized\": %v", err) } }) @@ -1196,7 +1197,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob1) - dp, err := reg.BlobPut(ctx, r, types.Descriptor{Digest: d1, Size: int64(len(blob1))}, br) + dp, err := reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: d1, Size: int64(len(blob1))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -1215,7 +1216,7 @@ func TestBlobPut(t *testing.T) { } br := bytes.NewReader(blob2) mt := "application/vnd.example.test" - dp, err := reg.BlobPut(ctx, r, types.Descriptor{MediaType: mt, Digest: d2, Size: int64(len(blob2))}, br) + dp, err := reg.BlobPut(ctx, r, descriptor.Descriptor{MediaType: mt, Digest: d2, Size: int64(len(blob2))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -1236,12 +1237,12 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob2) - _, err = reg.BlobPut(ctx, r, types.Descriptor{Digest: d2, Size: int64(len(blob2))}, io.NopCloser(br)) + _, err = reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: d2, Size: int64(len(blob2))}, io.NopCloser(br)) if err == nil { t.Fatalf("Blob put succeeded on a gateway timeout") } - if !errors.Is(err, types.ErrHTTPStatus) { - t.Errorf("unexpected err, expected %v, received %v", types.ErrHTTPStatus, err) + if !errors.Is(err, errs.ErrHTTPStatus) { + t.Errorf("unexpected err, expected %v, received %v", errs.ErrHTTPStatus, err) } }) @@ -1252,9 +1253,9 @@ func TestBlobPut(t *testing.T) { } br := bytes.NewReader(blob2) mt := "application/vnd.example.test" - _, err = reg.BlobPut(ctx, r, types.Descriptor{MediaType: mt, Digest: d2Bad, Size: int64(len(blob2))}, br) - if err == nil || !errors.Is(err, types.ErrDigestMismatch) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrDigestMismatch, err) + _, err = reg.BlobPut(ctx, r, descriptor.Descriptor{MediaType: mt, Digest: d2Bad, Size: int64(len(blob2))}, br) + if err == nil || !errors.Is(err, errs.ErrDigestMismatch) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrDigestMismatch, err) } }) @@ -1265,9 +1266,9 @@ func TestBlobPut(t *testing.T) { } br := bytes.NewReader(blob2) mt := "application/vnd.example.test" - _, err = reg.BlobPut(ctx, r, types.Descriptor{MediaType: mt, Digest: d2, Size: int64(len(blob2) - 2)}, br) - if err == nil || !errors.Is(err, types.ErrMismatch) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrMismatch, err) + _, err = reg.BlobPut(ctx, r, descriptor.Descriptor{MediaType: mt, Digest: d2, Size: int64(len(blob2) - 2)}, br) + if err == nil || !errors.Is(err, errs.ErrMismatch) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrMismatch, err) } }) @@ -1277,7 +1278,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob3) - dp, err := reg.BlobPut(ctx, r, types.Descriptor{Digest: d3, Size: int64(len(blob3))}, br) + dp, err := reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: d3, Size: int64(len(blob3))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -1295,7 +1296,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob4) - dp, err := reg.BlobPut(ctx, r, types.Descriptor{Digest: d4, Size: int64(len(blob4))}, br) + dp, err := reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: d4, Size: int64(len(blob4))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -1314,7 +1315,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob5) - dp, err := reg.BlobPut(ctx, r, types.Descriptor{}, br) + dp, err := reg.BlobPut(ctx, r, descriptor.Descriptor{}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } @@ -1333,7 +1334,7 @@ func TestBlobPut(t *testing.T) { t.Fatalf("Failed creating ref: %v", err) } br := bytes.NewReader(blob6) - dp, err := reg.BlobPut(ctx, r, types.Descriptor{Digest: d6, Size: int64(len(blob6))}, br) + dp, err := reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: d6, Size: int64(len(blob6))}, br) if err != nil { t.Fatalf("Failed running BlobPut: %v", err) } diff --git a/scheme/reg/manifest.go b/scheme/reg/manifest.go index 0ebf6fc..b649365 100644 --- a/scheme/reg/manifest.go +++ b/scheme/reg/manifest.go @@ -13,8 +13,9 @@ import ( "github.com/regclient/regclient/internal/limitread" "github.com/regclient/regclient/internal/reghttp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -22,7 +23,7 @@ import ( // This will implicitly delete all tags pointing to that manifest. func (reg *Reg) ManifestDelete(ctx context.Context, r ref.Ref, opts ...scheme.ManifestOpts) error { if r.Digest == "" { - return fmt.Errorf("digest required to delete manifest, reference %s%.0w", r.CommonName(), types.ErrMissingDigest) + return fmt.Errorf("digest required to delete manifest, reference %s%.0w", r.CommonName(), errs.ErrMissingDigest) } mc := scheme.ManifestConfig{} @@ -43,7 +44,7 @@ func (reg *Reg) ManifestDelete(ctx context.Context, r ref.Ref, opts ...scheme.Ma if err == nil && sDesc != nil && sDesc.MediaType != "" && sDesc.Size > 0 { // attempt to delete the referrer, but ignore if the referrer entry wasn't found err = reg.referrerDelete(ctx, r, mc.Manifest) - if err != nil && !errors.Is(err, types.ErrNotFound) { + if err != nil && !errors.Is(err, errs.ErrNotFound) { return err } } @@ -88,19 +89,19 @@ func (reg *Reg) ManifestGet(ctx context.Context, r ref.Ref) (manifest.Manifest, } else if r.Tag != "" { tagOrDigest = r.Tag } else { - return nil, fmt.Errorf("reference missing tag and digest: %s%.0w", r.CommonName(), types.ErrMissingTagOrDigest) + return nil, fmt.Errorf("reference missing tag and digest: %s%.0w", r.CommonName(), errs.ErrMissingTagOrDigest) } // build/send request headers := http.Header{ "Accept": []string{ - types.MediaTypeOCI1ManifestList, - types.MediaTypeOCI1Manifest, - types.MediaTypeDocker2ManifestList, - types.MediaTypeDocker2Manifest, - types.MediaTypeDocker1ManifestSigned, - types.MediaTypeDocker1Manifest, - types.MediaTypeOCI1Artifact, + mediatype.OCI1ManifestList, + mediatype.OCI1Manifest, + mediatype.Docker2ManifestList, + mediatype.Docker2Manifest, + mediatype.Docker1ManifestSigned, + mediatype.Docker1Manifest, + mediatype.OCI1Artifact, }, } req := ®http.Req{ @@ -126,7 +127,7 @@ func (reg *Reg) ManifestGet(ctx context.Context, r ref.Ref) (manifest.Manifest, // limit length size, _ := strconv.Atoi(resp.HTTPResponse().Header.Get("Content-Length")) if size > 0 && reg.manifestMaxPull > 0 && int64(size) > reg.manifestMaxPull { - return nil, fmt.Errorf("manifest too large, received %d, limit %d: %s%.0w", size, reg.manifestMaxPull, r.CommonName(), types.ErrSizeLimitExceeded) + return nil, fmt.Errorf("manifest too large, received %d, limit %d: %s%.0w", size, reg.manifestMaxPull, r.CommonName(), errs.ErrSizeLimitExceeded) } rdr := &limitread.LimitRead{ Reader: resp, @@ -165,19 +166,19 @@ func (reg *Reg) ManifestHead(ctx context.Context, r ref.Ref) (manifest.Manifest, } else if r.Tag != "" { tagOrDigest = r.Tag } else { - return nil, fmt.Errorf("reference missing tag and digest: %s%.0w", r.CommonName(), types.ErrMissingTagOrDigest) + return nil, fmt.Errorf("reference missing tag and digest: %s%.0w", r.CommonName(), errs.ErrMissingTagOrDigest) } // build/send request headers := http.Header{ "Accept": []string{ - types.MediaTypeOCI1ManifestList, - types.MediaTypeOCI1Manifest, - types.MediaTypeDocker2ManifestList, - types.MediaTypeDocker2Manifest, - types.MediaTypeDocker1ManifestSigned, - types.MediaTypeDocker1Manifest, - types.MediaTypeOCI1Artifact, + mediatype.OCI1ManifestList, + mediatype.OCI1Manifest, + mediatype.Docker2ManifestList, + mediatype.Docker2Manifest, + mediatype.Docker1ManifestSigned, + mediatype.Docker1Manifest, + mediatype.OCI1Artifact, }, } req := ®http.Req{ @@ -217,7 +218,7 @@ func (reg *Reg) ManifestPut(ctx context.Context, r ref.Ref, m manifest.Manifest, reg.log.WithFields(logrus.Fields{ "ref": r.Reference, }).Warn("Manifest put requires a tag") - return types.ErrMissingTag + return errs.ErrMissingTag } // create the request body @@ -232,7 +233,7 @@ func (reg *Reg) ManifestPut(ctx context.Context, r ref.Ref, m manifest.Manifest, // limit length if reg.manifestMaxPush > 0 && int64(len(mj)) > reg.manifestMaxPush { - return fmt.Errorf("manifest too large, calculated %d, limit %d: %s%.0w", len(mj), reg.manifestMaxPush, r.CommonName(), types.ErrSizeLimitExceeded) + return fmt.Errorf("manifest too large, calculated %d, limit %d: %s%.0w", len(mj), reg.manifestMaxPush, r.CommonName(), errs.ErrSizeLimitExceeded) } // build/send request diff --git a/scheme/reg/manifest_test.go b/scheme/reg/manifest_test.go index 110a167..3558b23 100644 --- a/scheme/reg/manifest_test.go +++ b/scheme/reg/manifest_test.go @@ -18,9 +18,11 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -37,14 +39,14 @@ func TestManifest(t *testing.T) { digest1 := digest.FromString("example1") digest2 := digest.FromString("example2") m := schema2.Manifest{ - Config: types.Descriptor{ - MediaType: types.MediaTypeDocker2ImageConfig, + Config: descriptor.Descriptor{ + MediaType: mediatype.Docker2ImageConfig, Size: 8, Digest: digest1, }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeDocker2LayerGzip, + MediaType: mediatype.Docker2LayerGzip, Size: 8, Digest: digest2, }, @@ -68,7 +70,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -84,7 +86,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -100,7 +102,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, }, @@ -115,7 +117,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, }, @@ -130,7 +132,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -146,7 +148,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen+defaultManifestMaxPull)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -162,7 +164,7 @@ func TestManifest(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen+10)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -184,7 +186,7 @@ func TestManifest(t *testing.T) { Method: "PUT", Path: "/v2" + repoPath + "/manifests/" + putTag, Headers: http.Header{ - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Content-Length": {fmt.Sprintf("%d", mLen)}, }, Body: mBody, @@ -254,7 +256,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestGet: %v", err) } - if manifest.GetMediaType(mGet) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mGet) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mGet)) } if mGet.GetDescriptor().Digest != mDigest { @@ -270,7 +272,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestHead: %v", err) } - if manifest.GetMediaType(mHead) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mHead) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mHead)) } if mHead.GetDescriptor().Digest != mDigest { @@ -285,8 +287,8 @@ func TestManifest(t *testing.T) { mNohead, err := reg.ManifestHead(ctx, noheadRef) if err == nil { t.Errorf("Unexpected successful head on \"no head\" registry: %v", mNohead) - } else if !errors.Is(err, types.ErrUnsupportedAPI) { - t.Errorf("Expected error, expected %v, received %v", types.ErrUnsupportedAPI, err) + } else if !errors.Is(err, errs.ErrUnsupportedAPI) { + t.Errorf("Expected error, expected %v, received %v", errs.ErrUnsupportedAPI, err) } }) t.Run("Get No Head", func(t *testing.T) { @@ -298,7 +300,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestGet: %v", err) } - if manifest.GetMediaType(mNohead) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mNohead) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mNohead)) } if mNohead.GetDescriptor().Digest != mDigest { @@ -324,7 +326,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestGet: %v", err) } - if manifest.GetMediaType(mGet) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mGet) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mGet)) } if mGet.GetDescriptor().Digest != mDigest { @@ -340,7 +342,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestHead: %v", err) } - if manifest.GetMediaType(mHead) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mHead) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mHead)) } if mHead.GetDescriptor().Digest != mDigest { @@ -356,7 +358,7 @@ func TestManifest(t *testing.T) { if err != nil { t.Fatalf("Failed running ManifestGet: %v", err) } - if manifest.GetMediaType(mGet) != types.MediaTypeDocker2Manifest { + if manifest.GetMediaType(mGet) != mediatype.Docker2Manifest { t.Errorf("Unexpected media type: %s", manifest.GetMediaType(mGet)) } if mGet.GetDescriptor().Digest != mDigest { @@ -381,8 +383,8 @@ func TestManifest(t *testing.T) { if err == nil { t.Fatalf("ManifestGet did not fail") } - if !errors.Is(err, types.ErrSizeLimitExceeded) { - t.Fatalf("unexpected error, expected %v, received %v", types.ErrSizeLimitExceeded, err) + if !errors.Is(err, errs.ErrSizeLimitExceeded) { + t.Fatalf("unexpected error, expected %v, received %v", errs.ErrSizeLimitExceeded, err) } }) t.Run("Read beyond size", func(t *testing.T) { @@ -394,8 +396,8 @@ func TestManifest(t *testing.T) { if err == nil { t.Fatalf("ManifestGet did not fail") } - if !errors.Is(err, types.ErrShortRead) && !errors.Is(err, io.ErrUnexpectedEOF) { - t.Fatalf("unexpected error, expected %v, received %v", types.ErrShortRead, err) + if !errors.Is(err, errs.ErrShortRead) && !errors.Is(err, io.ErrUnexpectedEOF) { + t.Fatalf("unexpected error, expected %v, received %v", errs.ErrShortRead, err) } }) @@ -431,8 +433,8 @@ func TestManifest(t *testing.T) { if err == nil { t.Fatalf("put manifest did not fail") } - if !errors.Is(err, types.ErrSizeLimitExceeded) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrSizeLimitExceeded, err) + if !errors.Is(err, errs.ErrSizeLimitExceeded) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrSizeLimitExceeded, err) } }) } diff --git a/scheme/reg/ping_test.go b/scheme/reg/ping_test.go index c0ead16..9cac0bc 100644 --- a/scheme/reg/ping_test.go +++ b/scheme/reg/ping_test.go @@ -15,7 +15,7 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -146,8 +146,8 @@ func TestPing(t *testing.T) { result, err := reg.Ping(ctx, r) if err == nil { t.Fatalf("ping did not fail") - } else if !errors.Is(err, types.ErrHTTPUnauthorized) { - t.Fatalf("unexpected error, expected %v, received %v", types.ErrHTTPUnauthorized, err) + } else if !errors.Is(err, errs.ErrHTTPUnauthorized) { + t.Fatalf("unexpected error, expected %v, received %v", errs.ErrHTTPUnauthorized, err) } if result.Header == nil { t.Errorf("headers missing") @@ -163,8 +163,8 @@ func TestPing(t *testing.T) { result, err := reg.Ping(ctx, r) if err == nil { t.Fatalf("ping did not fail") - } else if !errors.Is(err, types.ErrNotFound) { - t.Fatalf("unexpected error, expected %v, received %v", types.ErrNotFound, err) + } else if !errors.Is(err, errs.ErrNotFound) { + t.Fatalf("unexpected error, expected %v, received %v", errs.ErrNotFound, err) } if result.Header == nil { t.Errorf("headers missing") diff --git a/scheme/reg/referrer.go b/scheme/reg/referrer.go index 5b47b3f..1603308 100644 --- a/scheme/reg/referrer.go +++ b/scheme/reg/referrer.go @@ -10,8 +10,9 @@ import ( "github.com/regclient/regclient/internal/httplink" "github.com/regclient/regclient/internal/reghttp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -203,7 +204,7 @@ func (reg *Reg) referrerListByAPIPage(ctx context.Context, r ref.Ref, config sch } ociML, ok := m.GetOrig().(v1.Index) if !ok { - return rl, nil, fmt.Errorf("unexpected manifest type for referrers: %s, %w", m.GetDescriptor().MediaType, types.ErrUnsupportedMediaType) + return rl, nil, fmt.Errorf("unexpected manifest type for referrers: %s, %w", m.GetDescriptor().MediaType, errs.ErrUnsupportedMediaType) } rl.Manifest = m rl.Descriptors = ociML.Manifests @@ -223,11 +224,11 @@ func (reg *Reg) referrerListByTag(ctx context.Context, r ref.Ref) (referrer.Refe } m, err := reg.ManifestGet(ctx, rlTag) if err != nil { - if errors.Is(err, types.ErrNotFound) { + if errors.Is(err, errs.ErrNotFound) { // empty list, initialize a new manifest rl.Manifest, err = manifest.New(manifest.WithOrig(v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, })) if err != nil { return rl, err @@ -253,7 +254,7 @@ func (reg *Reg) referrerDelete(ctx context.Context, r ref.Ref, m manifest.Manife // get subject field mSubject, ok := m.(manifest.Subjecter) if !ok { - return fmt.Errorf("manifest does not support the subject field: %w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support the subject field: %w", errs.ErrUnsupportedMediaType) } subject, err := mSubject.GetSubject() if err != nil { @@ -261,7 +262,7 @@ func (reg *Reg) referrerDelete(ctx context.Context, r ref.Ref, m manifest.Manife } // validate/set subject descriptor if subject == nil || subject.MediaType == "" || subject.Digest == "" || subject.Size <= 0 { - return fmt.Errorf("refers is not set%.0w", types.ErrNotFound) + return fmt.Errorf("refers is not set%.0w", errs.ErrNotFound) } // remove from cache @@ -302,7 +303,7 @@ func (reg *Reg) referrerPut(ctx context.Context, r ref.Ref, m manifest.Manifest) // get subject field mSubject, ok := m.(manifest.Subjecter) if !ok { - return fmt.Errorf("manifest does not support the subject field: %w", types.ErrUnsupportedMediaType) + return fmt.Errorf("manifest does not support the subject field: %w", errs.ErrUnsupportedMediaType) } subject, err := mSubject.GetSubject() if err != nil { @@ -310,7 +311,7 @@ func (reg *Reg) referrerPut(ctx context.Context, r ref.Ref, m manifest.Manifest) } // validate/set subject descriptor if subject == nil || subject.MediaType == "" || subject.Digest == "" || subject.Size <= 0 { - return fmt.Errorf("subject is not set%.0w", types.ErrNotFound) + return fmt.Errorf("subject is not set%.0w", errs.ErrNotFound) } // lock to avoid internal race conditions between pulling and pushing tag diff --git a/scheme/reg/referrer_test.go b/scheme/reg/referrer_test.go index ccc077d..6b014d6 100644 --- a/scheme/reg/referrer_test.go +++ b/scheme/reg/referrer_test.go @@ -17,9 +17,10 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -43,14 +44,14 @@ func TestReferrer(t *testing.T) { // manifest being referenced m := schema2.Manifest{ Versioned: schema2.ManifestSchemaVersion, - Config: types.Descriptor{ - MediaType: types.MediaTypeDocker2ImageConfig, + Config: descriptor.Descriptor{ + MediaType: mediatype.Docker2ImageConfig, Size: 8, Digest: digest1, }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeDocker2LayerGzip, + MediaType: mediatype.Docker2LayerGzip, Size: 8, Digest: digest2, }, @@ -65,9 +66,9 @@ func TestReferrer(t *testing.T) { // manifest list mList := schema2.ManifestList{ Versioned: schema2.ManifestListSchemaVersion, - Manifests: []types.Descriptor{ + Manifests: []descriptor.Descriptor{ { - MediaType: types.MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Digest: mDigest, Size: int64(mLen), Platform: &platform.Platform{ @@ -76,7 +77,7 @@ func TestReferrer(t *testing.T) { }, }, { - MediaType: types.MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Digest: digest.FromString("missing"), Size: int64(1234), Platform: &platform.Platform{ @@ -98,22 +99,22 @@ func TestReferrer(t *testing.T) { } artifact := v1.Manifest{ Versioned: v1.ManifestSchemaVersion, - MediaType: types.MediaTypeOCI1Manifest, - Config: types.Descriptor{ + MediaType: mediatype.OCI1Manifest, + Config: descriptor.Descriptor{ MediaType: configMTA, Size: 8, Digest: digest1, }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 8, Digest: digest2, }, }, Annotations: artifactAnnot, - Subject: &types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + Subject: &descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(mLen), Digest: mDigest, }, @@ -131,18 +132,18 @@ func TestReferrer(t *testing.T) { extraAnnot: extraValue2, } artifact2 := v1.ArtifactManifest{ - MediaType: types.MediaTypeOCI1Artifact, + MediaType: mediatype.OCI1Artifact, ArtifactType: configMTB, - Blobs: []types.Descriptor{ + Blobs: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 8, Digest: digest2, }, }, Annotations: artifact2Annot, - Subject: &types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + Subject: &descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(mLen), Digest: mDigest, }, @@ -159,7 +160,7 @@ func TestReferrer(t *testing.T) { // empty response emptyReply := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, } emptyBody, err := json.Marshal(emptyReply) if err != nil { @@ -170,10 +171,10 @@ func TestReferrer(t *testing.T) { // a response replyA := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, ArtifactType: configMTA, Size: int64(len(artifactBody)), Digest: artifactM.GetDescriptor().Digest, @@ -190,10 +191,10 @@ func TestReferrer(t *testing.T) { // a response replyB := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1Artifact, + MediaType: mediatype.OCI1Artifact, ArtifactType: configMTB, Size: int64(len(artifact2Body)), Digest: artifact2M.GetDescriptor().Digest, @@ -210,17 +211,17 @@ func TestReferrer(t *testing.T) { // full response replyBoth := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, ArtifactType: configMTA, Size: int64(len(artifactBody)), Digest: artifactM.GetDescriptor().Digest, Annotations: artifactAnnot, }, { - MediaType: types.MediaTypeOCI1Artifact, + MediaType: mediatype.OCI1Artifact, ArtifactType: configMTB, Size: int64(len(artifact2Body)), Digest: artifact2M.GetDescriptor().Digest, @@ -249,7 +250,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, }, @@ -264,7 +265,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mLen)}, - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Docker-Content-Digest": []string{mDigest.String()}, }, Body: mBody, @@ -280,7 +281,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mlLen)}, - "Content-Type": []string{types.MediaTypeDocker2ManifestList}, + "Content-Type": []string{mediatype.Docker2ManifestList}, "Docker-Content-Digest": []string{mlDigest.String()}, }, }, @@ -295,7 +296,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", mlLen)}, - "Content-Type": []string{types.MediaTypeDocker2ManifestList}, + "Content-Type": []string{mediatype.Docker2ManifestList}, "Docker-Content-Digest": []string{mlDigest.String()}, }, Body: mlBody, @@ -312,7 +313,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", len(artifactBody))}, - "Content-Type": []string{types.MediaTypeOCI1Manifest}, + "Content-Type": []string{mediatype.OCI1Manifest}, "Docker-Content-Digest": []string{string(artifactDigest)}, }, Body: artifactBody, @@ -329,7 +330,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", len(artifact2Body))}, - "Content-Type": []string{types.MediaTypeOCI1Artifact}, + "Content-Type": []string{mediatype.OCI1Artifact}, "Docker-Content-Digest": []string{string(artifact2Digest)}, }, Body: artifact2Body, @@ -420,7 +421,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyALen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyADig.String()}, }, Body: replyABody, @@ -437,7 +438,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyBothLen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyBothDig.String()}, }, Body: replyBothBody, @@ -528,7 +529,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyALen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyADig.String()}, }, Body: replyABody, @@ -545,7 +546,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyBothLen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyBothDig.String()}, }, Body: replyBothBody, @@ -646,7 +647,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", emptyLen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{emptyDigest.String()}, }, Body: emptyBody, @@ -663,7 +664,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyALen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyADig.String()}, }, Body: replyABody, @@ -683,7 +684,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyBLen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyBDig.String()}, }, Body: replyBBody, @@ -700,7 +701,7 @@ func TestReferrer(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", replyALen)}, - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Docker-Content-Digest": []string{replyADig.String()}, "Link": []string{fmt.Sprintf(`; rel="next"`, repoPath, mDigest.String())}, }, @@ -848,7 +849,7 @@ func TestReferrer(t *testing.T) { if len(rl.Descriptors) < 1 { t.Fatalf("descriptor list missing") } - if rl.Descriptors[0].MediaType != types.MediaTypeOCI1Manifest || + if rl.Descriptors[0].MediaType != mediatype.OCI1Manifest || rl.Descriptors[0].Size != int64(len(artifactBody)) || rl.Descriptors[0].Digest != artifactM.GetDescriptor().Digest || !mapStringStringEq(rl.Descriptors[0].Annotations, artifactAnnot) { @@ -870,7 +871,7 @@ func TestReferrer(t *testing.T) { if len(rl.Descriptors) < 1 { t.Fatalf("descriptor list missing") } - if rl.Descriptors[0].MediaType != types.MediaTypeOCI1Manifest || + if rl.Descriptors[0].MediaType != mediatype.OCI1Manifest || rl.Descriptors[0].Size != int64(len(artifactBody)) || rl.Descriptors[0].Digest != artifactM.GetDescriptor().Digest || !mapStringStringEq(rl.Descriptors[0].Annotations, artifactAnnot) { @@ -892,7 +893,7 @@ func TestReferrer(t *testing.T) { if len(rl.Descriptors) < 1 { t.Fatalf("descriptor list missing") } - if rl.Descriptors[0].MediaType != types.MediaTypeOCI1Manifest || + if rl.Descriptors[0].MediaType != mediatype.OCI1Manifest || rl.Descriptors[0].Size != int64(len(artifactBody)) || rl.Descriptors[0].Digest != artifactM.GetDescriptor().Digest || !mapStringStringEq(rl.Descriptors[0].Annotations, artifactAnnot) { @@ -938,14 +939,14 @@ func TestReferrer(t *testing.T) { if len(rl.Descriptors) != 2 { t.Fatalf("descriptor list expected 2, received %d", len(rl.Descriptors)) } - if rl.Descriptors[0].MediaType != types.MediaTypeOCI1Manifest || + if rl.Descriptors[0].MediaType != mediatype.OCI1Manifest || rl.Descriptors[0].Size != int64(len(artifactBody)) || rl.Descriptors[0].Digest != artifactM.GetDescriptor().Digest || rl.Descriptors[0].ArtifactType != configMTA || !mapStringStringEq(rl.Descriptors[0].Annotations, artifactAnnot) { t.Errorf("returned descriptor mismatch: %v", rl.Descriptors[0]) } - if rl.Descriptors[1].MediaType != types.MediaTypeOCI1Artifact || + if rl.Descriptors[1].MediaType != mediatype.OCI1Artifact || rl.Descriptors[1].Size != int64(len(artifact2Body)) || rl.Descriptors[1].Digest != artifact2M.GetDescriptor().Digest || rl.Descriptors[1].ArtifactType != configMTB || @@ -968,14 +969,14 @@ func TestReferrer(t *testing.T) { if len(rl.Descriptors) != 2 { t.Fatalf("descriptor list expected 2, received %d", len(rl.Descriptors)) } - if rl.Descriptors[0].MediaType != types.MediaTypeOCI1Manifest || + if rl.Descriptors[0].MediaType != mediatype.OCI1Manifest || rl.Descriptors[0].Size != int64(len(artifactBody)) || rl.Descriptors[0].Digest != artifactM.GetDescriptor().Digest || rl.Descriptors[0].ArtifactType != configMTA || !mapStringStringEq(rl.Descriptors[0].Annotations, artifactAnnot) { t.Errorf("returned descriptor mismatch: %v", rl.Descriptors[0]) } - if rl.Descriptors[1].MediaType != types.MediaTypeOCI1Artifact || + if rl.Descriptors[1].MediaType != mediatype.OCI1Artifact || rl.Descriptors[1].Size != int64(len(artifact2Body)) || rl.Descriptors[1].Digest != artifact2M.GetDescriptor().Digest || rl.Descriptors[1].ArtifactType != configMTB || @@ -992,14 +993,14 @@ func TestReferrer(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - rl, err := reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: configMTA})) + rl, err := reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: configMTA})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } if len(rl.Descriptors) != 1 { t.Fatalf("descriptor list mismatch: %v", rl.Descriptors) } - rl, err = reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{ArtifactType: "application/vnd.example.unknown"})) + rl, err = reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{ArtifactType: "application/vnd.example.unknown"})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } @@ -1012,21 +1013,21 @@ func TestReferrer(t *testing.T) { if err != nil { t.Fatalf("Failed creating ref: %v", err) } - rl, err := reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: map[string]string{extraAnnot: extraValue2}})) + rl, err := reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: map[string]string{extraAnnot: extraValue2}})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } if len(rl.Descriptors) != 1 { t.Fatalf("descriptor list mismatch: %v", rl.Descriptors) } - rl, err = reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: map[string]string{extraAnnot: "unknown value"}})) + rl, err = reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: map[string]string{extraAnnot: "unknown value"}})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } if len(rl.Descriptors) > 0 { t.Fatalf("unexpected descriptors: %v", rl.Descriptors) } - rl, err = reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(types.MatchOpt{Annotations: map[string]string{extraAnnot: ""}})) + rl, err = reg.ReferrerList(ctx, r, scheme.WithReferrerMatchOpt(descriptor.MatchOpt{Annotations: map[string]string{extraAnnot: ""}})) if err != nil { t.Fatalf("Failed running ReferrerList: %v", err) } diff --git a/scheme/reg/repo.go b/scheme/reg/repo.go index 7cf981a..cea8f0a 100644 --- a/scheme/reg/repo.go +++ b/scheme/reg/repo.go @@ -12,7 +12,7 @@ import ( "github.com/regclient/regclient/internal/reghttp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/repo" ) @@ -65,7 +65,7 @@ func (reg *Reg) RepoList(ctx context.Context, hostname string, opts ...scheme.Re }).Warn("Failed to read repo list") return nil, fmt.Errorf("failed to read repo list for %s: %w", hostname, err) } - mt := types.MediaTypeBase(resp.HTTPResponse().Header.Get("Content-Type")) + mt := mediatype.Base(resp.HTTPResponse().Header.Get("Content-Type")) rl, err := repo.New( repo.WithMT(mt), repo.WithRaw(respBody), diff --git a/scheme/reg/repo_test.go b/scheme/reg/repo_test.go index 9060ad8..85e3e4e 100644 --- a/scheme/reg/repo_test.go +++ b/scheme/reg/repo_test.go @@ -17,7 +17,7 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestRepo(t *testing.T) { @@ -227,8 +227,8 @@ func TestRepo(t *testing.T) { _, err := reg.RepoList(ctx, host) if err == nil { t.Errorf("unexpected success listing repos on disabled registry") - } else if !errors.Is(err, types.ErrHTTPStatus) { - t.Errorf("unexpected error: expected %v, received %v", types.ErrHTTPStatus, err) + } else if !errors.Is(err, errs.ErrHTTPStatus) { + t.Errorf("unexpected error: expected %v, received %v", errs.ErrHTTPStatus, err) } }) // test with unknown media-type header @@ -238,8 +238,8 @@ func TestRepo(t *testing.T) { _, err := reg.RepoList(ctx, host) if err == nil { t.Errorf("unexpected success listing repos on unknown-mt registry") - } else if !errors.Is(err, types.ErrUnsupportedMediaType) { - t.Errorf("unexpected error: expected %v, received %v", types.ErrUnsupportedMediaType, err) + } else if !errors.Is(err, errs.ErrUnsupportedMediaType) { + t.Errorf("unexpected error: expected %v, received %v", errs.ErrUnsupportedMediaType, err) } }) // test with parsing errors diff --git a/scheme/reg/tag.go b/scheme/reg/tag.go index b0f15e4..30a4748 100644 --- a/scheme/reg/tag.go +++ b/scheme/reg/tag.go @@ -22,9 +22,11 @@ import ( "github.com/regclient/regclient/internal/httplink" "github.com/regclient/regclient/internal/reghttp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -37,7 +39,7 @@ import ( func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error { var tempManifest manifest.Manifest if r.Tag == "" { - return types.ErrMissingTag + return errs.ErrMissingTag } // attempt to delete the tag directly, available in OCI distribution-spec, and Hub API @@ -70,7 +72,7 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error { // lookup the current manifest media type curManifest, err := reg.ManifestHead(ctx, r) - if err != nil && errors.Is(err, types.ErrUnsupportedAPI) { + if err != nil && errors.Is(err, errs.ErrUnsupportedAPI) { curManifest, err = reg.ManifestGet(ctx, r) } if err != nil { @@ -102,7 +104,7 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error { RootFS: v1.RootFS{ Type: "layers", DiffIDs: []digest.Digest{ - types.EmptyDigest, + descriptor.EmptyDigest, }, }, } @@ -120,20 +122,20 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error { // create manifest with config, matching the original tag manifest type switch manifest.GetMediaType(curManifest) { - case types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList: + case mediatype.OCI1Manifest, mediatype.OCI1ManifestList: tempManifest, err = manifest.New(manifest.WithOrig(v1.Manifest{ Versioned: v1.ManifestSchemaVersion, - MediaType: types.MediaTypeOCI1Manifest, - Config: types.Descriptor{ - MediaType: types.MediaTypeOCI1ImageConfig, + MediaType: mediatype.OCI1Manifest, + Config: descriptor.Descriptor{ + MediaType: mediatype.OCI1ImageConfig, Digest: confDigest, Size: int64(len(confB)), }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeOCI1Layer, - Size: int64(len(types.EmptyData)), - Digest: types.EmptyDigest, + MediaType: mediatype.OCI1Layer, + Size: int64(len(descriptor.EmptyData)), + Digest: descriptor.EmptyDigest, }, }, })) @@ -143,16 +145,16 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error { default: // default to the docker v2 schema tempManifest, err = manifest.New(manifest.WithOrig(schema2.Manifest{ Versioned: schema2.ManifestSchemaVersion, - Config: types.Descriptor{ - MediaType: types.MediaTypeDocker2ImageConfig, + Config: descriptor.Descriptor{ + MediaType: mediatype.Docker2ImageConfig, Digest: confDigest, Size: int64(len(confB)), }, - Layers: []types.Descriptor{ + Layers: []descriptor.Descriptor{ { - MediaType: types.MediaTypeDocker2LayerGzip, - Size: int64(len(types.EmptyData)), - Digest: types.EmptyDigest, + MediaType: mediatype.Docker2LayerGzip, + Size: int64(len(descriptor.EmptyData)), + Digest: descriptor.EmptyDigest, }, }, })) @@ -165,13 +167,13 @@ func (reg *Reg) TagDelete(ctx context.Context, r ref.Ref) error { }).Debug("Sending dummy manifest to replace tag") // push empty layer - _, err = reg.BlobPut(ctx, r, types.Descriptor{Digest: types.EmptyDigest, Size: int64(len(types.EmptyData))}, bytes.NewReader(types.EmptyData)) + _, err = reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: descriptor.EmptyDigest, Size: int64(len(descriptor.EmptyData))}, bytes.NewReader(descriptor.EmptyData)) if err != nil { return err } // push config - _, err = reg.BlobPut(ctx, r, types.Descriptor{Digest: confDigest, Size: int64(len(confB))}, bytes.NewReader(confB)) + _, err = reg.BlobPut(ctx, r, descriptor.Descriptor{Digest: confDigest, Size: int64(len(confB))}, bytes.NewReader(confB)) if err != nil { return fmt.Errorf("failed sending dummy config to delete %s: %w", r.CommonName(), err) } diff --git a/scheme/reg/tag_test.go b/scheme/reg/tag_test.go index 63fdf5d..73b74c7 100644 --- a/scheme/reg/tag_test.go +++ b/scheme/reg/tag_test.go @@ -19,7 +19,8 @@ import ( "github.com/regclient/regclient/config" "github.com/regclient/regclient/internal/reqresp" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/ref" ) @@ -188,7 +189,7 @@ func TestTag(t *testing.T) { Status: http.StatusOK, Headers: http.Header{ "Content-Length": {fmt.Sprintf("%d", len(delFallbackManifest))}, - "Content-Type": {types.MediaTypeDocker2Manifest}, + "Content-Type": {mediatype.Docker2Manifest}, "Docker-Content-Digest": {delFallbackDigest.String()}, }, }, @@ -229,7 +230,7 @@ func TestTag(t *testing.T) { Method: "PUT", Path: "/v2" + repoPath + "/manifests/" + delFallbackTag, Headers: http.Header{ - "Content-Type": {types.MediaTypeDocker2Manifest}, + "Content-Type": {mediatype.Docker2Manifest}, }, }, RespEntry: reqresp.RespEntry{ @@ -347,8 +348,8 @@ func TestTag(t *testing.T) { _, err = reg.TagList(ctx, listRef) if err == nil { t.Fatalf("tag listing succeeded on missing repo") - } else if !errors.Is(err, types.ErrNotFound) { - t.Fatalf("unexpected error: expected %v, received %v", types.ErrNotFound, err) + } else if !errors.Is(err, errs.ErrNotFound) { + t.Fatalf("unexpected error: expected %v, received %v", errs.ErrNotFound, err) } }) diff --git a/scheme/scheme.go b/scheme/scheme.go index 5e9d02c..f55c727 100644 --- a/scheme/scheme.go +++ b/scheme/scheme.go @@ -6,8 +6,8 @@ import ( "io" "github.com/regclient/regclient/internal/throttle" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/blob" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/manifest" "github.com/regclient/regclient/types/ping" "github.com/regclient/regclient/types/ref" @@ -18,15 +18,15 @@ import ( // API is used to interface between different methods to store images. type API interface { // BlobDelete removes a blob from the repository. - BlobDelete(ctx context.Context, r ref.Ref, d types.Descriptor) error + BlobDelete(ctx context.Context, r ref.Ref, d descriptor.Descriptor) error // BlobGet retrieves a blob, returning a reader. - BlobGet(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) + BlobGet(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) // BlobHead verifies the existence of a blob, the reader contains the headers but no body to read. - BlobHead(ctx context.Context, r ref.Ref, d types.Descriptor) (blob.Reader, error) + BlobHead(ctx context.Context, r ref.Ref, d descriptor.Descriptor) (blob.Reader, error) // BlobMount attempts to perform a server side copy of the blob. - BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d types.Descriptor) error + BlobMount(ctx context.Context, refSrc ref.Ref, refTgt ref.Ref, d descriptor.Descriptor) error // BlobPut sends a blob to the repository, returns the digest and size when successful. - BlobPut(ctx context.Context, r ref.Ref, d types.Descriptor, rdr io.Reader) (types.Descriptor, error) + BlobPut(ctx context.Context, r ref.Ref, d descriptor.Descriptor, rdr io.Reader) (descriptor.Descriptor, error) // ManifestDelete removes a manifest, including all tags that point to that manifest. ManifestDelete(ctx context.Context, r ref.Ref, opts ...ManifestOpts) error @@ -105,15 +105,15 @@ func WithManifest(m manifest.Manifest) ManifestOpts { // ReferrerConfig is used by schemes to import [ReferrerOpts]. type ReferrerConfig struct { - MatchOpt types.MatchOpt // filter/sort results - Platform string // get referrers for a specific platform + MatchOpt descriptor.MatchOpt // filter/sort results + Platform string // get referrers for a specific platform } // ReferrerOpts is used to set options on referrer APIs. type ReferrerOpts func(*ReferrerConfig) -// WithReferrerMatchOpt filters results using [types.MatchOpt]. -func WithReferrerMatchOpt(mo types.MatchOpt) ReferrerOpts { +// WithReferrerMatchOpt filters results using [descriptor.MatchOpt]. +func WithReferrerMatchOpt(mo descriptor.MatchOpt) ReferrerOpts { return func(config *ReferrerConfig) { config.MatchOpt = mo } @@ -167,7 +167,7 @@ func ReferrerFilter(config ReferrerConfig, rlIn referrer.ReferrerList) referrer. Manifest: rlIn.Manifest, Annotations: rlIn.Annotations, Tags: rlIn.Tags, - Descriptors: types.DescriptorListFilter(rlIn.Descriptors, config.MatchOpt), + Descriptors: descriptor.DescriptorListFilter(rlIn.Descriptors, config.MatchOpt), } } diff --git a/tag.go b/tag.go index 5fe80ad..6b52280 100644 --- a/tag.go +++ b/tag.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/regclient/regclient/scheme" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" "github.com/regclient/regclient/types/tag" ) @@ -18,7 +18,7 @@ import ( // 3. Delete the digest for that new manifest that is only used by that tag. func (rc *RegClient) TagDelete(ctx context.Context, r ref.Ref) error { if !r.IsSet() { - return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { @@ -30,7 +30,7 @@ func (rc *RegClient) TagDelete(ctx context.Context, r ref.Ref) error { // TagList returns a tag list from a repository func (rc *RegClient) TagList(ctx context.Context, r ref.Ref, opts ...scheme.TagOpts) (*tag.List, error) { if !r.IsSetRepo() { - return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), types.ErrInvalidReference) + return nil, fmt.Errorf("ref is not set: %s%.0w", r.CommonName(), errs.ErrInvalidReference) } schemeAPI, err := rc.schemeGet(r.Scheme) if err != nil { diff --git a/types/blob/blob.go b/types/blob/blob.go index f5a486d..86d215c 100644 --- a/types/blob/blob.go +++ b/types/blob/blob.go @@ -7,7 +7,7 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" ) @@ -15,7 +15,7 @@ import ( // Blob interface is used for returning blobs. type Blob interface { // GetDescriptor returns the descriptor associated with the blob. - GetDescriptor() types.Descriptor + GetDescriptor() descriptor.Descriptor // RawBody returns the raw content of the blob. RawBody() ([]byte, error) // RawHeaders returns the headers received from the registry. @@ -38,7 +38,7 @@ type Blob interface { } type blobConfig struct { - desc types.Descriptor + desc descriptor.Descriptor header http.Header image *v1.Image r ref.Ref @@ -51,7 +51,7 @@ type blobConfig struct { type Opts func(*blobConfig) // WithDesc specifies the descriptor associated with the blob. -func WithDesc(d types.Descriptor) Opts { +func WithDesc(d descriptor.Descriptor) Opts { return func(bc *blobConfig) { bc.desc = d } diff --git a/types/blob/blob_test.go b/types/blob/blob_test.go index 93a152d..9ad4e5a 100644 --- a/types/blob/blob_test.go +++ b/types/blob/blob_test.go @@ -12,7 +12,9 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" ) @@ -53,19 +55,19 @@ var ( `) exLen = int64(len(exBlob)) exDigest = digest.FromBytes(exBlob) - exMT = types.MediaTypeDocker2ImageConfig + exMT = mediatype.Docker2ImageConfig exHeaders = http.Header{ - "Content-Type": {types.MediaTypeDocker2ImageConfig}, + "Content-Type": {mediatype.Docker2ImageConfig}, "Content-Length": {fmt.Sprintf("%d", exLen)}, "Docker-Content-Digest": {exDigest.String()}, } exHeadersShort = http.Header{ - "Content-Type": {types.MediaTypeDocker2ImageConfig}, + "Content-Type": {mediatype.Docker2ImageConfig}, "Content-Length": {fmt.Sprintf("%d", exLen-5)}, "Docker-Content-Digest": {exDigest.String()}, } exHeadersLong = http.Header{ - "Content-Type": {types.MediaTypeDocker2ImageConfig}, + "Content-Type": {mediatype.Docker2ImageConfig}, "Content-Length": {fmt.Sprintf("%d", exLen+5)}, "Docker-Content-Digest": {exDigest.String()}, } @@ -76,7 +78,7 @@ var ( ContentLength: exLen, Body: io.NopCloser(bytes.NewReader(exBlob)), } - exDesc = types.Descriptor{ + exDesc = descriptor.Descriptor{ MediaType: exMT, Digest: exDigest, Size: exLen, @@ -111,7 +113,7 @@ func TestCommon(t *testing.T) { name: "descriptor", opts: []Opts{ WithReader(bytes.NewReader(exBlob)), - WithDesc(types.Descriptor{ + WithDesc(descriptor.Descriptor{ MediaType: exMT, Digest: exDigest, Size: exLen, @@ -160,7 +162,7 @@ func TestCommon(t *testing.T) { eHeaders: exHeadersShort, eLen: exLen, eMT: exMT, - eErr: types.ErrSizeLimitExceeded, + eErr: errs.ErrSizeLimitExceeded, }, { name: "short read", @@ -174,7 +176,7 @@ func TestCommon(t *testing.T) { eHeaders: exHeadersLong, eLen: exLen, eMT: exMT, - eErr: types.ErrShortRead, + eErr: errs.ErrShortRead, }, } for _, tc := range tt { @@ -296,8 +298,8 @@ func TestReader(t *testing.T) { if err == nil { t.Fatalf("readall did not fail") } - if !errors.Is(err, types.ErrSizeLimitExceeded) { - t.Errorf("unexpected error on readall, expected %v, received %v", types.ErrSizeLimitExceeded, err) + if !errors.Is(err, errs.ErrSizeLimitExceeded) { + t.Errorf("unexpected error on readall, expected %v, received %v", errs.ErrSizeLimitExceeded, err) } }) @@ -305,7 +307,7 @@ func TestReader(t *testing.T) { // create blob b := NewReader( WithReader(bytes.NewReader(exBlob)), - WithDesc(types.Descriptor{ + WithDesc(descriptor.Descriptor{ MediaType: exMT, Digest: exDigest, Size: exLen, @@ -357,7 +359,7 @@ func TestOCI(t *testing.T) { fromJSON []byte wantRaw []byte wantJSON []byte - wantDesc types.Descriptor + wantDesc descriptor.Descriptor }{ { name: "RawBody", @@ -383,7 +385,7 @@ func TestOCI(t *testing.T) { opts: []Opts{ WithImage(ociConfig), }, - wantDesc: types.Descriptor{MediaType: types.MediaTypeOCI1ImageConfig}, + wantDesc: descriptor.Descriptor{MediaType: mediatype.OCI1ImageConfig}, }, { name: "Config with Docker Desc", @@ -391,7 +393,7 @@ func TestOCI(t *testing.T) { WithImage(ociConfig), WithDesc(exDesc), }, - wantDesc: types.Descriptor{MediaType: exMT}, + wantDesc: descriptor.Descriptor{MediaType: exMT}, }, } @@ -440,7 +442,7 @@ func TestOCI(t *testing.T) { // create blob oc := NewOCIConfig( WithRawBody(exBlob), - WithDesc(types.Descriptor{ + WithDesc(descriptor.Descriptor{ MediaType: exMT, Digest: exDigest, Size: exLen, @@ -493,8 +495,8 @@ func TestTarReader(t *testing.T) { { name: "good desc", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1Layer, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1Layer, Size: fhSize, Digest: dig, }), @@ -503,8 +505,8 @@ func TestTarReader(t *testing.T) { { name: "bad desc", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1Layer, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1Layer, Size: fhSize, Digest: digest.FromString("bad digest"), }), @@ -573,7 +575,7 @@ func TestReadFile(t *testing.T) { { name: "layer2", filename: "layer2.txt", - expectErr: types.ErrFileDeleted, + expectErr: errs.ErrFileDeleted, }, { name: "layer3", @@ -583,12 +585,12 @@ func TestReadFile(t *testing.T) { { name: "opaque dir", filename: "exdir/test.txt", - expectErr: types.ErrFileDeleted, + expectErr: errs.ErrFileDeleted, }, { name: "missing", filename: "missing.txt", - expectErr: types.ErrFileNotFound, + expectErr: errs.ErrFileNotFound, }, { name: "invalid", @@ -607,7 +609,7 @@ func TestReadFile(t *testing.T) { if err != nil { t.Fatalf("failed to open test data: %v", err) } - btr := NewTarReader(WithReader(fh), WithDesc(types.Descriptor{Size: int64(len(fileBytes)), Digest: blobDigest, MediaType: types.MediaTypeOCI1Layer})) + btr := NewTarReader(WithReader(fh), WithDesc(descriptor.Descriptor{Size: int64(len(fileBytes)), Digest: blobDigest, MediaType: mediatype.OCI1Layer})) defer btr.Close() th, rdr, err := btr.ReadFile(tc.filename) if tc.expectErr != nil { @@ -649,12 +651,12 @@ func TestReadFile(t *testing.T) { if err != nil { t.Fatalf("failed to open test data: %v", err) } - btr := NewTarReader(WithReader(fh), WithDesc(types.Descriptor{Size: int64(len(fileBytes)), Digest: digest.FromString("bad digest"), MediaType: types.MediaTypeOCI1Layer})) + btr := NewTarReader(WithReader(fh), WithDesc(descriptor.Descriptor{Size: int64(len(fileBytes)), Digest: digest.FromString("bad digest"), MediaType: mediatype.OCI1Layer})) _, _, err = btr.ReadFile("missing.txt") if err == nil { t.Errorf("ReadFile did not fail") - } else if !errors.Is(err, types.ErrDigestMismatch) { - t.Errorf("unexpected error, expected %v, received %v", types.ErrDigestMismatch, err) + } else if !errors.Is(err, errs.ErrDigestMismatch) { + t.Errorf("unexpected error, expected %v, received %v", errs.ErrDigestMismatch, err) } }) } diff --git a/types/blob/common.go b/types/blob/common.go index a3c89ee..44e652a 100644 --- a/types/blob/common.go +++ b/types/blob/common.go @@ -9,7 +9,7 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/ref" ) @@ -19,14 +19,14 @@ type Common = *BCommon // BCommon is a common struct for all blobs which includes various shared methods. type BCommon struct { r ref.Ref - desc types.Descriptor + desc descriptor.Descriptor blobSet bool rawHeader http.Header resp *http.Response } // GetDescriptor returns the descriptor associated with the blob. -func (c *BCommon) GetDescriptor() types.Descriptor { +func (c *BCommon) GetDescriptor() descriptor.Descriptor { return c.desc } diff --git a/types/blob/ociconfig.go b/types/blob/ociconfig.go index 1645bbc..a3db153 100644 --- a/types/blob/ociconfig.go +++ b/types/blob/ociconfig.go @@ -10,7 +10,7 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" ) @@ -50,7 +50,7 @@ func NewOCIConfig(opts ...Opts) *BOCIConfig { bc.desc.Digest = digest.FromBytes(bc.rawBody) bc.desc.Size = int64(len(bc.rawBody)) if bc.desc.MediaType == "" { - bc.desc.MediaType = types.MediaTypeOCI1ImageConfig + bc.desc.MediaType = mediatype.OCI1ImageConfig } } b := BOCIConfig{ @@ -91,7 +91,7 @@ func (oc *BOCIConfig) SetConfig(image v1.Image) { oc.image = image oc.rawBody, _ = json.Marshal(oc.image) if oc.desc.MediaType == "" { - oc.desc.MediaType = types.MediaTypeOCI1ImageConfig + oc.desc.MediaType = mediatype.OCI1ImageConfig } oc.desc.Digest = digest.FromBytes(oc.rawBody) oc.desc.Size = int64(len(oc.rawBody)) @@ -119,7 +119,7 @@ func (oc *BOCIConfig) UnmarshalJSON(data []byte) error { oc.rawBody = make([]byte, len(data)) copy(oc.rawBody, data) if oc.desc.MediaType == "" { - oc.desc.MediaType = types.MediaTypeOCI1ImageConfig + oc.desc.MediaType = mediatype.OCI1ImageConfig } oc.desc.Digest = digest.FromBytes(oc.rawBody) oc.desc.Size = int64(len(oc.rawBody)) diff --git a/types/blob/reader.go b/types/blob/reader.go index f135b97..51985e1 100644 --- a/types/blob/reader.go +++ b/types/blob/reader.go @@ -12,7 +12,8 @@ import ( "github.com/opencontainers/go-digest" "github.com/regclient/regclient/internal/limitread" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" ) // Reader was previously an interface. A type alias is provided for upgrading. @@ -45,7 +46,7 @@ func NewReader(opts ...Opts) *BReader { if bc.header != nil { // extract fields from header if descriptor not passed if bc.desc.MediaType == "" { - bc.desc.MediaType = types.MediaTypeBase(bc.header.Get("Content-Type")) + bc.desc.MediaType = mediatype.Base(bc.header.Get("Content-Type")) } if bc.desc.Size == 0 { cl, _ := strconv.Atoi(bc.header.Get("Content-Length")) @@ -109,15 +110,15 @@ func (r *BReader) Read(p []byte) (int, error) { if r.desc.Size == 0 { r.desc.Size = r.readBytes } else if r.readBytes < r.desc.Size { - err = fmt.Errorf("%w [expected %d, received %d]: %w", types.ErrShortRead, r.desc.Size, r.readBytes, err) + err = fmt.Errorf("%w [expected %d, received %d]: %w", errs.ErrShortRead, r.desc.Size, r.readBytes, err) } else if r.readBytes > r.desc.Size { - err = fmt.Errorf("%w [expected %d, received %d]: %w", types.ErrSizeLimitExceeded, r.desc.Size, r.readBytes, err) + err = fmt.Errorf("%w [expected %d, received %d]: %w", errs.ErrSizeLimitExceeded, r.desc.Size, r.readBytes, err) } // check/save digest if r.desc.Digest == "" { r.desc.Digest = r.digester.Digest() } else if r.desc.Digest != r.digester.Digest() { - err = fmt.Errorf("%w [expected %s, calculated %s]: %w", types.ErrDigestMismatch, r.desc.Digest.String(), r.digester.Digest().String(), err) + err = fmt.Errorf("%w [expected %s, calculated %s]: %w", errs.ErrDigestMismatch, r.desc.Digest.String(), r.digester.Digest().String(), err) } } return size, err diff --git a/types/blob/tar.go b/types/blob/tar.go index 84124c0..9fcd47a 100644 --- a/types/blob/tar.go +++ b/types/blob/tar.go @@ -12,7 +12,7 @@ import ( "github.com/regclient/regclient/internal/limitread" "github.com/regclient/regclient/pkg/archive" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) // TarReader was previously an interface. A type alias is provided for upgrading. @@ -98,7 +98,7 @@ func (tr *BTarReader) RawBody() ([]byte, error) { dig := tr.digester.Digest() tr.digester = nil if tr.desc.Digest.String() != "" && dig != tr.desc.Digest { - return b, fmt.Errorf("%w, expected %s, received %s", types.ErrDigestMismatch, tr.desc.Digest.String(), dig.String()) + return b, fmt.Errorf("%w, expected %s, received %s", errs.ErrDigestMismatch, tr.desc.Digest.String(), dig.String()) } tr.desc.Digest = dig } @@ -150,18 +150,18 @@ func (tr *BTarReader) ReadFile(filename string) (*tar.Header, io.Reader, error) } // EOF encountered if whiteout { - return nil, nil, types.ErrFileDeleted + return nil, nil, errs.ErrFileDeleted } if tr.digester != nil { _, _ = io.Copy(io.Discard, tr.reader) // process/digest any trailing bytes from reader dig := tr.digester.Digest() tr.digester = nil if tr.desc.Digest.String() != "" && dig != tr.desc.Digest { - return nil, nil, fmt.Errorf("%w, expected %s, received %s", types.ErrDigestMismatch, tr.desc.Digest.String(), dig.String()) + return nil, nil, fmt.Errorf("%w, expected %s, received %s", errs.ErrDigestMismatch, tr.desc.Digest.String(), dig.String()) } tr.desc.Digest = dig } - return nil, nil, types.ErrFileNotFound + return nil, nil, errs.ErrFileNotFound } func tarCmpWhiteout(whFile, tgtFile string) bool { diff --git a/types/descriptor.go b/types/descriptor.go index bb396b4..ad31e7e 100644 --- a/types/descriptor.go +++ b/types/descriptor.go @@ -1,272 +1,27 @@ package types -import ( - "fmt" - "sort" - "strings" - "text/tabwriter" +import "github.com/regclient/regclient/types/descriptor" - // crypto libraries included for go-digest - _ "crypto/sha256" - _ "crypto/sha512" - - "github.com/opencontainers/go-digest" - - "github.com/regclient/regclient/internal/units" - "github.com/regclient/regclient/types/platform" +type ( + // Descriptor is used in manifests to refer to content by media type, size, and digest. + // + // Deprecated: replace with [descriptor.Descriptor]. + Descriptor = descriptor.Descriptor + // MatchOpt defines conditions for a match descriptor. + // + // Deprecated: replace with [descriptor.MatchOpt]. + MatchOpt = descriptor.MatchOpt ) -// Descriptor is used in manifests to refer to content by media type, size, and digest. -type Descriptor struct { - // MediaType describe the type of the content. - MediaType string `json:"mediaType"` - - // Digest uniquely identifies the content. - Digest digest.Digest `json:"digest"` - - // Size in bytes of content. - Size int64 `json:"size"` - - // URLs contains the source URLs of this content. - URLs []string `json:"urls,omitempty"` - - // Annotations contains arbitrary metadata relating to the targeted content. - Annotations map[string]string `json:"annotations,omitempty"` - - // Data is an embedding of the targeted content. This is encoded as a base64 - // string when marshalled to JSON (automatically, by encoding/json). If - // present, Data can be used directly to avoid fetching the targeted content. - Data []byte `json:"data,omitempty"` - - // Platform describes the platform which the image in the manifest runs on. - // This should only be used when referring to a manifest. - Platform *platform.Platform `json:"platform,omitempty"` - - // ArtifactType is the media type of the artifact this descriptor refers to. - ArtifactType string `json:"artifactType,omitempty"` -} - -var EmptyData = []byte("{}") -var EmptyDigest = digest.FromBytes(EmptyData) -var emptyDigest = digest.FromBytes([]byte{}) -var mtToOCI map[string]string - -func init() { - mtToOCI = map[string]string{ - MediaTypeDocker2ManifestList: MediaTypeOCI1ManifestList, - MediaTypeDocker2Manifest: MediaTypeOCI1Manifest, - MediaTypeDocker2ImageConfig: MediaTypeOCI1ImageConfig, - MediaTypeDocker2LayerGzip: MediaTypeOCI1LayerGzip, - MediaTypeOCI1ManifestList: MediaTypeOCI1ManifestList, - MediaTypeOCI1Manifest: MediaTypeOCI1Manifest, - MediaTypeOCI1ImageConfig: MediaTypeOCI1ImageConfig, - MediaTypeOCI1LayerGzip: MediaTypeOCI1LayerGzip, - } -} - -// GetData decodes the Data field from the descriptor if available -func (d Descriptor) GetData() ([]byte, error) { - if len(d.Data) == 0 && d.Digest != emptyDigest { - return nil, ErrParsingFailed - } - // verify length - if int64(len(d.Data)) != d.Size { - return nil, ErrParsingFailed - } - // generate and verify digest - dDig := digest.FromBytes(d.Data) - if d.Digest != dDig { - return nil, ErrParsingFailed - } - // return data - return d.Data, nil -} - -// Equal indicates the two descriptors are identical, effectively a DeepEqual. -func (d Descriptor) Equal(d2 Descriptor) bool { - if !d.Same(d2) { - return false - } - if d.MediaType != d2.MediaType { - return false - } - if d.ArtifactType != d2.ArtifactType { - return false - } - if d.Platform == nil || d2.Platform == nil { - if d.Platform != nil || d2.Platform != nil { - return false - } - } else if !platform.Match(*d.Platform, *d2.Platform) { - return false - } - if d.URLs == nil || d2.URLs == nil { - if d.URLs != nil || d2.URLs != nil { - return false - } - } else if len(d.URLs) != len(d2.URLs) { - return false - } else { - for i := range d.URLs { - if d.URLs[i] != d2.URLs[i] { - return false - } - } - } - if d.Annotations == nil || d2.Annotations == nil { - if d.Annotations != nil || d2.Annotations != nil { - return false - } - } else if len(d.Annotations) != len(d2.Annotations) { - return false - } else { - for i := range d.Annotations { - if d.Annotations[i] != d2.Annotations[i] { - return false - } - } - } - return true -} - -// Same indicates two descriptors point to the same CAS object. -// This verifies the digest, media type, and size all match -func (d Descriptor) Same(d2 Descriptor) bool { - if d.Digest != d2.Digest || d.Size != d2.Size { - return false - } - // loosen the check on media type since this can be converted from a build - if d.MediaType != d2.MediaType { - if _, ok := mtToOCI[d.MediaType]; !ok { - return false - } else if mtToOCI[d.MediaType] != mtToOCI[d2.MediaType] { - return false - } - } - return true -} - -func (d Descriptor) MarshalPrettyTW(tw *tabwriter.Writer, prefix string) error { - fmt.Fprintf(tw, "%sDigest:\t%s\n", prefix, string(d.Digest)) - fmt.Fprintf(tw, "%sMediaType:\t%s\n", prefix, d.MediaType) - if d.ArtifactType != "" { - fmt.Fprintf(tw, "%sArtifactType:\t%s\n", prefix, d.ArtifactType) - } - switch d.MediaType { - case MediaTypeDocker1Manifest, MediaTypeDocker1ManifestSigned, - MediaTypeDocker2Manifest, MediaTypeDocker2ManifestList, - MediaTypeOCI1Manifest, MediaTypeOCI1ManifestList: - // skip printing size for descriptors to manifests - default: - if d.Size > 100000 { - fmt.Fprintf(tw, "%sSize:\t%s\n", prefix, units.HumanSize(float64(d.Size))) - } else { - fmt.Fprintf(tw, "%sSize:\t%dB\n", prefix, d.Size) - } - } - if p := d.Platform; p != nil && p.OS != "" { - fmt.Fprintf(tw, "%sPlatform:\t%s\n", prefix, p.String()) - if p.OSVersion != "" { - fmt.Fprintf(tw, "%sOSVersion:\t%s\n", prefix, p.OSVersion) - } - if len(p.OSFeatures) > 0 { - fmt.Fprintf(tw, "%sOSFeatures:\t%s\n", prefix, strings.Join(p.OSFeatures, ", ")) - } - } - if len(d.URLs) > 0 { - fmt.Fprintf(tw, "%sURLs:\t%s\n", prefix, strings.Join(d.URLs, ", ")) - } - if d.Annotations != nil { - fmt.Fprintf(tw, "%sAnnotations:\t\n", prefix) - for k, v := range d.Annotations { - fmt.Fprintf(tw, "%s %s:\t%s\n", prefix, k, v) - } - } - return nil -} - -// MatchOpt defines conditions for a match descriptor -type MatchOpt struct { - Platform *platform.Platform // Platform to match including compatible platforms (darwin/arm64 matches linux/arm64) - ArtifactType string // Match ArtifactType in the descriptor - Annotations map[string]string // Match each of the specified annotations and their value, an empty value verifies the key is set - SortAnnotation string // Sort the results by an annotation, string based comparison, descriptors without the annotation are sorted last - SortDesc bool // Set to true to sort in descending order -} - -// Match returns true if the descriptor matches the options, including compatible platforms -func (d Descriptor) Match(opt MatchOpt) bool { - if opt.ArtifactType != "" && d.ArtifactType != opt.ArtifactType { - return false - } - if opt.Annotations != nil && len(opt.Annotations) > 0 { - if d.Annotations == nil { - return false - } - for k, v := range opt.Annotations { - if dv, ok := d.Annotations[k]; !ok || (v != "" && v != dv) { - return false - } - } - } - if opt.Platform != nil { - if d.Platform == nil { - return false - } - if !platform.Compatible(*opt.Platform, *d.Platform) { - return false - } - } - return true -} - -// DescriptorListFilter returns a list of descriptors from the list matching the search options. -// When opt.SortAnnotation is set, the order of descriptors with matching annotations is undefined. -func DescriptorListFilter(dl []Descriptor, opt MatchOpt) []Descriptor { - ret := []Descriptor{} - for _, d := range dl { - if d.Match(opt) { - ret = append(ret, d) - } - } - if opt.SortAnnotation != "" { - sort.Slice(ret, func(i, j int) bool { - // if annotations are not defined, sort to the very end - if ret[i].Annotations == nil { - return false - } - if _, ok := ret[i].Annotations[opt.SortAnnotation]; !ok { - return false - } - if ret[j].Annotations == nil { - return true - } - if _, ok := ret[j].Annotations[opt.SortAnnotation]; !ok { - return true - } - // else sort by string - if strings.Compare(ret[i].Annotations[opt.SortAnnotation], ret[j].Annotations[opt.SortAnnotation]) < 0 { - return !opt.SortDesc - } - return opt.SortDesc - }) - } - return ret -} - -// DescriptorListSearch returns the first descriptor from the list matching the search options -func DescriptorListSearch(dl []Descriptor, opt MatchOpt) (Descriptor, error) { - filter := DescriptorListFilter(dl, opt) - if len(filter) < 1 { - return Descriptor{}, ErrNotFound - } - // prefer exact platform match when available - if opt.Platform != nil { - for _, d := range filter { - if platform.Match(*opt.Platform, *d.Platform) { - return d, nil - } - } - } - return filter[0], nil -} +var ( + // EmptyData is the content of the empty JSON descriptor. See [mediatype.OCI1Empty]. + // + // Deprecated: replace with [descriptor.EmptyData]. + EmptyData = descriptor.EmptyData + // EmptyDigest is the digest of the empty JSON descriptor. See [mediatype.OCI1Empty]. + // + // Deprecated: replace with [descriptor.EmptyDigest]. + EmptyDigest = descriptor.EmptyDigest + DescriptorListFilter = descriptor.DescriptorListFilter + DescriptorListSearch = descriptor.DescriptorListSearch +) diff --git a/types/descriptor/descriptor.go b/types/descriptor/descriptor.go new file mode 100644 index 0000000..8e34caf --- /dev/null +++ b/types/descriptor/descriptor.go @@ -0,0 +1,279 @@ +// Package descriptor defines the OCI descriptor data structure used in manifests to reference content addressable data. +package descriptor + +import ( + "fmt" + "sort" + "strings" + "text/tabwriter" + + // crypto libraries included for go-digest + _ "crypto/sha256" + _ "crypto/sha512" + + "github.com/opencontainers/go-digest" + + "github.com/regclient/regclient/internal/units" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" + "github.com/regclient/regclient/types/platform" +) + +// Descriptor is used in manifests to refer to content by media type, size, and digest. +type Descriptor struct { + // MediaType describe the type of the content. + MediaType string `json:"mediaType"` + + // Digest uniquely identifies the content. + Digest digest.Digest `json:"digest"` + + // Size in bytes of content. + Size int64 `json:"size"` + + // URLs contains the source URLs of this content. + URLs []string `json:"urls,omitempty"` + + // Annotations contains arbitrary metadata relating to the targeted content. + Annotations map[string]string `json:"annotations,omitempty"` + + // Data is an embedding of the targeted content. This is encoded as a base64 + // string when marshalled to JSON (automatically, by encoding/json). If + // present, Data can be used directly to avoid fetching the targeted content. + Data []byte `json:"data,omitempty"` + + // Platform describes the platform which the image in the manifest runs on. + // This should only be used when referring to a manifest. + Platform *platform.Platform `json:"platform,omitempty"` + + // ArtifactType is the media type of the artifact this descriptor refers to. + ArtifactType string `json:"artifactType,omitempty"` +} + +var ( + // EmptyData is the content of the empty JSON descriptor. See [mediatype.OCI1Empty]. + EmptyData = []byte("{}") + // EmptyDigest is the digest of the empty JSON descriptor. See [mediatype.OCI1Empty]. + EmptyDigest = digest.FromBytes(EmptyData) + emptyDigest = digest.FromBytes([]byte{}) + mtToOCI map[string]string +) + +func init() { + mtToOCI = map[string]string{ + mediatype.Docker2ManifestList: mediatype.OCI1ManifestList, + mediatype.Docker2Manifest: mediatype.OCI1Manifest, + mediatype.Docker2ImageConfig: mediatype.OCI1ImageConfig, + mediatype.Docker2LayerGzip: mediatype.OCI1LayerGzip, + mediatype.OCI1ManifestList: mediatype.OCI1ManifestList, + mediatype.OCI1Manifest: mediatype.OCI1Manifest, + mediatype.OCI1ImageConfig: mediatype.OCI1ImageConfig, + mediatype.OCI1LayerGzip: mediatype.OCI1LayerGzip, + } +} + +// GetData decodes the Data field from the descriptor if available +func (d Descriptor) GetData() ([]byte, error) { + if len(d.Data) == 0 && d.Digest != emptyDigest { + return nil, errs.ErrParsingFailed + } + // verify length + if int64(len(d.Data)) != d.Size { + return nil, errs.ErrParsingFailed + } + // generate and verify digest + dDig := digest.FromBytes(d.Data) + if d.Digest != dDig { + return nil, errs.ErrParsingFailed + } + // return data + return d.Data, nil +} + +// Equal indicates the two descriptors are identical, effectively a DeepEqual. +func (d Descriptor) Equal(d2 Descriptor) bool { + if !d.Same(d2) { + return false + } + if d.MediaType != d2.MediaType { + return false + } + if d.ArtifactType != d2.ArtifactType { + return false + } + if d.Platform == nil || d2.Platform == nil { + if d.Platform != nil || d2.Platform != nil { + return false + } + } else if !platform.Match(*d.Platform, *d2.Platform) { + return false + } + if d.URLs == nil || d2.URLs == nil { + if d.URLs != nil || d2.URLs != nil { + return false + } + } else if len(d.URLs) != len(d2.URLs) { + return false + } else { + for i := range d.URLs { + if d.URLs[i] != d2.URLs[i] { + return false + } + } + } + if d.Annotations == nil || d2.Annotations == nil { + if d.Annotations != nil || d2.Annotations != nil { + return false + } + } else if len(d.Annotations) != len(d2.Annotations) { + return false + } else { + for i := range d.Annotations { + if d.Annotations[i] != d2.Annotations[i] { + return false + } + } + } + return true +} + +// Same indicates two descriptors point to the same CAS object. +// This verifies the digest, media type, and size all match. +func (d Descriptor) Same(d2 Descriptor) bool { + if d.Digest != d2.Digest || d.Size != d2.Size { + return false + } + // loosen the check on media type since this can be converted from a build + if d.MediaType != d2.MediaType { + if _, ok := mtToOCI[d.MediaType]; !ok { + return false + } else if mtToOCI[d.MediaType] != mtToOCI[d2.MediaType] { + return false + } + } + return true +} + +func (d Descriptor) MarshalPrettyTW(tw *tabwriter.Writer, prefix string) error { + fmt.Fprintf(tw, "%sDigest:\t%s\n", prefix, string(d.Digest)) + fmt.Fprintf(tw, "%sMediaType:\t%s\n", prefix, d.MediaType) + if d.ArtifactType != "" { + fmt.Fprintf(tw, "%sArtifactType:\t%s\n", prefix, d.ArtifactType) + } + switch d.MediaType { + case mediatype.Docker1Manifest, mediatype.Docker1ManifestSigned, + mediatype.Docker2Manifest, mediatype.Docker2ManifestList, + mediatype.OCI1Manifest, mediatype.OCI1ManifestList: + // skip printing size for descriptors to manifests + default: + if d.Size > 100000 { + fmt.Fprintf(tw, "%sSize:\t%s\n", prefix, units.HumanSize(float64(d.Size))) + } else { + fmt.Fprintf(tw, "%sSize:\t%dB\n", prefix, d.Size) + } + } + if p := d.Platform; p != nil && p.OS != "" { + fmt.Fprintf(tw, "%sPlatform:\t%s\n", prefix, p.String()) + if p.OSVersion != "" { + fmt.Fprintf(tw, "%sOSVersion:\t%s\n", prefix, p.OSVersion) + } + if len(p.OSFeatures) > 0 { + fmt.Fprintf(tw, "%sOSFeatures:\t%s\n", prefix, strings.Join(p.OSFeatures, ", ")) + } + } + if len(d.URLs) > 0 { + fmt.Fprintf(tw, "%sURLs:\t%s\n", prefix, strings.Join(d.URLs, ", ")) + } + if d.Annotations != nil { + fmt.Fprintf(tw, "%sAnnotations:\t\n", prefix) + for k, v := range d.Annotations { + fmt.Fprintf(tw, "%s %s:\t%s\n", prefix, k, v) + } + } + return nil +} + +// MatchOpt defines conditions for a match descriptor. +type MatchOpt struct { + Platform *platform.Platform // Platform to match including compatible platforms (darwin/arm64 matches linux/arm64) + ArtifactType string // Match ArtifactType in the descriptor + Annotations map[string]string // Match each of the specified annotations and their value, an empty value verifies the key is set + SortAnnotation string // Sort the results by an annotation, string based comparison, descriptors without the annotation are sorted last + SortDesc bool // Set to true to sort in descending order +} + +// Match returns true if the descriptor matches the options, including compatible platforms. +func (d Descriptor) Match(opt MatchOpt) bool { + if opt.ArtifactType != "" && d.ArtifactType != opt.ArtifactType { + return false + } + if opt.Annotations != nil && len(opt.Annotations) > 0 { + if d.Annotations == nil { + return false + } + for k, v := range opt.Annotations { + if dv, ok := d.Annotations[k]; !ok || (v != "" && v != dv) { + return false + } + } + } + if opt.Platform != nil { + if d.Platform == nil { + return false + } + if !platform.Compatible(*opt.Platform, *d.Platform) { + return false + } + } + return true +} + +// DescriptorListFilter returns a list of descriptors from the list matching the search options. +// When opt.SortAnnotation is set, the order of descriptors with matching annotations is undefined. +func DescriptorListFilter(dl []Descriptor, opt MatchOpt) []Descriptor { + ret := []Descriptor{} + for _, d := range dl { + if d.Match(opt) { + ret = append(ret, d) + } + } + if opt.SortAnnotation != "" { + sort.Slice(ret, func(i, j int) bool { + // if annotations are not defined, sort to the very end + if ret[i].Annotations == nil { + return false + } + if _, ok := ret[i].Annotations[opt.SortAnnotation]; !ok { + return false + } + if ret[j].Annotations == nil { + return true + } + if _, ok := ret[j].Annotations[opt.SortAnnotation]; !ok { + return true + } + // else sort by string + if strings.Compare(ret[i].Annotations[opt.SortAnnotation], ret[j].Annotations[opt.SortAnnotation]) < 0 { + return !opt.SortDesc + } + return opt.SortDesc + }) + } + return ret +} + +// DescriptorListSearch returns the first descriptor from the list matching the search options +func DescriptorListSearch(dl []Descriptor, opt MatchOpt) (Descriptor, error) { + filter := DescriptorListFilter(dl, opt) + if len(filter) < 1 { + return Descriptor{}, errs.ErrNotFound + } + // prefer exact platform match when available + if opt.Platform != nil { + for _, d := range filter { + if platform.Match(*opt.Platform, *d.Platform) { + return d, nil + } + } + } + return filter[0], nil +} diff --git a/types/descriptor_test.go b/types/descriptor/descriptor_test.go similarity index 86% rename from types/descriptor_test.go rename to types/descriptor/descriptor_test.go index dc854d6..2ab87a5 100644 --- a/types/descriptor_test.go +++ b/types/descriptor/descriptor_test.go @@ -1,4 +1,4 @@ -package types +package descriptor import ( "bytes" @@ -9,6 +9,8 @@ import ( "github.com/opencontainers/go-digest" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/platform" ) @@ -23,36 +25,36 @@ func TestDescriptorData(t *testing.T) { { name: "No Data", d: Descriptor{ - MediaType: MediaTypeDocker2LayerGzip, + MediaType: mediatype.Docker2LayerGzip, Size: 941, Digest: digest.Digest("sha256:f6e2d7fa40092cf3d9817bf6ff54183d68d108a47fdf5a5e476c612626c80e14"), }, - wantErr: ErrParsingFailed, + wantErr: errs.ErrParsingFailed, }, { name: "Bad Digest", d: Descriptor{ - MediaType: MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 10, Digest: digest.Digest("sha256:e4a380728755139f156563e8b795581d5915dcc947fe937c524c6d52fd604b99"), Data: []byte("example data"), }, - wantErr: ErrParsingFailed, + wantErr: errs.ErrParsingFailed, }, { name: "Bad Size", d: Descriptor{ - MediaType: MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 1000, Digest: digest.Digest("sha256:44752f37272e944fd2c913a35342eaccdd1aaf189bae50676b301ab213fc5061"), Data: []byte("example data"), }, - wantErr: ErrParsingFailed, + wantErr: errs.ErrParsingFailed, }, { name: "Good data", d: Descriptor{ - MediaType: MediaTypeOCI1LayerGzip, + MediaType: mediatype.OCI1LayerGzip, Size: 12, Digest: digest.Digest("sha256:44752f37272e944fd2c913a35342eaccdd1aaf189bae50676b301ab213fc5061"), Data: []byte("example data"), @@ -98,7 +100,7 @@ func TestDescriptorEq(t *testing.T) { { name: "empty d1", d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -108,7 +110,7 @@ func TestDescriptorEq(t *testing.T) { { name: "empty d2", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -118,12 +120,12 @@ func TestDescriptorEq(t *testing.T) { { name: "same simple manifest", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -133,12 +135,12 @@ func TestDescriptorEq(t *testing.T) { { name: "converting OCI media type", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, d2: Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 1234, Digest: digA, }, @@ -148,12 +150,12 @@ func TestDescriptorEq(t *testing.T) { { name: "different media type", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, d2: Descriptor{ - MediaType: MediaTypeDocker2ManifestList, + MediaType: mediatype.Docker2ManifestList, Size: 1234, Digest: digA, }, @@ -163,12 +165,12 @@ func TestDescriptorEq(t *testing.T) { { name: "different size", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 4321, Digest: digA, }, @@ -178,12 +180,12 @@ func TestDescriptorEq(t *testing.T) { { name: "different digest", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digB, }, @@ -193,7 +195,7 @@ func TestDescriptorEq(t *testing.T) { { name: "annotation eq", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Annotations: map[string]string{ @@ -202,7 +204,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Annotations: map[string]string{ @@ -216,7 +218,7 @@ func TestDescriptorEq(t *testing.T) { { name: "annotation diff", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Annotations: map[string]string{ @@ -225,7 +227,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Annotations: map[string]string{ @@ -239,7 +241,7 @@ func TestDescriptorEq(t *testing.T) { { name: "annotation missing", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Annotations: map[string]string{ @@ -248,7 +250,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -258,7 +260,7 @@ func TestDescriptorEq(t *testing.T) { { name: "urls eq", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, URLs: []string{ @@ -267,7 +269,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, URLs: []string{ @@ -281,7 +283,7 @@ func TestDescriptorEq(t *testing.T) { { name: "urls diff", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, URLs: []string{ @@ -290,7 +292,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, URLs: []string{ @@ -304,7 +306,7 @@ func TestDescriptorEq(t *testing.T) { { name: "urls missing", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, URLs: []string{ @@ -313,7 +315,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -323,7 +325,7 @@ func TestDescriptorEq(t *testing.T) { { name: "platform eq", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Platform: &platform.Platform{ @@ -332,7 +334,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Platform: &platform.Platform{ @@ -346,7 +348,7 @@ func TestDescriptorEq(t *testing.T) { { name: "platform diff", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Platform: &platform.Platform{ @@ -355,7 +357,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Platform: &platform.Platform{ @@ -369,7 +371,7 @@ func TestDescriptorEq(t *testing.T) { { name: "platform missing", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, Platform: &platform.Platform{ @@ -378,7 +380,7 @@ func TestDescriptorEq(t *testing.T) { }, }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -388,13 +390,13 @@ func TestDescriptorEq(t *testing.T) { { name: "artifactType eq", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, ArtifactType: "application/vnd.example.test", }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, ArtifactType: "application/vnd.example.test", @@ -405,13 +407,13 @@ func TestDescriptorEq(t *testing.T) { { name: "artifactType diff", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, ArtifactType: "application/vnd.example.test", }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, ArtifactType: "application/vnd.example.test2", @@ -422,13 +424,13 @@ func TestDescriptorEq(t *testing.T) { { name: "artifactType missing", d1: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, ArtifactType: "application/vnd.example.test", }, d2: Descriptor{ - MediaType: MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, Size: 1234, Digest: digA, }, @@ -463,7 +465,7 @@ func TestDataJSON(t *testing.T) { "digest": "sha256:f6e2d7fa40092cf3d9817bf6ff54183d68d108a47fdf5a5e476c612626c80e14", "size": 941 }`), - wantErr: ErrParsingFailed, + wantErr: errs.ErrParsingFailed, }, { name: "Bad Data", @@ -483,7 +485,7 @@ func TestDataJSON(t *testing.T) { "size": 10, "data": "ZXhhbXBsZSBkYXRh" }`), - wantErr: ErrParsingFailed, + wantErr: errs.ErrParsingFailed, }, { name: "Bad Size", @@ -493,7 +495,7 @@ func TestDataJSON(t *testing.T) { "size": 1000, "data": "ZXhhbXBsZSBkYXRh" }`), - wantErr: ErrParsingFailed, + wantErr: errs.ErrParsingFailed, }, { name: "Good data", @@ -538,7 +540,7 @@ func TestDataJSON(t *testing.T) { func TestDescriptorSearch(t *testing.T) { t.Parallel() dAMD64 := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, Platform: &platform.Platform{ @@ -547,7 +549,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dAMD64Win := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, Platform: &platform.Platform{ @@ -556,7 +558,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dARM64 := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, Platform: &platform.Platform{ @@ -565,7 +567,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dAnnotations := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, Platform: &platform.Platform{ @@ -579,7 +581,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dAnnotations2 := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, Platform: &platform.Platform{ @@ -593,7 +595,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dArtifact := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, ArtifactType: "application/example.artifact", @@ -603,7 +605,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dArtifact2 := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, ArtifactType: "application/example.artifact", @@ -614,7 +616,7 @@ func TestDescriptorSearch(t *testing.T) { }, } dArtifact3 := Descriptor{ - MediaType: MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, Size: 12345, Digest: EmptyDigest, ArtifactType: "application/example.artifact", @@ -642,7 +644,7 @@ func TestDescriptorSearch(t *testing.T) { }{ { name: "empty", - err: ErrNotFound, + err: errs.ErrNotFound, }, { name: "amd64", @@ -701,7 +703,7 @@ func TestDescriptorSearch(t *testing.T) { Architecture: "amd64", }, }, - err: ErrNotFound, + err: errs.ErrNotFound, }, { name: "artifact", diff --git a/types/docker/schema1/manifest.go b/types/docker/schema1/manifest.go index d558476..9237973 100644 --- a/types/docker/schema1/manifest.go +++ b/types/docker/schema1/manifest.go @@ -10,20 +10,20 @@ import ( "github.com/docker/libtrust" "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" "github.com/regclient/regclient/types/docker" + "github.com/regclient/regclient/types/mediatype" ) var ( // ManifestSchemaVersion provides a pre-initialized version structure schema1 manifests. ManifestSchemaVersion = docker.Versioned{ SchemaVersion: 1, - MediaType: types.MediaTypeDocker1Manifest, + MediaType: mediatype.Docker1Manifest, } // ManifestSignedSchemaVersion provides a pre-initialized version structure schema1 signed manifests. ManifestSignedSchemaVersion = docker.Versioned{ SchemaVersion: 1, - MediaType: types.MediaTypeDocker1ManifestSigned, + MediaType: mediatype.Docker1ManifestSigned, } ) @@ -119,7 +119,7 @@ func (sm *SignedManifest) MarshalJSON() ([]byte, error) { // Payload returns the signed content of the signed manifest. func (sm SignedManifest) Payload() (string, []byte, error) { - return types.MediaTypeDocker1ManifestSigned, sm.all, nil + return mediatype.Docker1ManifestSigned, sm.all, nil } // Signatures returns the signatures as provided by (*libtrust.JSONSignature).Signatures. diff --git a/types/docker/schema2/manifest.go b/types/docker/schema2/manifest.go index 659640b..16bb8cb 100644 --- a/types/docker/schema2/manifest.go +++ b/types/docker/schema2/manifest.go @@ -1,14 +1,15 @@ package schema2 import ( - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker" + "github.com/regclient/regclient/types/mediatype" ) // ManifestSchemaVersion is a pre-configured versioned field for manifests var ManifestSchemaVersion = docker.Versioned{ SchemaVersion: 2, - MediaType: types.MediaTypeDocker2Manifest, + MediaType: mediatype.Docker2Manifest, } // Manifest defines a schema2 manifest. @@ -16,11 +17,11 @@ type Manifest struct { docker.Versioned // Config references the image configuration as a blob. - Config types.Descriptor `json:"config"` + Config descriptor.Descriptor `json:"config"` // Layers lists descriptors for the layers referenced by the // configuration. - Layers []types.Descriptor `json:"layers"` + Layers []descriptor.Descriptor `json:"layers"` // Annotations contains arbitrary metadata for the image index. // Note, this is not a defined docker schema2 field. diff --git a/types/docker/schema2/manifestlist.go b/types/docker/schema2/manifestlist.go index 613db51..8fa8047 100644 --- a/types/docker/schema2/manifestlist.go +++ b/types/docker/schema2/manifestlist.go @@ -1,14 +1,15 @@ package schema2 import ( - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker" + "github.com/regclient/regclient/types/mediatype" ) // ManifestListSchemaVersion is a pre-configured versioned field for manifest lists var ManifestListSchemaVersion = docker.Versioned{ SchemaVersion: 2, - MediaType: types.MediaTypeDocker2ManifestList, + MediaType: mediatype.Docker2ManifestList, } // ManifestList references manifests for various platforms. @@ -16,7 +17,7 @@ type ManifestList struct { docker.Versioned // Manifests lists descriptors in the manifest list - Manifests []types.Descriptor `json:"manifests"` + Manifests []descriptor.Descriptor `json:"manifests"` // Annotations contains arbitrary metadata for the image index. // Note, this is not a defined docker schema2 field. diff --git a/types/error.go b/types/error.go index 0c34acb..8841d01 100644 --- a/types/error.go +++ b/types/error.go @@ -1,86 +1,150 @@ package types -import ( - "errors" - "fmt" - "io/fs" -) +import "github.com/regclient/regclient/types/errs" var ( // ErrAllRequestsFailed when there are no mirrors left to try - ErrAllRequestsFailed = errors.New("all requests failed") + // + // Deprecated: replace with [errs.ErrAllRequestsFailed]. + ErrAllRequestsFailed = errs.ErrAllRequestsFailed // ErrAPINotFound if an api is not available for the host - ErrAPINotFound = errors.New("API not found") + // + // Deprecated: replace with [errs.ErrAPINotFound]. + ErrAPINotFound = errs.ErrAPINotFound // ErrBackoffLimit maximum backoff attempts reached - ErrBackoffLimit = errors.New("backoff limit reached") + // + // Deprecated: replace with [errs.ErrBackoffLimit]. + ErrBackoffLimit = errs.ErrBackoffLimit // ErrCanceled if the context was canceled - ErrCanceled = errors.New("context was canceled") + // + // Deprecated: replace with [errs.ErrCanceled]. + ErrCanceled = errs.ErrCanceled // ErrDigestMismatch if the expected digest wasn't received - ErrDigestMismatch = errors.New("digest mismatch") + // + // Deprecated: replace with [errs.ErrDigestMismatch]. + ErrDigestMismatch = errs.ErrDigestMismatch // ErrEmptyChallenge indicates an issue with the received challenge in the WWW-Authenticate header - ErrEmptyChallenge = errors.New("empty challenge header") + // + // Deprecated: replace with [errs.ErrEmptyChallenge]. + ErrEmptyChallenge = errs.ErrEmptyChallenge // ErrFileDeleted indicates a requested file has been deleted - ErrFileDeleted = errors.New("file deleted") + // + // Deprecated: replace with [errs.ErrFileDeleted]. + ErrFileDeleted = errs.ErrFileDeleted // ErrFileNotFound indicates a requested file is not found - ErrFileNotFound = fmt.Errorf("file not found%.0w", fs.ErrNotExist) + // + // Deprecated: replace with [errs.ErrFileNotFound]. + ErrFileNotFound = errs.ErrFileNotFound // ErrHTTPStatus if the http status code was unexpected - ErrHTTPStatus = errors.New("unexpected http status code") + // + // Deprecated: replace with [errs.ErrHTTPStatus]. + ErrHTTPStatus = errs.ErrHTTPStatus // ErrInvalidChallenge indicates an issue with the received challenge in the WWW-Authenticate header - ErrInvalidChallenge = errors.New("invalid challenge header") + // + // Deprecated: replace with [errs.ErrInvalidChallenge]. + ErrInvalidChallenge = errs.ErrInvalidChallenge // ErrInvalidReference indicates the reference to an image is has an invalid syntax - ErrInvalidReference = errors.New("invalid reference") + // + // Deprecated: replace with [errs.ErrInvalidReference]. + ErrInvalidReference = errs.ErrInvalidReference // ErrLoopDetected indicates a child node points back to the parent - ErrLoopDetected = errors.New("loop detected") + // + // Deprecated: replace with [errs.ErrLoopDetected]. + ErrLoopDetected = errs.ErrLoopDetected // ErrManifestNotSet indicates the manifest is not set, it must be pulled with a ManifestGet first - ErrManifestNotSet = errors.New("manifest not set") + // + // Deprecated: replace with [errs.ErrManifestNotSet]. + ErrManifestNotSet = errs.ErrManifestNotSet // ErrMissingAnnotation returned when a needed annotation is not found - ErrMissingAnnotation = errors.New("annotation is missing") + // + // Deprecated: replace with [errs.ErrMissingAnnotation]. + ErrMissingAnnotation = errs.ErrMissingAnnotation // ErrMissingDigest returned when image reference does not include a digest - ErrMissingDigest = errors.New("digest missing from image reference") + // + // Deprecated: replace with [errs.ErrMissingDigest]. + ErrMissingDigest = errs.ErrMissingDigest // ErrMissingLocation returned when the location header is missing - ErrMissingLocation = errors.New("location header missing") + // + // Deprecated: replace with [errs.ErrMissingLocation]. + ErrMissingLocation = errs.ErrMissingLocation // ErrMissingName returned when name missing for host - ErrMissingName = errors.New("name missing") + // + // Deprecated: replace with [errs.ErrMissingName]. + ErrMissingName = errs.ErrMissingName // ErrMissingTag returned when image reference does not include a tag - ErrMissingTag = errors.New("tag missing from image reference") + // + // Deprecated: replace with [errs.ErrMissingTag]. + ErrMissingTag = errs.ErrMissingTag // ErrMissingTagOrDigest returned when image reference does not include a tag or digest - ErrMissingTagOrDigest = errors.New("tag or Digest missing from image reference") + // + // Deprecated: replace with [errs.ErrMissingTagOrDigest]. + ErrMissingTagOrDigest = errs.ErrMissingTagOrDigest // ErrMismatch returned when a comparison detects a difference - ErrMismatch = errors.New("content does not match") + // + // Deprecated: replace with [errs.ErrMismatch]. + ErrMismatch = errs.ErrMismatch // ErrMountReturnedLocation when a blob mount fails but a location header is received - ErrMountReturnedLocation = errors.New("blob mount returned a location to upload") + // + // Deprecated: replace with [errs.ErrMountReturnedLocation]. + ErrMountReturnedLocation = errs.ErrMountReturnedLocation // ErrNoNewChallenge indicates a challenge update did not result in any change - ErrNoNewChallenge = errors.New("no new challenge") + // + // Deprecated: replace with [errs.ErrNoNewChallenge]. + ErrNoNewChallenge = errs.ErrNoNewChallenge // ErrNotFound isn't there, search for your value elsewhere - ErrNotFound = errors.New("not found") + // + // Deprecated: replace with [errs.ErrNotFound]. + ErrNotFound = errs.ErrNotFound // ErrNotImplemented returned when method has not been implemented yet - ErrNotImplemented = errors.New("not implemented") + // + // Deprecated: replace with [errs.ErrNotImplemented]. + ErrNotImplemented = errs.ErrNotImplemented // ErrNotRetryable indicates the process cannot be retried - ErrNotRetryable = errors.New("not retryable") + // + // Deprecated: replace with [errs.ErrNotRetryable]. + ErrNotRetryable = errs.ErrNotRetryable // ErrParsingFailed when a string cannot be parsed - ErrParsingFailed = errors.New("parsing failed") + // + // Deprecated: replace with [errs.ErrParsingFailed]. + ErrParsingFailed = errs.ErrParsingFailed // ErrRetryNeeded indicates a request needs to be retried - ErrRetryNeeded = errors.New("retry needed") + // + // Deprecated: replace with [errs.ErrRetryNeeded]. + ErrRetryNeeded = errs.ErrRetryNeeded // ErrShortRead if contents are less than expected the size - ErrShortRead = errors.New("short read") + // + // Deprecated: replace with [errs.ErrShortRead]. + ErrShortRead = errs.ErrShortRead // ErrSizeLimitExceeded if contents exceed the size limit - ErrSizeLimitExceeded = errors.New("size limit exceeded") + // + // Deprecated: replace with [errs.ErrSizeLimitExceeded]. + ErrSizeLimitExceeded = errs.ErrSizeLimitExceeded // ErrUnavailable when a requested value is not available - ErrUnavailable = errors.New("unavailable") + // + // Deprecated: replace with [errs.ErrUnavailable]. + ErrUnavailable = errs.ErrUnavailable // ErrUnsupported indicates the request was unsupported - ErrUnsupported = errors.New("unsupported") + // + // Deprecated: replace with [errs.ErrUnsupported]. + ErrUnsupported = errs.ErrUnsupported // ErrUnsupportedAPI happens when an API is not supported on a registry - ErrUnsupportedAPI = errors.New("unsupported API") + // + // Deprecated: replace with [errs.ErrUnsupportedAPI]. + ErrUnsupportedAPI = errs.ErrUnsupportedAPI // ErrUnsupportedConfigVersion happens when config file version is greater than this command supports - ErrUnsupportedConfigVersion = errors.New("unsupported config version") + // + // Deprecated: replace with [errs.ErrUnsupportedConfigVersion]. + ErrUnsupportedConfigVersion = errs.ErrUnsupportedConfigVersion // ErrUnsupportedMediaType returned when media type is unknown or unsupported - ErrUnsupportedMediaType = errors.New("unsupported media type") -) - -// custom HTTP errors extend the ErrHTTPStatus error -var ( + // + // Deprecated: replace with [errs.ErrUnsupportedMediaType]. + ErrUnsupportedMediaType = errs.ErrUnsupportedMediaType // ErrHTTPRateLimit when requests exceed server rate limit - ErrHTTPRateLimit = fmt.Errorf("rate limit exceeded%.0w", ErrHTTPStatus) + // + // Deprecated: replace with [errs.ErrHTTPRateLimit]. + ErrHTTPRateLimit = errs.ErrHTTPRateLimit // ErrHTTPUnauthorized when authentication fails - ErrHTTPUnauthorized = fmt.Errorf("unauthorized%.0w", ErrHTTPStatus) + // + // Deprecated: replace with [errs.ErrHTTPUnauthorized]. + ErrHTTPUnauthorized = errs.ErrHTTPUnauthorized ) diff --git a/types/errs/error.go b/types/errs/error.go new file mode 100644 index 0000000..fc3e1dd --- /dev/null +++ b/types/errs/error.go @@ -0,0 +1,87 @@ +// Package errs is used for predefined error values. +package errs + +import ( + "errors" + "fmt" + "io/fs" +) + +var ( + // ErrAllRequestsFailed when there are no mirrors left to try + ErrAllRequestsFailed = errors.New("all requests failed") + // ErrAPINotFound if an api is not available for the host + ErrAPINotFound = errors.New("API not found") + // ErrBackoffLimit maximum backoff attempts reached + ErrBackoffLimit = errors.New("backoff limit reached") + // ErrCanceled if the context was canceled + ErrCanceled = errors.New("context was canceled") + // ErrDigestMismatch if the expected digest wasn't received + ErrDigestMismatch = errors.New("digest mismatch") + // ErrEmptyChallenge indicates an issue with the received challenge in the WWW-Authenticate header + ErrEmptyChallenge = errors.New("empty challenge header") + // ErrFileDeleted indicates a requested file has been deleted + ErrFileDeleted = errors.New("file deleted") + // ErrFileNotFound indicates a requested file is not found + ErrFileNotFound = fmt.Errorf("file not found%.0w", fs.ErrNotExist) + // ErrHTTPStatus if the http status code was unexpected + ErrHTTPStatus = errors.New("unexpected http status code") + // ErrInvalidChallenge indicates an issue with the received challenge in the WWW-Authenticate header + ErrInvalidChallenge = errors.New("invalid challenge header") + // ErrInvalidReference indicates the reference to an image is has an invalid syntax + ErrInvalidReference = errors.New("invalid reference") + // ErrLoopDetected indicates a child node points back to the parent + ErrLoopDetected = errors.New("loop detected") + // ErrManifestNotSet indicates the manifest is not set, it must be pulled with a ManifestGet first + ErrManifestNotSet = errors.New("manifest not set") + // ErrMissingAnnotation returned when a needed annotation is not found + ErrMissingAnnotation = errors.New("annotation is missing") + // ErrMissingDigest returned when image reference does not include a digest + ErrMissingDigest = errors.New("digest missing from image reference") + // ErrMissingLocation returned when the location header is missing + ErrMissingLocation = errors.New("location header missing") + // ErrMissingName returned when name missing for host + ErrMissingName = errors.New("name missing") + // ErrMissingTag returned when image reference does not include a tag + ErrMissingTag = errors.New("tag missing from image reference") + // ErrMissingTagOrDigest returned when image reference does not include a tag or digest + ErrMissingTagOrDigest = errors.New("tag or Digest missing from image reference") + // ErrMismatch returned when a comparison detects a difference + ErrMismatch = errors.New("content does not match") + // ErrMountReturnedLocation when a blob mount fails but a location header is received + ErrMountReturnedLocation = errors.New("blob mount returned a location to upload") + // ErrNoNewChallenge indicates a challenge update did not result in any change + ErrNoNewChallenge = errors.New("no new challenge") + // 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") + // ErrNotRetryable indicates the process cannot be retried + ErrNotRetryable = errors.New("not retryable") + // ErrParsingFailed when a string cannot be parsed + ErrParsingFailed = errors.New("parsing failed") + // ErrRetryNeeded indicates a request needs to be retried + ErrRetryNeeded = errors.New("retry needed") + // ErrShortRead if contents are less than expected the size + ErrShortRead = errors.New("short read") + // ErrSizeLimitExceeded if contents exceed the size limit + ErrSizeLimitExceeded = errors.New("size limit exceeded") + // ErrUnavailable when a requested value is not available + ErrUnavailable = errors.New("unavailable") + // ErrUnsupported indicates the request was unsupported + ErrUnsupported = errors.New("unsupported") + // ErrUnsupportedAPI happens when an API is not supported on a registry + ErrUnsupportedAPI = errors.New("unsupported API") + // ErrUnsupportedConfigVersion happens when config file version is greater than this command supports + ErrUnsupportedConfigVersion = errors.New("unsupported config version") + // ErrUnsupportedMediaType returned when media type is unknown or unsupported + ErrUnsupportedMediaType = errors.New("unsupported media type") +) + +// custom HTTP errors extend the ErrHTTPStatus error +var ( + // ErrHTTPRateLimit when requests exceed server rate limit + ErrHTTPRateLimit = fmt.Errorf("rate limit exceeded%.0w", ErrHTTPStatus) + // ErrHTTPUnauthorized when authentication fails + ErrHTTPUnauthorized = fmt.Errorf("unauthorized%.0w", ErrHTTPStatus) +) diff --git a/types/manifest/common.go b/types/manifest/common.go index 83b895d..70139b7 100644 --- a/types/manifest/common.go +++ b/types/manifest/common.go @@ -12,12 +12,14 @@ import ( digest "github.com/opencontainers/go-digest" "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) type common struct { r ref.Ref - desc types.Descriptor + desc descriptor.Descriptor manifSet bool ratelimit types.RateLimit rawHeader http.Header @@ -30,7 +32,7 @@ func (m *common) GetDigest() digest.Digest { } // GetDescriptor returns the descriptor -func (m *common) GetDescriptor() types.Descriptor { +func (m *common) GetDescriptor() descriptor.Descriptor { return m.desc } @@ -74,7 +76,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, types.ErrManifestNotSet + return m.rawBody, errs.ErrManifestNotSet } return m.rawBody, nil } diff --git a/types/manifest/docker1.go b/types/manifest/docker1.go index 15d5e14..00beae2 100644 --- a/types/manifest/docker1.go +++ b/types/manifest/docker1.go @@ -12,8 +12,10 @@ import ( digest "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema1" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/platform" ) @@ -33,47 +35,47 @@ type docker1SignedManifest struct { schema1.SignedManifest } -func (m *docker1Manifest) GetConfig() (types.Descriptor, error) { - return types.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1Manifest) GetConfig() (descriptor.Descriptor, error) { + return descriptor.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1Manifest) GetConfigDigest() (digest.Digest, error) { - return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1SignedManifest) GetConfig() (types.Descriptor, error) { - return types.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1SignedManifest) GetConfig() (descriptor.Descriptor, error) { + return descriptor.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1SignedManifest) GetConfigDigest() (digest.Digest, error) { - return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1Manifest) GetManifestList() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1Manifest) GetManifestList() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1SignedManifest) GetManifestList() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1SignedManifest) GetManifestList() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1Manifest) GetLayers() ([]types.Descriptor, error) { +func (m *docker1Manifest) GetLayers() ([]descriptor.Descriptor, error) { if !m.manifSet { - return []types.Descriptor{}, types.ErrManifestNotSet + return []descriptor.Descriptor{}, errs.ErrManifestNotSet } - var dl []types.Descriptor + var dl []descriptor.Descriptor for _, sd := range m.FSLayers { - dl = append(dl, types.Descriptor{ + dl = append(dl, descriptor.Descriptor{ Digest: sd.BlobSum, }) } return dl, nil } -func (m *docker1SignedManifest) GetLayers() ([]types.Descriptor, error) { +func (m *docker1SignedManifest) GetLayers() ([]descriptor.Descriptor, error) { if !m.manifSet { - return []types.Descriptor{}, types.ErrManifestNotSet + return []descriptor.Descriptor{}, errs.ErrManifestNotSet } - var dl []types.Descriptor + var dl []descriptor.Descriptor for _, sd := range m.FSLayers { - dl = append(dl, types.Descriptor{ + dl = append(dl, descriptor.Descriptor{ Digest: sd.BlobSum, }) } @@ -87,31 +89,31 @@ func (m *docker1SignedManifest) GetOrig() interface{} { return m.SignedManifest } -func (m *docker1Manifest) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { - return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1Manifest) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { + return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1SignedManifest) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { - return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1SignedManifest) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { + return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1Manifest) GetPlatformList() ([]*platform.Platform, error) { - return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1SignedManifest) GetPlatformList() ([]*platform.Platform, error) { - return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1Manifest) GetSize() (int64, error) { - return 0, fmt.Errorf("GetSize is not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return 0, fmt.Errorf("GetSize is not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1SignedManifest) GetSize() (int64, error) { - return 0, fmt.Errorf("GetSize is not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return 0, fmt.Errorf("GetSize is not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1Manifest) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } if len(m.rawBody) > 0 { @@ -123,7 +125,7 @@ func (m *docker1Manifest) MarshalJSON() ([]byte, error) { func (m *docker1SignedManifest) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } return m.SignedManifest.MarshalJSON() @@ -168,30 +170,30 @@ func (m *docker1SignedManifest) MarshalPretty() ([]byte, error) { return buf.Bytes(), err } -func (m *docker1Manifest) SetConfig(d types.Descriptor) error { - return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1Manifest) SetConfig(d descriptor.Descriptor) error { + return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1SignedManifest) SetConfig(d types.Descriptor) error { - return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1SignedManifest) SetConfig(d descriptor.Descriptor) error { + return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1Manifest) SetLayers(dl []types.Descriptor) error { - return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1Manifest) SetLayers(dl []descriptor.Descriptor) error { + return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker1SignedManifest) SetLayers(dl []types.Descriptor) error { - return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker1SignedManifest) SetLayers(dl []descriptor.Descriptor) error { + return fmt.Errorf("set methods not supported for for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker1Manifest) SetOrig(origIn interface{}) error { orig, ok := origIn.(schema1.Manifest) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeDocker1Manifest { + if orig.MediaType != mediatype.Docker1Manifest { // TODO: error? - orig.MediaType = types.MediaTypeDocker1Manifest + orig.MediaType = mediatype.Docker1Manifest } mj, err := json.Marshal(orig) if err != nil { @@ -199,8 +201,8 @@ func (m *docker1Manifest) SetOrig(origIn interface{}) error { } m.manifSet = true m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeDocker1Manifest, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.Docker1Manifest, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } @@ -212,11 +214,11 @@ func (m *docker1Manifest) SetOrig(origIn interface{}) error { func (m *docker1SignedManifest) SetOrig(origIn interface{}) error { orig, ok := origIn.(schema1.SignedManifest) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeDocker1ManifestSigned { + if orig.MediaType != mediatype.Docker1ManifestSigned { // TODO: error? - orig.MediaType = types.MediaTypeDocker1ManifestSigned + orig.MediaType = mediatype.Docker1ManifestSigned } mj, err := json.Marshal(orig) if err != nil { @@ -224,8 +226,8 @@ func (m *docker1SignedManifest) SetOrig(origIn interface{}) error { } m.manifSet = true m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeDocker1ManifestSigned, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.Docker1ManifestSigned, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } diff --git a/types/manifest/docker2.go b/types/manifest/docker2.go index 8f3db21..fcaf19f 100644 --- a/types/manifest/docker2.go +++ b/types/manifest/docker2.go @@ -14,16 +14,18 @@ import ( digest "github.com/opencontainers/go-digest" "github.com/regclient/regclient/internal/units" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" "github.com/regclient/regclient/types/platform" ) const ( // MediaTypeDocker2Manifest is the media type when pulling manifests from a v2 registry - MediaTypeDocker2Manifest = types.MediaTypeDocker2Manifest + MediaTypeDocker2Manifest = mediatype.Docker2Manifest // MediaTypeDocker2ManifestList is the media type when pulling a manifest list from a v2 registry - MediaTypeDocker2ManifestList = types.MediaTypeDocker2ManifestList + MediaTypeDocker2ManifestList = mediatype.Docker2ManifestList ) type docker2Manifest struct { @@ -37,53 +39,53 @@ type docker2ManifestList struct { func (m *docker2Manifest) GetAnnotations() (map[string]string, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Annotations, nil } -func (m *docker2Manifest) GetConfig() (types.Descriptor, error) { +func (m *docker2Manifest) GetConfig() (descriptor.Descriptor, error) { if !m.manifSet { - return types.Descriptor{}, types.ErrManifestNotSet + return descriptor.Descriptor{}, errs.ErrManifestNotSet } return m.Config, nil } func (m *docker2Manifest) GetConfigDigest() (digest.Digest, error) { if !m.manifSet { - return digest.Digest(""), types.ErrManifestNotSet + return digest.Digest(""), errs.ErrManifestNotSet } return m.Config.Digest, nil } func (m *docker2ManifestList) GetAnnotations() (map[string]string, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Annotations, nil } -func (m *docker2ManifestList) GetConfig() (types.Descriptor, error) { - return types.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker2ManifestList) GetConfig() (descriptor.Descriptor, error) { + return descriptor.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker2ManifestList) GetConfigDigest() (digest.Digest, error) { - return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker2Manifest) GetManifestList() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker2Manifest) GetManifestList() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker2ManifestList) GetManifestList() ([]types.Descriptor, error) { +func (m *docker2ManifestList) GetManifestList() ([]descriptor.Descriptor, error) { if !m.manifSet { - return []types.Descriptor{}, types.ErrManifestNotSet + return []descriptor.Descriptor{}, errs.ErrManifestNotSet } return m.Manifests, nil } -func (m *docker2Manifest) GetLayers() ([]types.Descriptor, error) { +func (m *docker2Manifest) GetLayers() ([]descriptor.Descriptor, error) { if !m.manifSet { - return []types.Descriptor{}, types.ErrManifestNotSet + return []descriptor.Descriptor{}, errs.ErrManifestNotSet } return m.Layers, nil } -func (m *docker2ManifestList) GetLayers() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("layers are not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker2ManifestList) GetLayers() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("layers are not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker2Manifest) GetOrig() interface{} { @@ -93,17 +95,17 @@ func (m *docker2ManifestList) GetOrig() interface{} { return m.ManifestList } -func (m *docker2Manifest) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { - return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *docker2Manifest) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { + return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *docker2ManifestList) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { +func (m *docker2ManifestList) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } if p == nil { - return nil, fmt.Errorf("invalid input, platform is nil%.0w", types.ErrNotFound) + return nil, fmt.Errorf("invalid input, platform is nil%.0w", errs.ErrNotFound) } - d, err := types.DescriptorListSearch(m.Manifests, types.MatchOpt{Platform: p}) + d, err := descriptor.DescriptorListSearch(m.Manifests, descriptor.MatchOpt{Platform: p}) if err != nil { return nil, fmt.Errorf("platform not found: %s%.0w", *p, err) } @@ -111,7 +113,7 @@ func (m *docker2ManifestList) GetPlatformDesc(p *platform.Platform) (*types.Desc } func (m *docker2Manifest) GetPlatformList() ([]*platform.Platform, error) { - return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *docker2ManifestList) GetPlatformList() ([]*platform.Platform, error) { dl, err := m.GetManifestList() @@ -124,7 +126,7 @@ func (m *docker2ManifestList) GetPlatformList() ([]*platform.Platform, error) { // GetSize returns the size in bytes of all layers func (m *docker2Manifest) GetSize() (int64, error) { if !m.manifSet { - return 0, types.ErrManifestNotSet + return 0, errs.ErrManifestNotSet } var total int64 for _, d := range m.Layers { @@ -135,7 +137,7 @@ func (m *docker2Manifest) GetSize() (int64, error) { func (m *docker2Manifest) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } if len(m.rawBody) > 0 { return m.rawBody, nil @@ -144,7 +146,7 @@ func (m *docker2Manifest) MarshalJSON() ([]byte, error) { } func (m *docker2ManifestList) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } if len(m.rawBody) > 0 { return m.rawBody, nil @@ -241,7 +243,7 @@ func (m *docker2ManifestList) MarshalPretty() ([]byte, error) { func (m *docker2Manifest) SetAnnotation(key, val string) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } if m.Annotations == nil { m.Annotations = map[string]string{} @@ -255,7 +257,7 @@ func (m *docker2Manifest) SetAnnotation(key, val string) error { } func (m *docker2ManifestList) SetAnnotation(key, val string) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } if m.Annotations == nil { m.Annotations = map[string]string{} @@ -268,25 +270,25 @@ func (m *docker2ManifestList) SetAnnotation(key, val string) error { return m.updateDesc() } -func (m *docker2Manifest) SetConfig(d types.Descriptor) error { +func (m *docker2Manifest) SetConfig(d descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Config = d return m.updateDesc() } -func (m *docker2Manifest) SetLayers(dl []types.Descriptor) error { +func (m *docker2Manifest) SetLayers(dl []descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Layers = dl return m.updateDesc() } -func (m *docker2ManifestList) SetManifestList(dl []types.Descriptor) error { +func (m *docker2ManifestList) SetManifestList(dl []descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Manifests = dl return m.updateDesc() @@ -295,11 +297,11 @@ func (m *docker2ManifestList) SetManifestList(dl []types.Descriptor) error { func (m *docker2Manifest) SetOrig(origIn interface{}) error { orig, ok := origIn.(schema2.Manifest) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeDocker2Manifest { + if orig.MediaType != mediatype.Docker2Manifest { // TODO: error? - orig.MediaType = types.MediaTypeDocker2Manifest + orig.MediaType = mediatype.Docker2Manifest } m.manifSet = true m.Manifest = orig @@ -309,11 +311,11 @@ func (m *docker2Manifest) SetOrig(origIn interface{}) error { func (m *docker2ManifestList) SetOrig(origIn interface{}) error { orig, ok := origIn.(schema2.ManifestList) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeDocker2ManifestList { + if orig.MediaType != mediatype.Docker2ManifestList { // TODO: error? - orig.MediaType = types.MediaTypeDocker2ManifestList + orig.MediaType = mediatype.Docker2ManifestList } m.manifSet = true m.ManifestList = orig @@ -326,8 +328,8 @@ func (m *docker2Manifest) updateDesc() error { return err } m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } @@ -339,8 +341,8 @@ func (m *docker2ManifestList) updateDesc() error { return err } m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeDocker2ManifestList, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.Docker2ManifestList, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } diff --git a/types/manifest/manifest.go b/types/manifest/manifest.go index 9810d4f..aae2cc4 100644 --- a/types/manifest/manifest.go +++ b/types/manifest/manifest.go @@ -17,8 +17,11 @@ import ( digest "github.com/opencontainers/go-digest" "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema1" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -27,7 +30,7 @@ import ( // Manifest interface is implemented by all supported manifests but // many calls are only supported by certain underlying media types. type Manifest interface { - GetDescriptor() types.Descriptor + GetDescriptor() descriptor.Descriptor GetOrig() interface{} GetRef() ref.Ref IsList() bool @@ -38,12 +41,12 @@ type Manifest interface { SetOrig(interface{}) error // Deprecated: GetConfig should be accessed using [Imager] interface. - GetConfig() (types.Descriptor, error) + GetConfig() (descriptor.Descriptor, error) // Deprecated: GetLayers should be accessed using [Imager] interface. - GetLayers() ([]types.Descriptor, error) + GetLayers() ([]descriptor.Descriptor, error) // Deprecated: GetManifestList should be accessed using [Indexer] interface. - GetManifestList() ([]types.Descriptor, error) + GetManifestList() ([]descriptor.Descriptor, error) // Deprecated: GetConfigDigest should be replaced with [GetConfig]. GetConfigDigest() (digest.Digest, error) @@ -52,7 +55,7 @@ type Manifest interface { // Deprecated: GetMediaType should be replaced with GetDescriptor().MediaType, see [GetDescriptor]. GetMediaType() string // Deprecated: GetPlatformDesc method should be replaced with [manifest.GetPlatformDesc]. - GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) + GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) // Deprecated: GetPlatformList method should be replaced with [manifest.GetPlatformList]. GetPlatformList() ([]*platform.Platform, error) // Deprecated: GetRateLimit method should be replaced with [manifest.GetRateLimit]. @@ -70,28 +73,28 @@ type Annotator interface { // Indexer is used by manifests that contain a manifest list. type Indexer interface { - GetManifestList() ([]types.Descriptor, error) - SetManifestList(dl []types.Descriptor) error + GetManifestList() ([]descriptor.Descriptor, error) + SetManifestList(dl []descriptor.Descriptor) error } // Imager is used by manifests packaging an image. type Imager interface { - GetConfig() (types.Descriptor, error) - GetLayers() ([]types.Descriptor, error) - SetConfig(d types.Descriptor) error - SetLayers(dl []types.Descriptor) error + GetConfig() (descriptor.Descriptor, error) + GetLayers() ([]descriptor.Descriptor, error) + SetConfig(d descriptor.Descriptor) error + SetLayers(dl []descriptor.Descriptor) error GetSize() (int64, error) } // Subjecter is used by manifests that may have a subject field. type Subjecter interface { - GetSubject() (*types.Descriptor, error) - SetSubject(d *types.Descriptor) error + GetSubject() (*descriptor.Descriptor, error) + SetSubject(d *descriptor.Descriptor) error } type manifestConfig struct { r ref.Ref - desc types.Descriptor + desc descriptor.Descriptor raw []byte orig interface{} header http.Header @@ -113,7 +116,7 @@ func New(opts ...Opts) (Manifest, error) { // extract fields from header where available if mc.header != nil { if c.desc.MediaType == "" { - c.desc.MediaType = types.MediaTypeBase(mc.header.Get("Content-Type")) + c.desc.MediaType = mediatype.Base(mc.header.Get("Content-Type")) } if c.desc.Size == 0 { cl, _ := strconv.Atoi(mc.header.Get("Content-Length")) @@ -131,7 +134,7 @@ func New(opts ...Opts) (Manifest, error) { } // WithDesc specifies the descriptor for the manifest. -func WithDesc(desc types.Descriptor) Opts { +func WithDesc(desc descriptor.Descriptor) Opts { return func(mc *manifestConfig) { mc.desc = desc } @@ -178,15 +181,15 @@ func GetMediaType(m Manifest) string { } // GetPlatformDesc returns the descriptor for a specific platform from an index. -func GetPlatformDesc(m Manifest, p *platform.Platform) (*types.Descriptor, error) { +func GetPlatformDesc(m Manifest, p *platform.Platform) (*descriptor.Descriptor, error) { dl, err := m.GetManifestList() if err != nil { return nil, err } if p == nil { - return nil, fmt.Errorf("invalid input, platform is nil%.0w", types.ErrNotFound) + return nil, fmt.Errorf("invalid input, platform is nil%.0w", errs.ErrNotFound) } - d, err := types.DescriptorListSearch(dl, types.MatchOpt{Platform: p}) + d, err := descriptor.DescriptorListSearch(dl, descriptor.MatchOpt{Platform: p}) if err != nil { return nil, fmt.Errorf("platform not found: %s%.0w", *p, err) } @@ -265,7 +268,7 @@ func HasRateLimit(m Manifest) bool { func OCIIndexFromAny(orig interface{}) (v1.Index, error) { ociI := v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, } switch orig := orig.(type) { case schema2.ManifestList: @@ -311,7 +314,7 @@ func OCIIndexToAny(ociI v1.Index, origP interface{}) error { func OCIManifestFromAny(orig interface{}) (v1.Manifest, error) { ociM := v1.Manifest{ Versioned: v1.ManifestSchemaVersion, - MediaType: types.MediaTypeOCI1Manifest, + MediaType: mediatype.OCI1Manifest, } switch orig := orig.(type) { case schema2.Manifest: @@ -382,14 +385,14 @@ func fromOrig(c common, orig interface{}) (Manifest, error) { switch mOrig := orig.(type) { case schema1.Manifest: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeDocker1Manifest + c.desc.MediaType = mediatype.Docker1Manifest m = &docker1Manifest{ common: c, Manifest: mOrig, } case schema1.SignedManifest: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeDocker1ManifestSigned + c.desc.MediaType = mediatype.Docker1ManifestSigned // recompute digest on the canonical data c.desc.Digest = digest.FromBytes(mOrig.Canonical) m = &docker1SignedManifest{ @@ -398,35 +401,35 @@ func fromOrig(c common, orig interface{}) (Manifest, error) { } case schema2.Manifest: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeDocker2Manifest + c.desc.MediaType = mediatype.Docker2Manifest m = &docker2Manifest{ common: c, Manifest: mOrig, } case schema2.ManifestList: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeDocker2ManifestList + c.desc.MediaType = mediatype.Docker2ManifestList m = &docker2ManifestList{ common: c, ManifestList: mOrig, } case v1.Manifest: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeOCI1Manifest + c.desc.MediaType = mediatype.OCI1Manifest m = &oci1Manifest{ common: c, Manifest: mOrig, } case v1.Index: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeOCI1ManifestList + c.desc.MediaType = mediatype.OCI1ManifestList m = &oci1Index{ common: c, Index: orig.(v1.Index), } case v1.ArtifactManifest: mt = mOrig.MediaType - c.desc.MediaType = types.MediaTypeOCI1Artifact + c.desc.MediaType = mediatype.OCI1Artifact m = &oci1Artifact{ common: c, ArtifactManifest: mOrig, @@ -458,49 +461,49 @@ func fromCommon(c common) (Manifest, error) { // extract media type from body, either explicitly or with duck typing if c.desc.MediaType == "" { mt := struct { - MediaType string `json:"mediaType,omitempty"` - SchemaVersion int `json:"schemaVersion,omitempty"` - Signatures []interface{} `json:"signatures,omitempty"` - Manifests []types.Descriptor `json:"manifests,omitempty"` - Layers []types.Descriptor `json:"layers,omitempty"` + MediaType string `json:"mediaType,omitempty"` + SchemaVersion int `json:"schemaVersion,omitempty"` + Signatures []interface{} `json:"signatures,omitempty"` + Manifests []descriptor.Descriptor `json:"manifests,omitempty"` + Layers []descriptor.Descriptor `json:"layers,omitempty"` }{} err = json.Unmarshal(c.rawBody, &mt) if mt.MediaType != "" { c.desc.MediaType = mt.MediaType } else if mt.SchemaVersion == 1 && len(mt.Signatures) > 0 { - c.desc.MediaType = types.MediaTypeDocker1ManifestSigned + c.desc.MediaType = mediatype.Docker1ManifestSigned } else if mt.SchemaVersion == 1 { - c.desc.MediaType = types.MediaTypeDocker1Manifest + c.desc.MediaType = mediatype.Docker1Manifest } else if len(mt.Manifests) > 0 { if strings.HasPrefix(mt.Manifests[0].MediaType, "application/vnd.docker.") { - c.desc.MediaType = types.MediaTypeDocker2ManifestList + c.desc.MediaType = mediatype.Docker2ManifestList } else { - c.desc.MediaType = types.MediaTypeOCI1ManifestList + c.desc.MediaType = mediatype.OCI1ManifestList } } else if len(mt.Layers) > 0 { if strings.HasPrefix(mt.Layers[0].MediaType, "application/vnd.docker.") { - c.desc.MediaType = types.MediaTypeDocker2Manifest + c.desc.MediaType = mediatype.Docker2Manifest } else { - c.desc.MediaType = types.MediaTypeOCI1Manifest + c.desc.MediaType = mediatype.OCI1Manifest } } } // compute digest - if c.desc.MediaType != types.MediaTypeDocker1ManifestSigned { + if c.desc.MediaType != mediatype.Docker1ManifestSigned { d := digest.FromBytes(c.rawBody) c.desc.Digest = d c.desc.Size = int64(len(c.rawBody)) } } switch c.desc.MediaType { - case types.MediaTypeDocker1Manifest: + case mediatype.Docker1Manifest: var mOrig schema1.Manifest if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) mt = mOrig.MediaType } m = &docker1Manifest{common: c, Manifest: mOrig} - case types.MediaTypeDocker1ManifestSigned: + case mediatype.Docker1ManifestSigned: var mOrig schema1.SignedManifest if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) @@ -510,35 +513,35 @@ func fromCommon(c common) (Manifest, error) { c.desc.Size = int64(len(mOrig.Canonical)) } m = &docker1SignedManifest{common: c, SignedManifest: mOrig} - case types.MediaTypeDocker2Manifest: + case mediatype.Docker2Manifest: var mOrig schema2.Manifest if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) mt = mOrig.MediaType } m = &docker2Manifest{common: c, Manifest: mOrig} - case types.MediaTypeDocker2ManifestList: + case mediatype.Docker2ManifestList: var mOrig schema2.ManifestList if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) mt = mOrig.MediaType } m = &docker2ManifestList{common: c, ManifestList: mOrig} - case types.MediaTypeOCI1Manifest: + case mediatype.OCI1Manifest: var mOrig v1.Manifest if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) mt = mOrig.MediaType } m = &oci1Manifest{common: c, Manifest: mOrig} - case types.MediaTypeOCI1ManifestList: + case mediatype.OCI1ManifestList: var mOrig v1.Index if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) mt = mOrig.MediaType } m = &oci1Index{common: c, Index: mOrig} - case types.MediaTypeOCI1Artifact: + case mediatype.OCI1Artifact: var mOrig v1.ArtifactManifest if len(c.rawBody) > 0 { err = json.Unmarshal(c.rawBody, &mOrig) @@ -546,7 +549,7 @@ func fromCommon(c common) (Manifest, error) { } m = &oci1Artifact{common: c, ArtifactManifest: mOrig} default: - return nil, fmt.Errorf("%w: \"%s\"", types.ErrUnsupportedMediaType, c.desc.MediaType) + return nil, fmt.Errorf("%w: \"%s\"", errs.ErrUnsupportedMediaType, c.desc.MediaType) } if err != nil { return nil, fmt.Errorf("error unmarshaling manifest for %s: %w", c.r.CommonName(), err) @@ -570,7 +573,7 @@ func verifyMT(expected, received string) error { return nil } -func getPlatformList(dl []types.Descriptor) ([]*platform.Platform, error) { +func getPlatformList(dl []descriptor.Descriptor) ([]*platform.Platform, error) { var l []*platform.Platform for _, d := range dl { if d.Platform != nil { diff --git a/types/manifest/manifest_test.go b/types/manifest/manifest_test.go index 185cf94..375d3b5 100644 --- a/types/manifest/manifest_test.go +++ b/types/manifest/manifest_test.go @@ -9,9 +9,11 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/docker/schema1" "github.com/regclient/regclient/types/docker/schema2" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" "github.com/regclient/regclient/types/ref" @@ -418,7 +420,7 @@ func TestNew(t *testing.T) { if err != nil { t.Fatalf("failed to unmarshal OCI Artifact json: %v", err) } - manifestInvalid.MediaType = types.MediaTypeOCI1Manifest + manifestInvalid.MediaType = mediatype.OCI1Manifest err = json.Unmarshal(rawDockerSchema1Signed, &manifestDockerSchema1Signed) if err != nil { t.Fatalf("failed to unmarshal docker schema1 signed json: %v", err) @@ -427,20 +429,20 @@ func TestNew(t *testing.T) { name string opts []Opts wantR ref.Ref - wantDesc types.Descriptor + wantDesc descriptor.Descriptor wantE error isSet bool testAnnot bool hasAnnot bool testPlat string - wantPlat types.Descriptor + wantPlat descriptor.Descriptor wantSize int64 testSubject bool hasSubject bool }{ { name: "empty", - wantE: fmt.Errorf("%w: \"%s\"", types.ErrUnsupportedMediaType, ""), + wantE: fmt.Errorf("%w: \"%s\"", errs.ErrUnsupportedMediaType, ""), }, { name: "Docker Schema 2 Manifest", @@ -449,8 +451,8 @@ func TestNew(t *testing.T) { WithRaw(rawDockerSchema2), }, wantR: r, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -464,15 +466,15 @@ func TestNew(t *testing.T) { { name: "Docker Schema 2 Manifest full desc", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Digest: digestDockerSchema2, Size: int64(len(rawDockerSchema2)), }), WithRaw(rawDockerSchema2), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -499,7 +501,7 @@ func TestNew(t *testing.T) { testSubject: true, hasAnnot: true, testPlat: "linux/amd64", - wantPlat: types.Descriptor{ + wantPlat: descriptor.Descriptor{ MediaType: "application/vnd.docker.distribution.manifest.v2+json", Digest: "sha256:41b9947d8f19e154a5415c88ef71b851d37fa3ceb1de56ffe88d1b616ce503d9", Size: 1152, @@ -517,7 +519,7 @@ func TestNew(t *testing.T) { testSubject: true, hasAnnot: true, testPlat: "darwin/arm64", - wantPlat: types.Descriptor{ + wantPlat: descriptor.Descriptor{ MediaType: "application/vnd.docker.distribution.manifest.v2+json", Digest: "sha256:b302f648065bb2ba542dc75167db065781f296ef72bb504585d652b27b5079ad", Size: 1152, @@ -529,7 +531,7 @@ func TestNew(t *testing.T) { WithRef(r), WithRaw(rawOCI1Artifact), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeOCI1Artifact}, + "Content-Type": []string{mediatype.OCI1Artifact}, "Docker-Content-Digest": []string{digestOCIArtifact.String()}, }), }, @@ -548,8 +550,8 @@ func TestNew(t *testing.T) { WithRaw(rawOCIImage), }, wantR: r, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Size: int64(len(rawOCIImage)), Digest: digestOCIImage, }, @@ -568,8 +570,8 @@ func TestNew(t *testing.T) { WithRaw(rawOCIIndex), }, wantR: r, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Size: int64(len(rawOCIIndex)), Digest: digestOCIIndex, }, @@ -585,13 +587,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -602,13 +604,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeDocker1Manifest}, + "Content-Type": []string{mediatype.Docker1Manifest}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker1Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker1Manifest, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -619,13 +621,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeDocker1ManifestSigned}, + "Content-Type": []string{mediatype.Docker1ManifestSigned}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker1ManifestSigned, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker1ManifestSigned, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -636,13 +638,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeDocker2Manifest}, + "Content-Type": []string{mediatype.Docker2Manifest}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -653,13 +655,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeDocker2ManifestList}, + "Content-Type": []string{mediatype.Docker2ManifestList}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2ManifestList, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2ManifestList, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -670,13 +672,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeOCI1Manifest}, + "Content-Type": []string{mediatype.OCI1Manifest}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -687,13 +689,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeOCI1ManifestList}, + "Content-Type": []string{mediatype.OCI1ManifestList}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -704,13 +706,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithHeader(http.Header{ - "Content-Type": []string{types.MediaTypeOCI1Artifact}, + "Content-Type": []string{mediatype.OCI1Artifact}, "Content-Length": []string{fmt.Sprintf("%d", len(rawDockerSchema2))}, "Docker-Content-Digest": []string{digestDockerSchema2.String()}, }), }, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1Artifact, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1Artifact, Size: int64(len(rawDockerSchema2)), Digest: digestDockerSchema2, }, @@ -731,8 +733,8 @@ func TestNew(t *testing.T) { name: "Docker Schema 1 Signed Manifest", opts: []Opts{ WithRaw(rawDockerSchema1Signed), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker1ManifestSigned, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker1ManifestSigned, Digest: digestDockerSchema1Signed, Size: int64(len(rawDockerSchema1Signed)), }), @@ -757,8 +759,8 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithRaw(rawAmbiguousOCI), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, }), }, wantE: nil, @@ -769,8 +771,8 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithRaw(rawAmbiguousOCI), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, }), }, wantE: nil, @@ -781,30 +783,30 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithRaw(rawOCIImage), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, }), }, - wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeOCI1ManifestList, types.MediaTypeOCI1Manifest), + wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", mediatype.OCI1ManifestList, mediatype.OCI1Manifest), }, { name: "Invalid OCI Image", opts: []Opts{ WithRef(r), WithRaw(rawOCIIndex), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, }), }, - wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeOCI1Manifest, types.MediaTypeOCI1ManifestList), + wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", mediatype.OCI1Manifest, mediatype.OCI1ManifestList), }, { name: "Invalid digest", opts: []Opts{ WithRef(r), WithRaw(rawDockerSchema2), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Digest: digestInvalid, Size: int64(len(rawDockerSchema2)), }), @@ -816,13 +818,13 @@ func TestNew(t *testing.T) { opts: []Opts{ WithRef(r), WithRaw(rawOCIImage), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Digest: digestOCIImage, Size: int64(len(rawOCIImage)), }), }, - wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeOCI1ManifestList, types.MediaTypeOCI1Manifest), + wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", mediatype.OCI1ManifestList, mediatype.OCI1Manifest), }, { name: "Docker Schema2 Orig", @@ -860,7 +862,7 @@ func TestNew(t *testing.T) { opts: []Opts{ WithOrig(manifestInvalid), }, - wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", types.MediaTypeDocker2Manifest, types.MediaTypeOCI1Manifest), + wantE: fmt.Errorf("manifest contains an unexpected media type: expected %s, received %s", mediatype.Docker2Manifest, mediatype.OCI1Manifest), }, { name: "OCI Image without mediaType", @@ -869,8 +871,8 @@ func TestNew(t *testing.T) { }, wantE: nil, isSet: true, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Size: int64(len(rawOCIImageDuck)), Digest: digestOCIImageDuck, }, @@ -882,8 +884,8 @@ func TestNew(t *testing.T) { }, wantE: nil, isSet: true, - wantDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + wantDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Size: int64(len(rawOCIIndexDuck)), Digest: digestOCIIndexDuck, }, @@ -895,8 +897,8 @@ func TestNew(t *testing.T) { // - test if manifest is set // - test raw body } - subDesc := types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + subDesc := descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Size: 1234, Digest: digest.FromString("test referrer"), } @@ -939,32 +941,32 @@ func TestNew(t *testing.T) { if m.IsSet() { t.Errorf("manifest reports it is set") } - if _, err := m.RawBody(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := m.RawBody(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("RawBody did not return ManifestNotSet: %v", err) } - if _, err := m.MarshalJSON(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := m.MarshalJSON(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("MarshalJSON did not return ManifestNotSet: %v", err) } if ma, ok := m.(Annotator); ok { - if _, err := ma.GetAnnotations(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := ma.GetAnnotations(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("GetAnnotations did not return ManifestNotSet: %v", err) } } if mi, ok := m.(Indexer); ok { - if _, err := mi.GetManifestList(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := mi.GetManifestList(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("GetManifestList did not return ManifestNotSet: %v", err) } } if mi, ok := m.(Imager); ok { - if _, err := mi.GetConfig(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := mi.GetConfig(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("GetConfig did not return ManifestNotSet: %v", err) } - if _, err := mi.GetLayers(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := mi.GetLayers(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("GetLayers did not return ManifestNotSet: %v", err) } } if ms, ok := m.(Subjecter); ok { - if _, err := ms.GetSubject(); !errors.Is(err, types.ErrManifestNotSet) && !errors.Is(err, types.ErrUnsupportedMediaType) { + if _, err := ms.GetSubject(); !errors.Is(err, errs.ErrManifestNotSet) && !errors.Is(err, errs.ErrUnsupportedMediaType) { t.Errorf("GetSubject did not return ManifestNotSet: %v", err) } } @@ -973,32 +975,32 @@ func TestNew(t *testing.T) { if !m.IsSet() { t.Errorf("manifest reports it is not set") } - if _, err := m.RawBody(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := m.RawBody(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("RawBody returned ManifestNotSet: %v", err) } - if _, err := m.MarshalJSON(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := m.MarshalJSON(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("MarshalJSON returned ManifestNotSet: %v", err) } if ma, ok := m.(Annotator); ok { - if _, err := ma.GetAnnotations(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := ma.GetAnnotations(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("GetAnnotations returned ManifestNotSet: %v", err) } } if mi, ok := m.(Indexer); ok { - if _, err := mi.GetManifestList(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := mi.GetManifestList(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("GetManifestList returned ManifestNotSet: %v", err) } } if mi, ok := m.(Imager); ok { - if _, err := mi.GetConfig(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := mi.GetConfig(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("GetConfig returned ManifestNotSet: %v", err) } - if _, err := mi.GetLayers(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := mi.GetLayers(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("GetLayers returned ManifestNotSet: %v", err) } } if ms, ok := m.(Subjecter); ok { - if _, err := ms.GetSubject(); errors.Is(err, types.ErrManifestNotSet) { + if _, err := ms.GetSubject(); errors.Is(err, errs.ErrManifestNotSet) { t.Errorf("GetSubject returned ManifestNotSet: %v", err) } } @@ -1078,7 +1080,7 @@ func TestNew(t *testing.T) { func TestModify(t *testing.T) { t.Parallel() addDigest := digest.FromString("new layer digest") - addDesc := types.Descriptor{ + addDesc := descriptor.Descriptor{ Digest: addDigest, Size: 42, Annotations: map[string]string{ @@ -1090,22 +1092,22 @@ func TestModify(t *testing.T) { tests := []struct { name string opts []Opts - addDesc types.Descriptor - origDesc types.Descriptor + addDesc descriptor.Descriptor + origDesc descriptor.Descriptor }{ { name: "Docker Schema 2 Manifest", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Digest: digestDockerSchema2, Size: int64(len(rawDockerSchema2)), }), WithRaw(rawDockerSchema2), }, addDesc: addDesc, - origDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + origDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Digest: digestDockerSchema2, Size: int64(len(rawDockerSchema2)), }, @@ -1113,16 +1115,16 @@ func TestModify(t *testing.T) { { name: "Docker Schema 2 List", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker2ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker2ManifestList, Digest: digestDockerSchema2List, Size: int64(len(rawDockerSchema2List)), }), WithRaw(rawDockerSchema2List), }, addDesc: addDesc, - origDesc: types.Descriptor{ - MediaType: types.MediaTypeDocker2ManifestList, + origDesc: descriptor.Descriptor{ + MediaType: mediatype.Docker2ManifestList, Digest: digestDockerSchema2List, Size: int64(len(rawDockerSchema2List)), }, @@ -1131,15 +1133,15 @@ func TestModify(t *testing.T) { name: "OCI Image", opts: []Opts{ WithRaw(rawOCIImage), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Digest: digestOCIImage, Size: int64(len(rawOCIImage)), }), }, addDesc: addDesc, - origDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + origDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Digest: digestOCIImage, Size: int64(len(rawOCIImage)), }, @@ -1148,15 +1150,15 @@ func TestModify(t *testing.T) { name: "OCI Index", opts: []Opts{ WithRaw(rawOCIIndex), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Digest: digestOCIIndex, Size: int64(len(rawOCIIndex)), }), }, addDesc: addDesc, - origDesc: types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + origDesc: descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Digest: digestOCIIndex, Size: int64(len(rawOCIIndex)), }, @@ -1298,7 +1300,7 @@ func TestModify(t *testing.T) { func TestSet(t *testing.T) { t.Parallel() addDigest := digest.FromString("new digest") - addDesc := types.Descriptor{ + addDesc := descriptor.Descriptor{ Digest: addDigest, Size: 42, Annotations: map[string]string{ @@ -1318,21 +1320,21 @@ func TestSet(t *testing.T) { { name: "Docker Schema 1", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker1ManifestSigned, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker1ManifestSigned, Digest: digestDockerSchema1Signed, Size: int64(len(rawDockerSchema1Signed)), }), WithRaw(rawDockerSchema1Signed), }, expectImage: true, - expectErr: types.ErrUnsupportedMediaType, + expectErr: errs.ErrUnsupportedMediaType, }, { name: "Docker Schema 2 Manifest", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker2Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker2Manifest, Digest: digestDockerSchema2, Size: int64(len(rawDockerSchema2)), }), @@ -1344,8 +1346,8 @@ func TestSet(t *testing.T) { { name: "Docker Schema 2 List", opts: []Opts{ - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeDocker2ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.Docker2ManifestList, Digest: digestDockerSchema2List, Size: int64(len(rawDockerSchema2List)), }), @@ -1358,8 +1360,8 @@ func TestSet(t *testing.T) { name: "OCI Image", opts: []Opts{ WithRaw(rawOCIImage), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Digest: digestOCIImage, Size: int64(len(rawOCIImage)), }), @@ -1371,8 +1373,8 @@ func TestSet(t *testing.T) { name: "OCI Index", opts: []Opts{ WithRaw(rawOCIIndex), - WithDesc(types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + WithDesc(descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Digest: digestOCIIndex, Size: int64(len(rawOCIIndex)), }), diff --git a/types/manifest/oci1.go b/types/manifest/oci1.go index c95962a..8d92f0e 100644 --- a/types/manifest/oci1.go +++ b/types/manifest/oci1.go @@ -14,16 +14,18 @@ import ( digest "github.com/opencontainers/go-digest" "github.com/regclient/regclient/internal/units" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/platform" ) const ( // MediaTypeOCI1Manifest OCI v1 manifest media type - MediaTypeOCI1Manifest = types.MediaTypeOCI1Manifest + MediaTypeOCI1Manifest = mediatype.OCI1Manifest // MediaTypeOCI1ManifestList OCI v1 manifest list media type - MediaTypeOCI1ManifestList = types.MediaTypeOCI1ManifestList + MediaTypeOCI1ManifestList = mediatype.OCI1ManifestList ) type oci1Manifest struct { @@ -43,72 +45,72 @@ type oci1Artifact struct { func (m *oci1Manifest) GetAnnotations() (map[string]string, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Annotations, nil } -func (m *oci1Manifest) GetConfig() (types.Descriptor, error) { +func (m *oci1Manifest) GetConfig() (descriptor.Descriptor, error) { if !m.manifSet { - return types.Descriptor{}, types.ErrManifestNotSet + return descriptor.Descriptor{}, errs.ErrManifestNotSet } return m.Config, nil } func (m *oci1Manifest) GetConfigDigest() (digest.Digest, error) { if !m.manifSet { - return digest.Digest(""), types.ErrManifestNotSet + return digest.Digest(""), errs.ErrManifestNotSet } return m.Config.Digest, nil } func (m *oci1Index) GetAnnotations() (map[string]string, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Annotations, nil } -func (m *oci1Index) GetConfig() (types.Descriptor, error) { - return types.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Index) GetConfig() (descriptor.Descriptor, error) { + return descriptor.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *oci1Index) GetConfigDigest() (digest.Digest, error) { - return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *oci1Artifact) GetAnnotations() (map[string]string, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Annotations, nil } -func (m *oci1Artifact) GetConfig() (types.Descriptor, error) { - return types.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Artifact) GetConfig() (descriptor.Descriptor, error) { + return descriptor.Descriptor{}, fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *oci1Artifact) GetConfigDigest() (digest.Digest, error) { - return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return "", fmt.Errorf("config digest not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *oci1Manifest) GetManifestList() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Manifest) GetManifestList() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *oci1Index) GetManifestList() ([]types.Descriptor, error) { +func (m *oci1Index) GetManifestList() ([]descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Manifests, nil } -func (m *oci1Artifact) GetManifestList() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Artifact) GetManifestList() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("platform descriptor list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *oci1Manifest) GetLayers() ([]types.Descriptor, error) { +func (m *oci1Manifest) GetLayers() ([]descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Layers, nil } -func (m *oci1Index) GetLayers() ([]types.Descriptor, error) { - return []types.Descriptor{}, fmt.Errorf("layers are not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Index) GetLayers() ([]descriptor.Descriptor, error) { + return []descriptor.Descriptor{}, fmt.Errorf("layers are not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *oci1Artifact) GetLayers() ([]types.Descriptor, error) { +func (m *oci1Artifact) GetLayers() ([]descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Blobs, nil } @@ -123,28 +125,28 @@ func (m *oci1Artifact) GetOrig() interface{} { return m.ArtifactManifest } -func (m *oci1Manifest) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { - return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Manifest) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { + return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *oci1Index) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { +func (m *oci1Index) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } if p == nil { - return nil, fmt.Errorf("invalid input, platform is nil%.0w", types.ErrNotFound) + return nil, fmt.Errorf("invalid input, platform is nil%.0w", errs.ErrNotFound) } - d, err := types.DescriptorListSearch(m.Manifests, types.MatchOpt{Platform: p}) + d, err := descriptor.DescriptorListSearch(m.Manifests, descriptor.MatchOpt{Platform: p}) if err != nil { return nil, fmt.Errorf("platform not found: %s%.0w", *p, err) } return &d, nil } -func (m *oci1Artifact) GetPlatformDesc(p *platform.Platform) (*types.Descriptor, error) { - return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Artifact) GetPlatformDesc(p *platform.Platform) (*descriptor.Descriptor, error) { + return nil, fmt.Errorf("platform lookup not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *oci1Manifest) GetPlatformList() ([]*platform.Platform, error) { - return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *oci1Index) GetPlatformList() ([]*platform.Platform, error) { dl, err := m.GetManifestList() @@ -154,12 +156,12 @@ func (m *oci1Index) GetPlatformList() ([]*platform.Platform, error) { return getPlatformList(dl) } func (m *oci1Artifact) GetPlatformList() ([]*platform.Platform, error) { - return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) + return nil, fmt.Errorf("platform list not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } func (m *oci1Manifest) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } if len(m.rawBody) > 0 { @@ -168,28 +170,28 @@ func (m *oci1Manifest) MarshalJSON() ([]byte, error) { return json.Marshal((m.Manifest)) } -func (m *oci1Manifest) GetSubject() (*types.Descriptor, error) { +func (m *oci1Manifest) GetSubject() (*descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Manifest.Subject, nil } -func (m *oci1Index) GetSubject() (*types.Descriptor, error) { +func (m *oci1Index) GetSubject() (*descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.Index.Subject, nil } -func (m *oci1Artifact) GetSubject() (*types.Descriptor, error) { +func (m *oci1Artifact) GetSubject() (*descriptor.Descriptor, error) { if !m.manifSet { - return nil, types.ErrManifestNotSet + return nil, errs.ErrManifestNotSet } return m.ArtifactManifest.Subject, nil } func (m *oci1Index) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } if len(m.rawBody) > 0 { @@ -200,7 +202,7 @@ func (m *oci1Index) MarshalJSON() ([]byte, error) { } func (m *oci1Artifact) MarshalJSON() ([]byte, error) { if !m.manifSet { - return []byte{}, types.ErrManifestNotSet + return []byte{}, errs.ErrManifestNotSet } if len(m.rawBody) > 0 { @@ -370,7 +372,7 @@ func (m *oci1Artifact) MarshalPretty() ([]byte, error) { func (m *oci1Manifest) SetAnnotation(key, val string) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } if m.Annotations == nil { m.Annotations = map[string]string{} @@ -384,7 +386,7 @@ func (m *oci1Manifest) SetAnnotation(key, val string) error { } func (m *oci1Index) SetAnnotation(key, val string) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } if m.Annotations == nil { m.Annotations = map[string]string{} @@ -398,7 +400,7 @@ func (m *oci1Index) SetAnnotation(key, val string) error { } func (m *oci1Artifact) SetAnnotation(key, val string) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } if m.Annotations == nil { m.Annotations = map[string]string{} @@ -411,21 +413,21 @@ func (m *oci1Artifact) SetAnnotation(key, val string) error { return m.updateDesc() } -func (m *oci1Artifact) SetConfig(d types.Descriptor) error { - return fmt.Errorf("set config not available for media type %s%.0w", m.desc.MediaType, types.ErrUnsupportedMediaType) +func (m *oci1Artifact) SetConfig(d descriptor.Descriptor) error { + return fmt.Errorf("set config not available for media type %s%.0w", m.desc.MediaType, errs.ErrUnsupportedMediaType) } -func (m *oci1Manifest) SetConfig(d types.Descriptor) error { +func (m *oci1Manifest) SetConfig(d descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Config = d return m.updateDesc() } -func (m *oci1Artifact) SetLayers(dl []types.Descriptor) error { +func (m *oci1Artifact) SetLayers(dl []descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Blobs = dl return m.updateDesc() @@ -434,7 +436,7 @@ func (m *oci1Artifact) SetLayers(dl []types.Descriptor) error { // GetSize returns the size in bytes of all layers func (m *oci1Manifest) GetSize() (int64, error) { if !m.manifSet { - return 0, types.ErrManifestNotSet + return 0, errs.ErrManifestNotSet } var total int64 for _, d := range m.Layers { @@ -446,7 +448,7 @@ func (m *oci1Manifest) GetSize() (int64, error) { // GetSize returns the size in bytes of all layers func (m *oci1Artifact) GetSize() (int64, error) { if !m.manifSet { - return 0, types.ErrManifestNotSet + return 0, errs.ErrManifestNotSet } var total int64 for _, d := range m.Blobs { @@ -455,17 +457,17 @@ func (m *oci1Artifact) GetSize() (int64, error) { return total, nil } -func (m *oci1Manifest) SetLayers(dl []types.Descriptor) error { +func (m *oci1Manifest) SetLayers(dl []descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Layers = dl return m.updateDesc() } -func (m *oci1Index) SetManifestList(dl []types.Descriptor) error { +func (m *oci1Index) SetManifestList(dl []descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Manifests = dl return m.updateDesc() @@ -474,11 +476,11 @@ func (m *oci1Index) SetManifestList(dl []types.Descriptor) error { func (m *oci1Manifest) SetOrig(origIn interface{}) error { orig, ok := origIn.(v1.Manifest) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeOCI1Manifest { + if orig.MediaType != mediatype.OCI1Manifest { // TODO: error? - orig.MediaType = types.MediaTypeOCI1Manifest + orig.MediaType = mediatype.OCI1Manifest } m.manifSet = true m.Manifest = orig @@ -489,11 +491,11 @@ func (m *oci1Manifest) SetOrig(origIn interface{}) error { func (m *oci1Index) SetOrig(origIn interface{}) error { orig, ok := origIn.(v1.Index) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeOCI1ManifestList { + if orig.MediaType != mediatype.OCI1ManifestList { // TODO: error? - orig.MediaType = types.MediaTypeOCI1ManifestList + orig.MediaType = mediatype.OCI1ManifestList } m.manifSet = true m.Index = orig @@ -501,23 +503,23 @@ func (m *oci1Index) SetOrig(origIn interface{}) error { return m.updateDesc() } -func (m *oci1Artifact) SetSubject(d *types.Descriptor) error { +func (m *oci1Artifact) SetSubject(d *descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.ArtifactManifest.Subject = d return m.updateDesc() } -func (m *oci1Manifest) SetSubject(d *types.Descriptor) error { +func (m *oci1Manifest) SetSubject(d *descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Manifest.Subject = d return m.updateDesc() } -func (m *oci1Index) SetSubject(d *types.Descriptor) error { +func (m *oci1Index) SetSubject(d *descriptor.Descriptor) error { if !m.manifSet { - return types.ErrManifestNotSet + return errs.ErrManifestNotSet } m.Index.Subject = d return m.updateDesc() @@ -526,11 +528,11 @@ func (m *oci1Index) SetSubject(d *types.Descriptor) error { func (m *oci1Artifact) SetOrig(origIn interface{}) error { orig, ok := origIn.(v1.ArtifactManifest) if !ok { - return types.ErrUnsupportedMediaType + return errs.ErrUnsupportedMediaType } - if orig.MediaType != types.MediaTypeOCI1Artifact { + if orig.MediaType != mediatype.OCI1Artifact { // TODO: error? - orig.MediaType = types.MediaTypeOCI1Artifact + orig.MediaType = mediatype.OCI1Artifact } m.manifSet = true m.ArtifactManifest = orig @@ -544,8 +546,8 @@ func (m *oci1Manifest) updateDesc() error { return err } m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeOCI1Manifest, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.OCI1Manifest, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } @@ -557,8 +559,8 @@ func (m *oci1Index) updateDesc() error { return err } m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeOCI1ManifestList, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.OCI1ManifestList, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } @@ -570,8 +572,8 @@ func (m *oci1Artifact) updateDesc() error { return err } m.rawBody = mj - m.desc = types.Descriptor{ - MediaType: types.MediaTypeOCI1Artifact, + m.desc = descriptor.Descriptor{ + MediaType: mediatype.OCI1Artifact, Digest: digest.FromBytes(mj), Size: int64(len(mj)), } diff --git a/types/mediatype.go b/types/mediatype.go index 16a74c9..d80acde 100644 --- a/types/mediatype.go +++ b/types/mediatype.go @@ -1,50 +1,91 @@ package types -import "strings" +import ( + "github.com/regclient/regclient/types/mediatype" +) const ( // MediaTypeDocker1Manifest deprecated media type for docker schema1 manifests. - MediaTypeDocker1Manifest = "application/vnd.docker.distribution.manifest.v1+json" + // + // Deprecated: replace with [mediatype.Docker1Manifest]. + MediaTypeDocker1Manifest = mediatype.Docker1Manifest // MediaTypeDocker1ManifestSigned is a deprecated schema1 manifest with jws signing. - MediaTypeDocker1ManifestSigned = "application/vnd.docker.distribution.manifest.v1+prettyjws" + // + // Deprecated: replace with [mediatype.Docker1ManifestSigned]. + MediaTypeDocker1ManifestSigned = mediatype.Docker1ManifestSigned // MediaTypeDocker2Manifest is the media type when pulling manifests from a v2 registry. - MediaTypeDocker2Manifest = "application/vnd.docker.distribution.manifest.v2+json" + // + // Deprecated: replace with [mediatype.Docker2Manifest]. + MediaTypeDocker2Manifest = mediatype.Docker2Manifest // MediaTypeDocker2ManifestList is the media type when pulling a manifest list from a v2 registry. - MediaTypeDocker2ManifestList = "application/vnd.docker.distribution.manifest.list.v2+json" + // + // Deprecated: replace with [mediatype.Docker2ManifestList]. + MediaTypeDocker2ManifestList = mediatype.Docker2ManifestList // MediaTypeDocker2ImageConfig is for the configuration json object media type. - MediaTypeDocker2ImageConfig = "application/vnd.docker.container.image.v1+json" + // + // Deprecated: replace with [mediatype.Docker2ImageConfig]. + MediaTypeDocker2ImageConfig = mediatype.Docker2ImageConfig // MediaTypeOCI1Artifact EXPERIMENTAL OCI v1 artifact media type. - MediaTypeOCI1Artifact = "application/vnd.oci.artifact.manifest.v1+json" + // + // Deprecated: replace with [mediatype.OCI1Artifact]. + MediaTypeOCI1Artifact = mediatype.OCI1Artifact // MediaTypeOCI1Manifest OCI v1 manifest media type. - MediaTypeOCI1Manifest = "application/vnd.oci.image.manifest.v1+json" + // + // Deprecated: replace with [mediatype.OCI1Manifest]. + MediaTypeOCI1Manifest = mediatype.OCI1Manifest // MediaTypeOCI1ManifestList OCI v1 manifest list media type. - MediaTypeOCI1ManifestList = "application/vnd.oci.image.index.v1+json" + // + // Deprecated: replace with [mediatype.OCI1ManifestList]. + MediaTypeOCI1ManifestList = mediatype.OCI1ManifestList // MediaTypeOCI1ImageConfig OCI v1 configuration json object media type. - MediaTypeOCI1ImageConfig = "application/vnd.oci.image.config.v1+json" + // + // Deprecated: replace with [mediatype.OCI1ImageConfig]. + MediaTypeOCI1ImageConfig = mediatype.OCI1ImageConfig // MediaTypeDocker2LayerGzip is the default compressed layer for docker schema2. - MediaTypeDocker2LayerGzip = "application/vnd.docker.image.rootfs.diff.tar.gzip" + // + // Deprecated: replace with [mediatype.Docker2LayerGzip]. + MediaTypeDocker2LayerGzip = mediatype.Docker2LayerGzip // MediaTypeDocker2ForeignLayer is the default compressed layer for foreign layers in docker schema2. - MediaTypeDocker2ForeignLayer = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" + // + // Deprecated: replace with [mediatype.Docker2ForeignLayer]. + MediaTypeDocker2ForeignLayer = mediatype.Docker2ForeignLayer // MediaTypeOCI1Layer is the uncompressed layer for OCIv1. - MediaTypeOCI1Layer = "application/vnd.oci.image.layer.v1.tar" + // + // Deprecated: replace with [mediatype.OCI1Layer]. + MediaTypeOCI1Layer = mediatype.OCI1Layer // MediaTypeOCI1LayerGzip is the gzip compressed layer for OCI v1. - MediaTypeOCI1LayerGzip = "application/vnd.oci.image.layer.v1.tar+gzip" + // + // Deprecated: replace with [mediatype.OCI1LayerGzip]. + MediaTypeOCI1LayerGzip = mediatype.OCI1LayerGzip // MediaTypeOCI1LayerZstd is the zstd compressed layer for OCI v1. - MediaTypeOCI1LayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd" + // + // Deprecated: replace with [mediatype.OCI1LayerZstd]. + MediaTypeOCI1LayerZstd = mediatype.OCI1LayerZstd // MediaTypeOCI1ForeignLayer is the foreign layer for OCI v1. - MediaTypeOCI1ForeignLayer = "application/vnd.oci.image.layer.nondistributable.v1.tar" + // + // Deprecated: replace with [mediatype.OCI1ForeignLayer]. + MediaTypeOCI1ForeignLayer = mediatype.OCI1ForeignLayer // MediaTypeOCI1ForeignLayerGzip is the gzip compressed foreign layer for OCI v1. - MediaTypeOCI1ForeignLayerGzip = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" + // + // Deprecated: replace with [mediatype.OCI1ForeignLayerGzip]. + MediaTypeOCI1ForeignLayerGzip = mediatype.OCI1ForeignLayerGzip // MediaTypeOCI1ForeignLayerZstd is the zstd compressed foreign layer for OCI v1. - MediaTypeOCI1ForeignLayerZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd" + // + // Deprecated: replace with [mediatype.OCI1ForeignLayerZstd]. + MediaTypeOCI1ForeignLayerZstd = mediatype.OCI1ForeignLayerZstd // MediaTypeOCI1Empty is used for blobs containing the empty JSON data `{}`. - MediaTypeOCI1Empty = "application/vnd.oci.empty.v1+json" + // + // Deprecated: replace with [mediatype.OCI1Empty]. + MediaTypeOCI1Empty = mediatype.OCI1Empty // MediaTypeBuildkitCacheConfig is used by buildkit cache images. - MediaTypeBuildkitCacheConfig = "application/vnd.buildkit.cacheconfig.v0" + // + // Deprecated: replace with [mediatype.BuildkitCacheConfig]. + MediaTypeBuildkitCacheConfig = mediatype.BuildkitCacheConfig ) -// MediaTypeBase cleans the Content-Type header to return only the lower case base media type. -func MediaTypeBase(orig string) string { - base, _, _ := strings.Cut(orig, ";") - return strings.TrimSpace(strings.ToLower(base)) -} +var ( + // Base cleans the Content-Type header to return only the lower case base media type. + // + // Deprecated: replace with [mediatype.Base]. + MediaTypeBase = mediatype.Base +) diff --git a/types/mediatype/mediatype.go b/types/mediatype/mediatype.go new file mode 100644 index 0000000..914ac53 --- /dev/null +++ b/types/mediatype/mediatype.go @@ -0,0 +1,51 @@ +// Package mediatype defines well known media types. +package mediatype + +import "strings" + +const ( + // Docker1Manifest deprecated media type for docker schema1 manifests. + Docker1Manifest = "application/vnd.docker.distribution.manifest.v1+json" + // Docker1ManifestSigned is a deprecated schema1 manifest with jws signing. + Docker1ManifestSigned = "application/vnd.docker.distribution.manifest.v1+prettyjws" + // Docker2Manifest is the media type when pulling manifests from a v2 registry. + Docker2Manifest = "application/vnd.docker.distribution.manifest.v2+json" + // Docker2ManifestList is the media type when pulling a manifest list from a v2 registry. + Docker2ManifestList = "application/vnd.docker.distribution.manifest.list.v2+json" + // Docker2ImageConfig is for the configuration json object media type. + Docker2ImageConfig = "application/vnd.docker.container.image.v1+json" + // OCI1Artifact EXPERIMENTAL OCI v1 artifact media type. + OCI1Artifact = "application/vnd.oci.artifact.manifest.v1+json" + // OCI1Manifest OCI v1 manifest media type. + OCI1Manifest = "application/vnd.oci.image.manifest.v1+json" + // OCI1ManifestList OCI v1 manifest list media type. + OCI1ManifestList = "application/vnd.oci.image.index.v1+json" + // OCI1ImageConfig OCI v1 configuration json object media type. + OCI1ImageConfig = "application/vnd.oci.image.config.v1+json" + // Docker2LayerGzip is the default compressed layer for docker schema2. + Docker2LayerGzip = "application/vnd.docker.image.rootfs.diff.tar.gzip" + // Docker2ForeignLayer is the default compressed layer for foreign layers in docker schema2. + Docker2ForeignLayer = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" + // OCI1Layer is the uncompressed layer for OCIv1. + OCI1Layer = "application/vnd.oci.image.layer.v1.tar" + // OCI1LayerGzip is the gzip compressed layer for OCI v1. + OCI1LayerGzip = "application/vnd.oci.image.layer.v1.tar+gzip" + // OCI1LayerZstd is the zstd compressed layer for OCI v1. + OCI1LayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd" + // OCI1ForeignLayer is the foreign layer for OCI v1. + OCI1ForeignLayer = "application/vnd.oci.image.layer.nondistributable.v1.tar" + // OCI1ForeignLayerGzip is the gzip compressed foreign layer for OCI v1. + OCI1ForeignLayerGzip = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" + // OCI1ForeignLayerZstd is the zstd compressed foreign layer for OCI v1. + OCI1ForeignLayerZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd" + // OCI1Empty is used for blobs containing the empty JSON data `{}`. + OCI1Empty = "application/vnd.oci.empty.v1+json" + // BuildkitCacheConfig is used by buildkit cache images. + BuildkitCacheConfig = "application/vnd.buildkit.cacheconfig.v0" +) + +// Base cleans the Content-Type header to return only the lower case base media type. +func Base(orig string) string { + base, _, _ := strings.Cut(orig, ";") + return strings.TrimSpace(strings.ToLower(base)) +} diff --git a/types/mediatype_test.go b/types/mediatype/mediatype_test.go similarity index 74% rename from types/mediatype_test.go rename to types/mediatype/mediatype_test.go index b2fb7c2..3fd97ad 100644 --- a/types/mediatype_test.go +++ b/types/mediatype/mediatype_test.go @@ -1,4 +1,4 @@ -package types +package mediatype import "testing" @@ -11,19 +11,19 @@ func TestMediaTypeBase(t *testing.T) { }{ { name: "OCI Index", - orig: MediaTypeOCI1ManifestList, - expect: MediaTypeOCI1ManifestList, + orig: OCI1ManifestList, + expect: OCI1ManifestList, }, { name: "OCI Index with charset", orig: "application/vnd.oci.image.index.v1+json; charset=utf-8", - expect: MediaTypeOCI1ManifestList, + expect: OCI1ManifestList, }, } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - result := MediaTypeBase(tc.orig) + result := Base(tc.orig) if tc.expect != result { t.Errorf("invalid result: expected \"%s\", received \"%s\"", tc.expect, result) } diff --git a/types/oci/v1/artifact.go b/types/oci/v1/artifact.go index 42e6e3b..8effe16 100644 --- a/types/oci/v1/artifact.go +++ b/types/oci/v1/artifact.go @@ -1,8 +1,6 @@ package v1 -import ( - "github.com/regclient/regclient/types" -) +import "github.com/regclient/regclient/types/descriptor" // ArtifactManifest EXPERIMENTAL defines an OCI Artifact type ArtifactManifest struct { @@ -13,10 +11,10 @@ type ArtifactManifest struct { ArtifactType string `json:"artifactType,omitempty"` // Blobs is a collection of blobs referenced by this manifest. - Blobs []types.Descriptor `json:"blobs,omitempty"` + Blobs []descriptor.Descriptor `json:"blobs,omitempty"` // Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest. - Subject *types.Descriptor `json:"subject,omitempty"` + Subject *descriptor.Descriptor `json:"subject,omitempty"` // Annotations contains arbitrary metadata for the artifact manifest. Annotations map[string]string `json:"annotations,omitempty"` diff --git a/types/oci/v1/index.go b/types/oci/v1/index.go index bb72d4d..887a5c8 100644 --- a/types/oci/v1/index.go +++ b/types/oci/v1/index.go @@ -1,7 +1,7 @@ package v1 import ( - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/oci" ) @@ -22,10 +22,10 @@ type Index struct { ArtifactType string `json:"artifactType,omitempty"` // Manifests references platform specific manifests. - Manifests []types.Descriptor `json:"manifests"` + Manifests []descriptor.Descriptor `json:"manifests"` // Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest. - Subject *types.Descriptor `json:"subject,omitempty"` + Subject *descriptor.Descriptor `json:"subject,omitempty"` // Annotations contains arbitrary metadata for the image index. Annotations map[string]string `json:"annotations,omitempty"` diff --git a/types/oci/v1/manifest.go b/types/oci/v1/manifest.go index e70c252..d49c39a 100644 --- a/types/oci/v1/manifest.go +++ b/types/oci/v1/manifest.go @@ -1,7 +1,7 @@ package v1 import ( - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" "github.com/regclient/regclient/types/oci" ) @@ -22,13 +22,13 @@ type Manifest struct { // Config references a configuration object for a container, by digest. // The referenced configuration object is a JSON blob that the runtime uses to set up the container. - Config types.Descriptor `json:"config"` + Config descriptor.Descriptor `json:"config"` // Layers is an indexed list of layers referenced by the manifest. - Layers []types.Descriptor `json:"layers"` + Layers []descriptor.Descriptor `json:"layers"` // Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest. - Subject *types.Descriptor `json:"subject,omitempty"` + Subject *descriptor.Descriptor `json:"subject,omitempty"` // Annotations contains arbitrary metadata for the image manifest. Annotations map[string]string `json:"annotations,omitempty"` diff --git a/types/ref/ref.go b/types/ref/ref.go index e6bee43..c01b02c 100644 --- a/types/ref/ref.go +++ b/types/ref/ref.go @@ -9,7 +9,7 @@ import ( "regexp" "strings" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) const ( @@ -74,9 +74,9 @@ func New(parse string) (Ref, error) { matchRef := refRE.FindStringSubmatch(tail) if matchRef == nil || len(matchRef) < 5 { if refRE.FindStringSubmatch(strings.ToLower(tail)) != nil { - return Ref{}, fmt.Errorf("%w \"%s\", repo must be lowercase", types.ErrInvalidReference, tail) + return Ref{}, fmt.Errorf("%w \"%s\", repo must be lowercase", errs.ErrInvalidReference, tail) } - return Ref{}, fmt.Errorf("%w \"%s\"", types.ErrInvalidReference, tail) + return Ref{}, fmt.Errorf("%w \"%s\"", errs.ErrInvalidReference, tail) } ret.Registry = matchRef[1] ret.Repository = matchRef[2] @@ -100,13 +100,13 @@ func New(parse string) (Ref, error) { ret.Tag = "latest" } if ret.Repository == "" { - return Ref{}, fmt.Errorf("%w \"%s\"", types.ErrInvalidReference, tail) + return Ref{}, fmt.Errorf("%w \"%s\"", errs.ErrInvalidReference, tail) } case "ocidir", "ocifile": matchPath := ocidirRE.FindStringSubmatch(tail) if matchPath == nil || len(matchPath) < 2 || matchPath[1] == "" { - return Ref{}, fmt.Errorf("%w, invalid path for scheme \"%s\": %s", types.ErrInvalidReference, scheme, tail) + return Ref{}, fmt.Errorf("%w, invalid path for scheme \"%s\": %s", errs.ErrInvalidReference, scheme, tail) } ret.Path = matchPath[1] if len(matchPath) > 2 && matchPath[2] != "" { @@ -117,7 +117,7 @@ func New(parse string) (Ref, error) { } default: - return Ref{}, fmt.Errorf("%w, unknown scheme \"%s\" in \"%s\"", types.ErrInvalidReference, scheme, parse) + return Ref{}, fmt.Errorf("%w, unknown scheme \"%s\" in \"%s\"", errs.ErrInvalidReference, scheme, parse) } return ret, nil } @@ -141,22 +141,22 @@ func NewHost(parse string) (Ref, error) { ret.Scheme = "reg" matchReg := registryRE.FindStringSubmatch(tail) if matchReg == nil || len(matchReg) < 2 { - return Ref{}, fmt.Errorf("%w \"%s\"", types.ErrParsingFailed, tail) + return Ref{}, fmt.Errorf("%w \"%s\"", errs.ErrParsingFailed, tail) } ret.Registry = matchReg[1] if ret.Registry == "" { - return Ref{}, fmt.Errorf("%w \"%s\"", types.ErrParsingFailed, tail) + return Ref{}, fmt.Errorf("%w \"%s\"", errs.ErrParsingFailed, tail) } case "ocidir", "ocifile": matchPath := ocidirRE.FindStringSubmatch(tail) if matchPath == nil || len(matchPath) < 2 || matchPath[1] == "" { - return Ref{}, fmt.Errorf("%w, invalid path for scheme \"%s\": %s", types.ErrParsingFailed, scheme, tail) + return Ref{}, fmt.Errorf("%w, invalid path for scheme \"%s\": %s", errs.ErrParsingFailed, scheme, tail) } ret.Path = matchPath[1] default: - return Ref{}, fmt.Errorf("%w, unknown scheme \"%s\" in \"%s\"", types.ErrParsingFailed, scheme, parse) + return Ref{}, fmt.Errorf("%w, unknown scheme \"%s\" in \"%s\"", errs.ErrParsingFailed, scheme, parse) } return ret, nil } diff --git a/types/ref/ref_test.go b/types/ref/ref_test.go index 3a3a622..dda8147 100644 --- a/types/ref/ref_test.go +++ b/types/ref/ref_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) var testDigest = "sha256:15f840677a5e245d9ea199eb9b026b1539208a5183621dced7b469f6aa678115" @@ -268,7 +268,7 @@ func TestNew(t *testing.T) { { name: "OCI file with invalid digest", ref: "ocifile://path/to/file.tgz@sha256:ZZ15f840677a5e245d9ea199eb9b026b1539208a5183621dced7b469f6aa678115ZZ", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "OCI dir", @@ -306,82 +306,82 @@ func TestNew(t *testing.T) { { name: "invalid scheme", ref: "unknown://repo:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid host leading dash", ref: "-docker.io/project/image:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid host trailing dash", ref: "docker-.io/project/image:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid repo case", ref: "docker.io/Upper/Case/Repo:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid repo dash leading", ref: "project/-image:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid repo dash trailing", ref: "project/image-:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid repo triple underscore", ref: "project/image___x:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid repo chars", ref: "project/star*:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid tag chars", ref: "project/image:tag^1", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid tag length", ref: "project/image:" + strings.Repeat("x", 129), - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid short digest", ref: "project/image@sha256:12345", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid digest characters", ref: "project/image@sha256:gggg40677a5e245d9ea199eb9b026b1539208a5183621dced7b469f6aa678115", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid ocidir path", ref: "ocidir://invalid*filename:tag", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid ocidir tag", ref: "ocidir://filename:tag=fail", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "invalid ocidir digest", ref: "ocidir://filename@sha256:abcd", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, { name: "localhost missing repo", ref: "localhost:5000", - wantE: types.ErrInvalidReference, + wantE: errs.ErrInvalidReference, }, } @@ -436,7 +436,7 @@ func TestNewHost(t *testing.T) { { name: "empty string", host: "", - wantE: types.ErrParsingFailed, + wantE: errs.ErrParsingFailed, }, { name: "Docker Hub", @@ -537,7 +537,7 @@ func TestNewHost(t *testing.T) { { name: "OCI file with invalid digest", host: "ocifile://path/to/file.tgz@sha256:ZZ15f840677a5e245d9ea199eb9b026b1539208a5183621dced7b469f6aa678115ZZ", - wantE: types.ErrParsingFailed, + wantE: errs.ErrParsingFailed, }, { name: "OCI dir", @@ -566,17 +566,17 @@ func TestNewHost(t *testing.T) { { name: "invalid scheme", host: "unknown://repo:tag", - wantE: types.ErrParsingFailed, + wantE: errs.ErrParsingFailed, }, { name: "invalid host leading dash", host: "-docker.io", - wantE: types.ErrParsingFailed, + wantE: errs.ErrParsingFailed, }, { name: "invalid host trailing dash", host: "docker-.io", - wantE: types.ErrParsingFailed, + wantE: errs.ErrParsingFailed, }, } diff --git a/types/referrer/referrer.go b/types/referrer/referrer.go index fa1b12e..2cbb727 100644 --- a/types/referrer/referrer.go +++ b/types/referrer/referrer.go @@ -9,7 +9,8 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" v1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" @@ -17,11 +18,11 @@ import ( // ReferrerList contains the response to a request for referrers to a subject type ReferrerList struct { - Subject ref.Ref `json:"subject"` // subject queried - Descriptors []types.Descriptor `json:"descriptors"` // descriptors found in Index - Annotations map[string]string `json:"annotations,omitempty"` // annotations extracted from Index - Manifest manifest.Manifest `json:"-"` // returned OCI Index - Tags []string `json:"-"` // tags matched when fetching referrers + Subject ref.Ref `json:"subject"` // subject queried + Descriptors []descriptor.Descriptor `json:"descriptors"` // descriptors found in Index + Annotations map[string]string `json:"annotations,omitempty"` // annotations extracted from Index + Manifest manifest.Manifest `json:"-"` // returned OCI Index + Tags []string `json:"-"` // tags matched when fetching referrers } // Add appends an entry to rl.Manifest, used to modify the client managed Index @@ -54,7 +55,7 @@ func (rl *ReferrerList) Add(m manifest.Manifest) error { mDesc.ArtifactType = mOrig.ArtifactType default: // other types are not supported - return fmt.Errorf("invalid manifest for referrer \"%t\": %w", m.GetOrig(), types.ErrUnsupportedMediaType) + return fmt.Errorf("invalid manifest for referrer \"%t\": %w", m.GetOrig(), errs.ErrUnsupportedMediaType) } // append descriptor to index rlM.Manifests = append(rlM.Manifests, mDesc) @@ -86,7 +87,7 @@ func (rl *ReferrerList) Delete(m manifest.Manifest) error { } } if !found { - return fmt.Errorf("subject not found in referrer list%.0w", types.ErrNotFound) + return fmt.Errorf("subject not found in referrer list%.0w", errs.ErrNotFound) } rl.Descriptors = rlM.Manifests err := rl.Manifest.SetOrig(rlM) diff --git a/types/referrer/referrer_test.go b/types/referrer/referrer_test.go index fad7c79..37cde7e 100644 --- a/types/referrer/referrer_test.go +++ b/types/referrer/referrer_test.go @@ -6,8 +6,10 @@ import ( "github.com/opencontainers/go-digest" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/descriptor" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/manifest" + "github.com/regclient/regclient/types/mediatype" v1 "github.com/regclient/regclient/types/oci/v1" ) @@ -121,13 +123,13 @@ const bDockerImg = ` ` var mOCIImg, mOCIImgAT, mOCIIndex, mDockerImg manifest.Manifest -var dOCIImg = types.Descriptor{ +var dOCIImg = descriptor.Descriptor{ MediaType: "application/vnd.oci.image.manifest.v1+json", ArtifactType: "application/vnd.example.config.v1+json", Size: int64(len(bOCIImg)), Digest: digest.FromString(bOCIImg), } -var dOCIImgAT = types.Descriptor{ +var dOCIImgAT = descriptor.Descriptor{ MediaType: "application/vnd.oci.image.manifest.v1+json", ArtifactType: "application/vnd.example.data", Size: int64(len(bOCIImgAT)), @@ -137,7 +139,7 @@ var dOCIImgAT = types.Descriptor{ "com.example.version": "1.0", }, } -var dOCIIndex = types.Descriptor{ +var dOCIIndex = descriptor.Descriptor{ MediaType: "application/vnd.oci.image.index.v1+json", ArtifactType: "application/vnd.example.data", Size: int64(len(bOCIIndex)), @@ -172,13 +174,13 @@ func TestEmpty(t *testing.T) { t.Parallel() // create an empty list and full list, test is empty rlEmpty := &ReferrerList{ - Descriptors: []types.Descriptor{}, + Descriptors: []descriptor.Descriptor{}, Annotations: map[string]string{}, Tags: []string{}, } mEmpty, err := manifest.New(manifest.WithOrig(v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, })) if err != nil { t.Fatalf("failed to generate index: %v", err) @@ -189,7 +191,7 @@ func TestEmpty(t *testing.T) { } rlPopulated := &ReferrerList{ - Descriptors: []types.Descriptor{ + Descriptors: []descriptor.Descriptor{ dOCIImg, dOCIImgAT, dOCIIndex, @@ -199,8 +201,8 @@ func TestEmpty(t *testing.T) { } mPopulated, err := manifest.New(manifest.WithOrig(v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ dOCIImg, dOCIImgAT, dOCIIndex, @@ -241,18 +243,18 @@ func TestAdd(t *testing.T) { { name: "Docker Image", m: mDockerImg, - expectedErr: types.ErrUnsupportedMediaType, + expectedErr: errs.ErrUnsupportedMediaType, }, } // add manifests (image without AT, image with AT, artifact, docker, no subject), verify list contents and error handling rl := &ReferrerList{ - Descriptors: []types.Descriptor{}, + Descriptors: []descriptor.Descriptor{}, Annotations: map[string]string{}, Tags: []string{}, } m, err := manifest.New(manifest.WithOrig(v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, + MediaType: mediatype.OCI1ManifestList, })) if err != nil { t.Fatalf("failed to generate empty index: %v", err) @@ -279,7 +281,7 @@ func TestAdd(t *testing.T) { t.Errorf("number of descriptors, expected 3, received %d", len(rl.Descriptors)) } for _, d := range rl.Descriptors { - if d.ArtifactType == types.MediaTypeOCI1Empty || d.ArtifactType == "" { + if d.ArtifactType == mediatype.OCI1Empty || d.ArtifactType == "" { t.Errorf("unexpected artifact type: %s", d.ArtifactType) } } @@ -288,7 +290,7 @@ func TestAdd(t *testing.T) { func TestDelete(t *testing.T) { t.Parallel() rl := &ReferrerList{ - Descriptors: []types.Descriptor{ + Descriptors: []descriptor.Descriptor{ dOCIImg, dOCIImgAT, }, @@ -297,8 +299,8 @@ func TestDelete(t *testing.T) { } m, err := manifest.New(manifest.WithOrig(v1.Index{ Versioned: v1.IndexSchemaVersion, - MediaType: types.MediaTypeOCI1ManifestList, - Manifests: []types.Descriptor{ + MediaType: mediatype.OCI1ManifestList, + Manifests: []descriptor.Descriptor{ dOCIImg, dOCIImgAT, dOCIIndex, @@ -325,7 +327,7 @@ func TestDelete(t *testing.T) { { name: "OCI Image again", m: mOCIImg, - expectedErr: types.ErrNotFound, + expectedErr: errs.ErrNotFound, }, { name: "OCI Index", @@ -334,7 +336,7 @@ func TestDelete(t *testing.T) { { name: "Docker Image", m: mDockerImg, - expectedErr: types.ErrNotFound, + expectedErr: errs.ErrNotFound, }, } for _, tt := range tests { diff --git a/types/repo/repolist.go b/types/repo/repolist.go index 6094393..a27aa25 100644 --- a/types/repo/repolist.go +++ b/types/repo/repolist.go @@ -9,9 +9,10 @@ import ( "sort" "strings" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) +// RepoList is the response for a repository listing. type RepoList struct { repoCommon RepoRegistryList @@ -34,6 +35,7 @@ type repoConfig struct { type Opts func(*repoConfig) +// New is used to create a repository listing. func New(opts ...Opts) (*RepoList, error) { conf := repoConfig{ mt: "application/json", @@ -57,7 +59,7 @@ func New(opts ...Opts) (*RepoList, error) { return nil, err } default: - return nil, fmt.Errorf("%w: media type: %s, hostname: %s", types.ErrUnsupportedMediaType, conf.mt, conf.host) + return nil, fmt.Errorf("%w: media type: %s, hostname: %s", errs.ErrUnsupportedMediaType, conf.mt, conf.host) } rl.repoCommon = rc @@ -102,7 +104,7 @@ func (r repoCommon) MarshalJSON() ([]byte, error) { if r.orig != nil { return json.Marshal((r.orig)) } - return []byte{}, fmt.Errorf("JSON marshalling failed: %w", types.ErrNotFound) + return []byte{}, fmt.Errorf("JSON marshalling failed: %w", errs.ErrNotFound) } func (r repoCommon) RawBody() ([]byte, error) { diff --git a/types/repo/repolist_test.go b/types/repo/repolist_test.go index 6113d2a..e0c5a34 100644 --- a/types/repo/repolist_test.go +++ b/types/repo/repolist_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" ) func TestNew(t *testing.T) { @@ -56,7 +56,7 @@ func TestNew(t *testing.T) { WithHeaders(registryHeaders), WithMT("application/unknown"), }, - err: types.ErrUnsupportedMediaType, + err: errs.ErrUnsupportedMediaType, }, } diff --git a/types/tag/taglist.go b/types/tag/taglist.go index 0d098bb..02661e2 100644 --- a/types/tag/taglist.go +++ b/types/tag/taglist.go @@ -10,14 +10,15 @@ import ( "sort" "strings" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" + "github.com/regclient/regclient/types/mediatype" ociv1 "github.com/regclient/regclient/types/oci/v1" "github.com/regclient/regclient/types/ref" ) -// List contains a tag list -// Currently this is a struct but the underlying type could be changed to an interface in the future -// Using methods is recommended over directly accessing fields +// List contains a tag list. +// Currently this is a struct but the underlying type could be changed to an interface in the future. +// Using methods is recommended over directly accessing fields. type List struct { tagCommon DockerList @@ -34,19 +35,19 @@ type tagCommon struct { url *url.URL } -// DockerList is returned from registry/2.0 API's +// DockerList is returned from registry/2.0 API's. type DockerList struct { Name string `json:"name"` Tags []string `json:"tags"` } -// GCRList fields are from gcr.io +// GCRList fields are from gcr.io. type GCRList struct { Children []string `json:"child,omitempty"` Manifests map[string]GCRManifestInfo `json:"manifest,omitempty"` } -// LayoutList includes the OCI Index from an OCI Layout +// LayoutList includes the OCI Index from an OCI Layout. type LayoutList struct { Index ociv1.Index } @@ -61,11 +62,11 @@ type tagConfig struct { url *url.URL } -// Opts defines options for creating a new tag +// Opts defines options for creating a new tag. type Opts func(*tagConfig) -// New creates a tag list from options -// Tags may be provided directly, or they will be parsed from the raw input based on the media type +// New creates a tag list from options. +// Tags may be provided directly, or they will be parsed from the raw input based on the media type. func New(opts ...Opts) (*List, error) { conf := tagConfig{} for _, opt := range opts { @@ -89,17 +90,17 @@ func New(opts ...Opts) (*List, error) { tl.LayoutList.Index = conf.index } if len(conf.raw) > 0 { - mt := types.MediaTypeBase(conf.mt) + mt := mediatype.Base(conf.mt) switch mt { case "application/json", "text/plain": err := json.Unmarshal(conf.raw, &tl) if err != nil { return nil, err } - case types.MediaTypeOCI1ManifestList: + case mediatype.OCI1ManifestList: // noop default: - return nil, fmt.Errorf("%w: media type: %s, reference: %s", types.ErrUnsupportedMediaType, conf.mt, conf.ref.CommonName()) + return nil, fmt.Errorf("%w: media type: %s, reference: %s", errs.ErrUnsupportedMediaType, conf.mt, conf.ref.CommonName()) } } tl.tagCommon = tc @@ -107,42 +108,42 @@ func New(opts ...Opts) (*List, error) { return &tl, nil } -// WithHeaders includes data from http headers when creating tag list +// WithHeaders includes data from http headers when creating tag list. func WithHeaders(header http.Header) Opts { return func(tConf *tagConfig) { tConf.header = header } } -// WithLayoutIndex include the index from an OCI Layout +// WithLayoutIndex include the index from an OCI Layout. func WithLayoutIndex(index ociv1.Index) Opts { return func(tConf *tagConfig) { tConf.index = index } } -// WithMT sets the returned media type on the tag list +// WithMT sets the returned media type on the tag list. func WithMT(mt string) Opts { return func(tConf *tagConfig) { tConf.mt = mt } } -// WithRaw defines the raw response from the tag list request +// WithRaw defines the raw response from the tag list request. func WithRaw(raw []byte) Opts { return func(tConf *tagConfig) { tConf.raw = raw } } -// WithRef specifies the reference (repository) associated with the tag list +// WithRef specifies the reference (repository) associated with the tag list. func WithRef(ref ref.Ref) Opts { return func(tConf *tagConfig) { tConf.ref = ref } } -// WithResp includes the response from an http request +// WithResp includes the response from an http request. func WithResp(resp *http.Response) Opts { return func(tConf *tagConfig) { if len(tConf.raw) == 0 { @@ -163,14 +164,14 @@ func WithResp(resp *http.Response) Opts { } } -// WithTags provides the parsed tags for the tag list +// WithTags provides the parsed tags for the tag list. func WithTags(tags []string) Opts { return func(tConf *tagConfig) { tConf.tags = tags } } -// Append extends a tag list with another +// Append extends a tag list with another. func (l *List) Append(add *List) error { // verify two lists are compatible if l.mt != add.mt || !ref.EqualRepository(l.r, add.r) || l.Name != add.Name { @@ -204,12 +205,12 @@ func (l *List) Append(add *List) error { return nil } -// GetOrig returns the underlying tag data structure if defined +// GetOrig returns the underlying tag data structure if defined. func (t tagCommon) GetOrig() interface{} { return t.orig } -// MarshalJSON returns the tag list in json +// MarshalJSON returns the tag list in json. func (t tagCommon) MarshalJSON() ([]byte, error) { if len(t.rawBody) > 0 { return t.rawBody, nil @@ -218,30 +219,30 @@ func (t tagCommon) MarshalJSON() ([]byte, error) { if t.orig != nil { return json.Marshal((t.orig)) } - return []byte{}, fmt.Errorf("JSON marshalling failed: %w", types.ErrNotFound) + return []byte{}, fmt.Errorf("JSON marshalling failed: %w", errs.ErrNotFound) } -// RawBody returns the original tag list response +// RawBody returns the original tag list response. func (t tagCommon) RawBody() ([]byte, error) { return t.rawBody, nil } -// RawHeaders returns the received http headers +// RawHeaders returns the received http headers. func (t tagCommon) RawHeaders() (http.Header, error) { return t.rawHeader, nil } -// GetURL returns the URL of the request +// GetURL returns the URL of the request. func (t tagCommon) GetURL() *url.URL { return t.url } -// GetTags returns the tags from a list +// GetTags returns the tags from a list. func (tl DockerList) GetTags() ([]string, error) { return tl.Tags, nil } -// MarshalPretty is used for printPretty template formatting +// MarshalPretty is used for printPretty template formatting. func (tl DockerList) MarshalPretty() ([]byte, error) { sort.Slice(tl.Tags, func(i, j int) bool { return strings.Compare(tl.Tags[i], tl.Tags[j]) < 0 diff --git a/types/tag/taglist_test.go b/types/tag/taglist_test.go index 247db92..9ead76e 100644 --- a/types/tag/taglist_test.go +++ b/types/tag/taglist_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - "github.com/regclient/regclient/types" + "github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/ref" ) @@ -200,7 +200,7 @@ func TestNew(t *testing.T) { WithHeaders(registryHeaders), WithMT("application/unknown"), }, - err: types.ErrUnsupportedMediaType, + err: errs.ErrUnsupportedMediaType, }, }