@@ -20,6 +20,7 @@ package oci
20
20
import (
21
21
"context"
22
22
"encoding/json"
23
+ "errors"
23
24
"fmt"
24
25
"io"
25
26
"os"
@@ -30,6 +31,7 @@ import (
30
31
specs "github.com/opencontainers/image-spec/specs-go"
31
32
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
32
33
"oras.land/oras-go/v2/content"
34
+ "oras.land/oras-go/v2/errdef"
33
35
"oras.land/oras-go/v2/internal/container/set"
34
36
"oras.land/oras-go/v2/internal/descriptor"
35
37
"oras.land/oras-go/v2/internal/graph"
@@ -98,86 +100,115 @@ func NewDeletableStoreWithContext(ctx context.Context, root string) (*DeletableS
98
100
}
99
101
100
102
// Fetch fetches the content identified by the descriptor.
101
- func (s * DeletableStore ) Fetch (ctx context.Context , target ocispec.Descriptor ) (io.ReadCloser , error ) {
102
- s .operationLock .RLock ()
103
- defer s .operationLock .RUnlock ()
104
- return s .storage .Fetch (ctx , target )
103
+ func (ds * DeletableStore ) Fetch (ctx context.Context , target ocispec.Descriptor ) (io.ReadCloser , error ) {
104
+ ds .operationLock .RLock ()
105
+ defer ds .operationLock .RUnlock ()
106
+ return ds .storage .Fetch (ctx , target )
105
107
}
106
108
107
109
// Push pushes the content, matching the expected descriptor.
108
- func (s * DeletableStore ) Push (ctx context.Context , expected ocispec.Descriptor , reader io.Reader ) error {
109
- s .operationLock .Lock ()
110
- defer s .operationLock .Unlock ()
111
- if err := s .storage .Push (ctx , expected , reader ); err != nil {
110
+ func (ds * DeletableStore ) Push (ctx context.Context , expected ocispec.Descriptor , reader io.Reader ) error {
111
+ ds .operationLock .Lock ()
112
+ defer ds .operationLock .Unlock ()
113
+ if err := ds .storage .Push (ctx , expected , reader ); err != nil {
112
114
return err
113
115
}
114
- if err := s .graph .Index (ctx , s .storage , expected ); err != nil {
116
+ if err := ds .graph .Index (ctx , ds .storage , expected ); err != nil {
115
117
return err
116
118
}
117
119
if descriptor .IsManifest (expected ) {
118
120
// tag by digest
119
- return s .tag (ctx , expected , expected .Digest .String ())
121
+ return ds .tag (ctx , expected , expected .Digest .String ())
120
122
}
121
123
return nil
122
124
}
123
125
124
126
// Delete removes the content matching the descriptor from the store.
125
- func (s * DeletableStore ) Delete (ctx context.Context , target ocispec.Descriptor ) error {
126
- s .operationLock .Lock ()
127
- defer s .operationLock .Unlock ()
128
- resolvers := s .tagResolver .Map ()
127
+ func (ds * DeletableStore ) Delete (ctx context.Context , target ocispec.Descriptor ) error {
128
+ ds .operationLock .Lock ()
129
+ defer ds .operationLock .Unlock ()
130
+ resolvers := ds .tagResolver .Map ()
129
131
for reference , desc := range resolvers {
130
132
if content .Equal (desc , target ) {
131
- s .tagResolver .Delete (reference )
133
+ ds .tagResolver .Delete (reference )
132
134
}
133
135
}
134
- if err := s .graph .RemoveFromIndex (ctx , target ); err != nil {
136
+ if err := ds .graph .RemoveFromIndex (ctx , target ); err != nil {
135
137
return err
136
138
}
137
- if s .AutoSaveIndex {
138
- err := s .SaveIndex ()
139
+ if ds .AutoSaveIndex {
140
+ err := ds .SaveIndex ()
139
141
if err != nil {
140
142
return err
141
143
}
142
144
}
143
- return s .storage .Delete (ctx , target )
145
+ return ds .storage .Delete (ctx , target )
144
146
}
145
147
146
148
// Exists returns true if the described content exists.
147
- func (s * DeletableStore ) Exists (ctx context.Context , target ocispec.Descriptor ) (bool , error ) {
148
- s .operationLock .RLock ()
149
- defer s .operationLock .RUnlock ()
150
- return s .storage .Exists (ctx , target )
149
+ func (ds * DeletableStore ) Exists (ctx context.Context , target ocispec.Descriptor ) (bool , error ) {
150
+ ds .operationLock .RLock ()
151
+ defer ds .operationLock .RUnlock ()
152
+ return ds .storage .Exists (ctx , target )
151
153
}
152
154
153
155
// tag tags a descriptor with a reference string.
154
- func (s * DeletableStore ) tag (ctx context.Context , desc ocispec.Descriptor , reference string ) error {
156
+ func (ds * DeletableStore ) tag (ctx context.Context , desc ocispec.Descriptor , reference string ) error {
155
157
dgst := desc .Digest .String ()
156
158
if reference != dgst {
157
159
// also tag desc by its digest
158
- if err := s .tagResolver .Tag (ctx , desc , dgst ); err != nil {
160
+ if err := ds .tagResolver .Tag (ctx , desc , dgst ); err != nil {
159
161
return err
160
162
}
161
163
}
162
- if err := s .tagResolver .Tag (ctx , desc , reference ); err != nil {
164
+ if err := ds .tagResolver .Tag (ctx , desc , reference ); err != nil {
163
165
return err
164
166
}
165
- if s .AutoSaveIndex {
166
- return s .SaveIndex ()
167
+ if ds .AutoSaveIndex {
168
+ return ds .SaveIndex ()
167
169
}
168
170
return nil
169
171
}
170
172
173
+ // Resolve resolves a reference to a descriptor. If the reference to be resolved
174
+ // is a tag, the returned descriptor will be a full descriptor declared by
175
+ // github.com/opencontainers/image-spec/specs-go/v1. If the reference is a
176
+ // digest the returned descriptor will be a plain descriptor (containing only
177
+ // the digest, media type and size).
178
+ func (ds * DeletableStore ) Resolve (ctx context.Context , reference string ) (ocispec.Descriptor , error ) {
179
+ ds .operationLock .RLock ()
180
+ defer ds .operationLock .RUnlock ()
181
+ if reference == "" {
182
+ return ocispec.Descriptor {}, errdef .ErrMissingReference
183
+ }
184
+
185
+ // attempt resolving manifest
186
+ desc , err := ds .tagResolver .Resolve (ctx , reference )
187
+ if err != nil {
188
+ if errors .Is (err , errdef .ErrNotFound ) {
189
+ // attempt resolving blob
190
+ return resolveBlob (os .DirFS (ds .root ), reference )
191
+ }
192
+ return ocispec.Descriptor {}, err
193
+ }
194
+
195
+ if reference == desc .Digest .String () {
196
+ return descriptor .Plain (desc ), nil
197
+ }
198
+
199
+ return desc , nil
200
+ }
201
+
171
202
// Predecessors returns the nodes directly pointing to the current node.
172
203
// Predecessors returns nil without error if the node does not exists in the
173
204
// store.
174
- func (s * DeletableStore ) Predecessors (ctx context.Context , node ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
175
- return s .graph .Predecessors (ctx , node )
205
+ func (ds * DeletableStore ) Predecessors (ctx context.Context , node ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
206
+ return ds .graph .Predecessors (ctx , node )
176
207
}
177
208
178
209
// ensureOCILayoutFile ensures the `oci-layout` file.
179
- func (s * DeletableStore ) ensureOCILayoutFile () error {
180
- layoutFilePath := filepath .Join (s .root , ocispec .ImageLayoutFile )
210
+ func (ds * DeletableStore ) ensureOCILayoutFile () error {
211
+ layoutFilePath := filepath .Join (ds .root , ocispec .ImageLayoutFile )
181
212
layoutFile , err := os .Open (layoutFilePath )
182
213
if err != nil {
183
214
if ! os .IsNotExist (err ) {
@@ -205,44 +236,44 @@ func (s *DeletableStore) ensureOCILayoutFile() error {
205
236
206
237
// loadIndexFile reads index.json from the file system.
207
238
// Create index.json if it does not exist.
208
- func (s * DeletableStore ) loadIndexFile (ctx context.Context ) error {
209
- indexFile , err := os .Open (s .indexPath )
239
+ func (ds * DeletableStore ) loadIndexFile (ctx context.Context ) error {
240
+ indexFile , err := os .Open (ds .indexPath )
210
241
if err != nil {
211
242
if ! os .IsNotExist (err ) {
212
243
return fmt .Errorf ("failed to open index file: %w" , err )
213
244
}
214
245
215
246
// write index.json if it does not exist
216
- s .index = & ocispec.Index {
247
+ ds .index = & ocispec.Index {
217
248
Versioned : specs.Versioned {
218
249
SchemaVersion : 2 , // historical value
219
250
},
220
251
Manifests : []ocispec.Descriptor {},
221
252
}
222
- return s .writeIndexFile ()
253
+ return ds .writeIndexFile ()
223
254
}
224
255
defer indexFile .Close ()
225
256
226
257
var index ocispec.Index
227
258
if err := json .NewDecoder (indexFile ).Decode (& index ); err != nil {
228
259
return fmt .Errorf ("failed to decode index file: %w" , err )
229
260
}
230
- s .index = & index
231
- return loadIndex (ctx , s .index , s .storage , s .tagResolver , s .graph )
261
+ ds .index = & index
262
+ return loadIndex (ctx , ds .index , ds .storage , ds .tagResolver , ds .graph )
232
263
}
233
264
234
265
// SaveIndex writes the `index.json` file to the file system.
235
266
// - If AutoSaveIndex is set to true (default value),
236
267
// the OCI store will automatically call this method on each Tag() call.
237
268
// - If AutoSaveIndex is set to false, it's the caller's responsibility
238
269
// to manually call this method when needed.
239
- func (s * DeletableStore ) SaveIndex () error {
240
- s .indexLock .Lock ()
241
- defer s .indexLock .Unlock ()
270
+ func (ds * DeletableStore ) SaveIndex () error {
271
+ ds .indexLock .Lock ()
272
+ defer ds .indexLock .Unlock ()
242
273
243
274
var manifests []ocispec.Descriptor
244
275
tagged := set .New [digest.Digest ]()
245
- refMap := s .tagResolver .Map ()
276
+ refMap := ds .tagResolver .Map ()
246
277
247
278
// 1. Add descriptors that are associated with tags
248
279
// Note: One descriptor can be associated with multiple tags.
@@ -267,15 +298,15 @@ func (s *DeletableStore) SaveIndex() error {
267
298
}
268
299
}
269
300
270
- s .index .Manifests = manifests
271
- return s .writeIndexFile ()
301
+ ds .index .Manifests = manifests
302
+ return ds .writeIndexFile ()
272
303
}
273
304
274
305
// writeIndexFile writes the `index.json` file.
275
- func (s * DeletableStore ) writeIndexFile () error {
276
- indexJSON , err := json .Marshal (s .index )
306
+ func (ds * DeletableStore ) writeIndexFile () error {
307
+ indexJSON , err := json .Marshal (ds .index )
277
308
if err != nil {
278
309
return fmt .Errorf ("failed to marshal index file: %w" , err )
279
310
}
280
- return os .WriteFile (s .indexPath , indexJSON , 0666 )
311
+ return os .WriteFile (ds .indexPath , indexJSON , 0666 )
281
312
}
0 commit comments