1
0
mirror of https://github.com/regclient/regclient.git synced 2025-04-18 22:44:00 +03:00
regclient/manifest.go
Brandon Mitchell eea06e2a5c
Refactoring the type package
I feel like I need to explain, this is all to move the descriptor package.
The platform package could not use the predefined errors in types because of a circular dependency from descriptor.
The most appropriate way to reorg this is to move descriptor out of the type package since it was more complex than a self contained type.
When doing that, type aliases were needed to avoid breaking changes to existing users.
Those aliases themselves caused circular dependency loops because of the media types and errors, so those were also pulled out to separate packages.
All of the old values were aliased and deprecated, and to fix the linter, those deprecations were fixed by updating the imports... everywhere.

Signed-off-by: Brandon Mitchell <git@bmitch.net>
2024-03-04 15:43:18 -05:00

145 lines
4.4 KiB
Go

package regclient
import (
"context"
"fmt"
"github.com/regclient/regclient/scheme"
"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 descriptor.Descriptor
schemeOpts []scheme.ManifestOpts
requireDigest bool
}
// ManifestOpts define options for the Manifest* commands.
type ManifestOpts func(*manifestOpt)
// WithManifest passes a manifest to ManifestDelete.
func WithManifest(m manifest.Manifest) ManifestOpts {
return func(opts *manifestOpt) {
opts.schemeOpts = append(opts.schemeOpts, scheme.WithManifest(m))
}
}
// WithManifestCheckReferrers checks for referrers field on ManifestDelete.
// This will update the client managed referrer listing.
func WithManifestCheckReferrers() ManifestOpts {
return func(opts *manifestOpt) {
opts.schemeOpts = append(opts.schemeOpts, scheme.WithManifestCheckReferrers())
}
}
// WithManifestChild for ManifestPut indicates the manifest is not the top level manifest being copied.
// This is used by the ocidir scheme to determine what entries to include in the index.json.
func WithManifestChild() ManifestOpts {
return func(opts *manifestOpt) {
opts.schemeOpts = append(opts.schemeOpts, scheme.WithManifestChild())
}
}
// WithManifestDesc includes the descriptor for ManifestGet.
// This is used to automatically extract a Data field if available.
func WithManifestDesc(d descriptor.Descriptor) ManifestOpts {
return func(opts *manifestOpt) {
opts.d = d
}
}
// WithManifestRequireDigest falls back from a HEAD to a GET request when digest headers aren't received.
func WithManifestRequireDigest() ManifestOpts {
return func(opts *manifestOpt) {
opts.requireDigest = true
}
}
// ManifestDelete removes a manifest, including all tags pointing to that registry.
// The reference must include the digest to delete (see TagDelete for deleting a tag).
// 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(), errs.ErrInvalidReference)
}
opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}}
for _, fn := range opts {
fn(&opt)
}
schemeAPI, err := rc.schemeGet(r.Scheme)
if err != nil {
return err
}
return schemeAPI.ManifestDelete(ctx, r, opt.schemeOpts...)
}
// 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(), errs.ErrInvalidReference)
}
opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}}
for _, fn := range opts {
fn(&opt)
}
if opt.d.Digest != "" {
r.Digest = opt.d.Digest.String()
data, err := opt.d.GetData()
if err == nil {
return manifest.New(
manifest.WithDesc(opt.d),
manifest.WithRaw(data),
manifest.WithRef(r),
)
}
}
schemeAPI, err := rc.schemeGet(r.Scheme)
if err != nil {
return nil, err
}
return schemeAPI.ManifestGet(ctx, r)
}
// 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(), errs.ErrInvalidReference)
}
opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}}
for _, fn := range opts {
fn(&opt)
}
schemeAPI, err := rc.schemeGet(r.Scheme)
if err != nil {
return nil, err
}
m, err := schemeAPI.ManifestHead(ctx, r)
if err != nil {
return m, err
}
if opt.requireDigest && m.GetDescriptor().Digest.String() == "" {
m, err = schemeAPI.ManifestGet(ctx, r)
}
return m, err
}
// ManifestPut pushes a manifest.
// 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(), errs.ErrInvalidReference)
}
opt := manifestOpt{schemeOpts: []scheme.ManifestOpts{}}
for _, fn := range opts {
fn(&opt)
}
schemeAPI, err := rc.schemeGet(r.Scheme)
if err != nil {
return err
}
return schemeAPI.ManifestPut(ctx, r, m, opt.schemeOpts...)
}