/
manifest.go
144 lines (131 loc) · 4.41 KB
/
manifest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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...)
}