Skip to content

Commit b74100d

Browse files
added Resolve
Signed-off-by: Xiaoxuan Wang <[email protected]>
1 parent f8166fc commit b74100d

File tree

2 files changed

+95
-48
lines changed

2 files changed

+95
-48
lines changed

content/oci/deletableOci.go

+78-47
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package oci
2020
import (
2121
"context"
2222
"encoding/json"
23+
"errors"
2324
"fmt"
2425
"io"
2526
"os"
@@ -30,6 +31,7 @@ import (
3031
specs "github.com/opencontainers/image-spec/specs-go"
3132
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3233
"oras.land/oras-go/v2/content"
34+
"oras.land/oras-go/v2/errdef"
3335
"oras.land/oras-go/v2/internal/container/set"
3436
"oras.land/oras-go/v2/internal/descriptor"
3537
"oras.land/oras-go/v2/internal/graph"
@@ -98,86 +100,115 @@ func NewDeletableStoreWithContext(ctx context.Context, root string) (*DeletableS
98100
}
99101

100102
// 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)
105107
}
106108

107109
// 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 {
112114
return err
113115
}
114-
if err := s.graph.Index(ctx, s.storage, expected); err != nil {
116+
if err := ds.graph.Index(ctx, ds.storage, expected); err != nil {
115117
return err
116118
}
117119
if descriptor.IsManifest(expected) {
118120
// tag by digest
119-
return s.tag(ctx, expected, expected.Digest.String())
121+
return ds.tag(ctx, expected, expected.Digest.String())
120122
}
121123
return nil
122124
}
123125

124126
// 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()
129131
for reference, desc := range resolvers {
130132
if content.Equal(desc, target) {
131-
s.tagResolver.Delete(reference)
133+
ds.tagResolver.Delete(reference)
132134
}
133135
}
134-
if err := s.graph.RemoveFromIndex(ctx, target); err != nil {
136+
if err := ds.graph.RemoveFromIndex(ctx, target); err != nil {
135137
return err
136138
}
137-
if s.AutoSaveIndex {
138-
err := s.SaveIndex()
139+
if ds.AutoSaveIndex {
140+
err := ds.SaveIndex()
139141
if err != nil {
140142
return err
141143
}
142144
}
143-
return s.storage.Delete(ctx, target)
145+
return ds.storage.Delete(ctx, target)
144146
}
145147

146148
// 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)
151153
}
152154

153155
// 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 {
155157
dgst := desc.Digest.String()
156158
if reference != dgst {
157159
// 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 {
159161
return err
160162
}
161163
}
162-
if err := s.tagResolver.Tag(ctx, desc, reference); err != nil {
164+
if err := ds.tagResolver.Tag(ctx, desc, reference); err != nil {
163165
return err
164166
}
165-
if s.AutoSaveIndex {
166-
return s.SaveIndex()
167+
if ds.AutoSaveIndex {
168+
return ds.SaveIndex()
167169
}
168170
return nil
169171
}
170172

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+
171202
// Predecessors returns the nodes directly pointing to the current node.
172203
// Predecessors returns nil without error if the node does not exists in the
173204
// 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)
176207
}
177208

178209
// 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)
181212
layoutFile, err := os.Open(layoutFilePath)
182213
if err != nil {
183214
if !os.IsNotExist(err) {
@@ -205,44 +236,44 @@ func (s *DeletableStore) ensureOCILayoutFile() error {
205236

206237
// loadIndexFile reads index.json from the file system.
207238
// 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)
210241
if err != nil {
211242
if !os.IsNotExist(err) {
212243
return fmt.Errorf("failed to open index file: %w", err)
213244
}
214245

215246
// write index.json if it does not exist
216-
s.index = &ocispec.Index{
247+
ds.index = &ocispec.Index{
217248
Versioned: specs.Versioned{
218249
SchemaVersion: 2, // historical value
219250
},
220251
Manifests: []ocispec.Descriptor{},
221252
}
222-
return s.writeIndexFile()
253+
return ds.writeIndexFile()
223254
}
224255
defer indexFile.Close()
225256

226257
var index ocispec.Index
227258
if err := json.NewDecoder(indexFile).Decode(&index); err != nil {
228259
return fmt.Errorf("failed to decode index file: %w", err)
229260
}
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)
232263
}
233264

234265
// SaveIndex writes the `index.json` file to the file system.
235266
// - If AutoSaveIndex is set to true (default value),
236267
// the OCI store will automatically call this method on each Tag() call.
237268
// - If AutoSaveIndex is set to false, it's the caller's responsibility
238269
// 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()
242273

243274
var manifests []ocispec.Descriptor
244275
tagged := set.New[digest.Digest]()
245-
refMap := s.tagResolver.Map()
276+
refMap := ds.tagResolver.Map()
246277

247278
// 1. Add descriptors that are associated with tags
248279
// Note: One descriptor can be associated with multiple tags.
@@ -267,15 +298,15 @@ func (s *DeletableStore) SaveIndex() error {
267298
}
268299
}
269300

270-
s.index.Manifests = manifests
271-
return s.writeIndexFile()
301+
ds.index.Manifests = manifests
302+
return ds.writeIndexFile()
272303
}
273304

274305
// 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)
277308
if err != nil {
278309
return fmt.Errorf("failed to marshal index file: %w", err)
279310
}
280-
return os.WriteFile(s.indexPath, indexJSON, 0666)
311+
return os.WriteFile(ds.indexPath, indexJSON, 0666)
281312
}

content/oci/deletableOci_test.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,21 @@ package oci
2020
import (
2121
"bytes"
2222
"context"
23+
"reflect"
2324
"testing"
2425

2526
"github.com/opencontainers/go-digest"
2627
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2728
)
2829

29-
func TestDeletableStore_Delete(t *testing.T) {
30+
func TestDeletableStore(t *testing.T) {
3031
content := []byte("test delete")
3132
desc := ocispec.Descriptor{
3233
MediaType: "test-delete",
3334
Digest: digest.FromBytes(content),
3435
Size: int64(len(content)),
3536
}
37+
ref := "latest"
3638

3739
tempDir := t.TempDir()
3840
s, err := NewDeletableStore(tempDir)
@@ -54,6 +56,20 @@ func TestDeletableStore_Delete(t *testing.T) {
5456
t.Errorf("Store.Exists() = %v, want %v", exists, true)
5557
}
5658

59+
err = s.tag(ctx, desc, ref)
60+
if err != nil {
61+
t.Errorf("error tagging descriptor error = %v, wantErr %v", err, false)
62+
}
63+
64+
resolvedDescr, err := s.Resolve(ctx, ref)
65+
if err != nil {
66+
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, false)
67+
}
68+
69+
if !reflect.DeepEqual(resolvedDescr, desc) {
70+
t.Errorf("Store.Resolve() = %v, want %v", resolvedDescr, desc)
71+
}
72+
5773
err = s.Delete(ctx, desc)
5874
if err != nil {
5975
t.Errorf("Store.Delete() = %v, wantErr %v", err, true)

0 commit comments

Comments
 (0)