@@ -18,11 +18,14 @@ package oras
18
18
import (
19
19
"context"
20
20
"fmt"
21
+ "io"
21
22
22
23
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
24
+ "oras.land/oras-go/v2/content"
23
25
"oras.land/oras-go/v2/errdef"
24
26
"oras.land/oras-go/v2/internal/cas"
25
27
"oras.land/oras-go/v2/internal/docker"
28
+ "oras.land/oras-go/v2/internal/platform"
26
29
"oras.land/oras-go/v2/registry"
27
30
)
28
31
@@ -60,14 +63,19 @@ type ResolveOptions struct {
60
63
}
61
64
62
65
// Resolve resolves a descriptor with provided reference from the target.
63
- func Resolve (ctx context.Context , target ReadOnlyTarget , ref string , opts ResolveOptions ) (ocispec.Descriptor , error ) {
66
+ func Resolve (ctx context.Context , target ReadOnlyTarget , reference string , opts ResolveOptions ) (ocispec.Descriptor , error ) {
64
67
if opts .TargetPlatform == nil {
65
- return target .Resolve (ctx , ref )
68
+ return target .Resolve (ctx , reference )
66
69
}
70
+ return resolve (ctx , target , nil , reference , opts )
71
+ }
67
72
73
+ // resolve resolves a descriptor with provided reference from the target, with
74
+ // specified caching.
75
+ func resolve (ctx context.Context , target ReadOnlyTarget , proxy * cas.Proxy , reference string , opts ResolveOptions ) (ocispec.Descriptor , error ) {
68
76
if refFetcher , ok := target .(registry.ReferenceFetcher ); ok {
69
77
// optimize performance for ReferenceFetcher targets
70
- desc , rc , err := refFetcher .FetchReference (ctx , ref )
78
+ desc , rc , err := refFetcher .FetchReference (ctx , reference )
71
79
if err != nil {
72
80
return ocispec.Descriptor {}, err
73
81
}
@@ -76,25 +84,110 @@ func Resolve(ctx context.Context, target ReadOnlyTarget, ref string, opts Resolv
76
84
switch desc .MediaType {
77
85
case docker .MediaTypeManifestList , ocispec .MediaTypeImageIndex ,
78
86
docker .MediaTypeManifest , ocispec .MediaTypeImageManifest :
79
- // create a proxy to cache the fetched descriptor
80
- store := cas .NewMemory ()
81
- err = store .Push (ctx , desc , rc )
82
- if err != nil {
87
+ // cache the fetched content
88
+ if proxy == nil {
89
+ proxy = cas .NewProxy (target , cas .NewMemory ())
90
+ }
91
+ if err := proxy .Cache .Push (ctx , desc , rc ); err != nil {
83
92
return ocispec.Descriptor {}, err
84
93
}
85
-
86
- proxy := cas .NewProxy (target , store )
94
+ // stop caching as SelectManifest may fetch a config blob
87
95
proxy .StopCaching = true
88
- return selectPlatform (ctx , proxy , desc , opts .TargetPlatform )
96
+ return platform . SelectManifest (ctx , proxy , desc , opts .TargetPlatform )
89
97
default :
90
98
return ocispec.Descriptor {}, fmt .Errorf ("%s: %s: %w" , desc .Digest , desc .MediaType , errdef .ErrUnsupported )
91
99
}
92
100
}
93
101
94
- desc , err := target .Resolve (ctx , ref )
102
+ desc , err := target .Resolve (ctx , reference )
95
103
if err != nil {
96
104
return ocispec.Descriptor {}, err
97
105
}
106
+ return platform .SelectManifest (ctx , target , desc , opts .TargetPlatform )
107
+ }
108
+
109
+ // DefaultFetchOptions provides the default FetchOptions.
110
+ var DefaultFetchOptions FetchOptions
111
+
112
+ // FetchOptions contains parameters for oras.Fetch.
113
+ type FetchOptions struct {
114
+ // ResolveOptions contains parameters for resolving reference.
115
+ ResolveOptions
116
+ }
117
+
118
+ // Fetch fetches the content identified by the reference.
119
+ func Fetch (ctx context.Context , target ReadOnlyTarget , reference string , opts FetchOptions ) (ocispec.Descriptor , io.ReadCloser , error ) {
120
+ if opts .TargetPlatform == nil {
121
+ if refFetcher , ok := target .(registry.ReferenceFetcher ); ok {
122
+ return refFetcher .FetchReference (ctx , reference )
123
+ }
124
+
125
+ desc , err := target .Resolve (ctx , reference )
126
+ if err != nil {
127
+ return ocispec.Descriptor {}, nil , err
128
+ }
129
+ rc , err := target .Fetch (ctx , desc )
130
+ if err != nil {
131
+ return ocispec.Descriptor {}, nil , err
132
+ }
133
+ return desc , rc , nil
134
+ }
135
+
136
+ proxy := cas .NewProxy (target , cas .NewMemory ())
137
+ desc , err := resolve (ctx , target , proxy , reference , opts .ResolveOptions )
138
+ if err != nil {
139
+ return ocispec.Descriptor {}, nil , err
140
+ }
141
+ // if the content exists in cache, fetch it from cache
142
+ // otherwise fetch without caching
143
+ proxy .StopCaching = true
144
+ rc , err := proxy .Fetch (ctx , desc )
145
+ if err != nil {
146
+ return ocispec.Descriptor {}, nil , err
147
+ }
148
+ return desc , rc , nil
149
+ }
150
+
151
+ // DefaultFetchBytesOptions provides the default FetchBytesOptions.
152
+ var DefaultFetchBytesOptions = FetchBytesOptions {
153
+ MaxBytes : defaultMaxBytes ,
154
+ }
155
+
156
+ // defaultMaxBytes is the default value of MaxBytes.
157
+ const defaultMaxBytes int64 = 4 * 1024 * 1024 // 4 MiB
158
+
159
+ // FetchBytesOptions contains parameters for oras.FetchBytes.
160
+ type FetchBytesOptions struct {
161
+ // FetchOptions contains parameters for fetching content.
162
+ FetchOptions
163
+ // MaxBytes limits the maximum size of the fetched content bytes.
164
+ // If less than or equal to 0, a default (currently 4 MiB) is used.
165
+ MaxBytes int64
166
+ }
167
+
168
+ // FetchBytes fetches the content bytes identified by the reference.
169
+ func FetchBytes (ctx context.Context , target ReadOnlyTarget , reference string , opts FetchBytesOptions ) (ocispec.Descriptor , []byte , error ) {
170
+ if opts .MaxBytes <= 0 {
171
+ opts .MaxBytes = defaultMaxBytes
172
+ }
173
+
174
+ desc , rc , err := Fetch (ctx , target , reference , opts .FetchOptions )
175
+ if err != nil {
176
+ return ocispec.Descriptor {}, nil , err
177
+ }
178
+ defer rc .Close ()
179
+
180
+ if desc .Size > opts .MaxBytes {
181
+ return ocispec.Descriptor {}, nil , fmt .Errorf (
182
+ "content size %v exceeds MaxBytes %v: %w" ,
183
+ desc .Size ,
184
+ opts .MaxBytes ,
185
+ errdef .ErrSizeExceedsLimit )
186
+ }
187
+ bytes , err := content .ReadAll (rc , desc )
188
+ if err != nil {
189
+ return ocispec.Descriptor {}, nil , err
190
+ }
98
191
99
- return selectPlatform ( ctx , target , desc , opts . TargetPlatform )
192
+ return desc , bytes , nil
100
193
}
0 commit comments