Skip to content

Commit 5073458

Browse files
authored
feat: add Untag() to *oci.Store (#647)
Signed-off-by: Francis Laniel <flaniel@linux.microsoft.com>
1 parent 98758b7 commit 5073458

File tree

3 files changed

+175
-5
lines changed

3 files changed

+175
-5
lines changed

content/oci/oci.go

+23
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,29 @@ func (s *Store) Resolve(ctx context.Context, reference string) (ocispec.Descript
238238
return desc, nil
239239
}
240240

241+
func (s *Store) Untag(ctx context.Context, reference string) error {
242+
if reference == "" {
243+
return errdef.ErrMissingReference
244+
}
245+
246+
s.sync.RLock()
247+
defer s.sync.RUnlock()
248+
249+
desc, err := s.tagResolver.Resolve(ctx, reference)
250+
if err != nil {
251+
return fmt.Errorf("resolving reference %q: %w", reference, err)
252+
}
253+
if reference == desc.Digest.String() {
254+
return fmt.Errorf("reference %q is a digest and not a tag: %w", reference, errdef.ErrInvalidReference)
255+
}
256+
257+
s.tagResolver.Untag(reference)
258+
if s.AutoSaveIndex {
259+
return s.saveIndex()
260+
}
261+
return nil
262+
}
263+
241264
// Predecessors returns the nodes directly pointing to the current node.
242265
// Predecessors returns nil without error if the node does not exists in the
243266
// store.

content/oci/oci_test.go

+146-5
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,8 @@ func TestStore_DisableAutoSaveIndex(t *testing.T) {
575575
Digest: digest.FromBytes(content),
576576
Size: int64(len(content)),
577577
}
578-
ref := "foobar"
578+
ref0 := "foobar"
579+
ref1 := "barfoo"
579580

580581
tempDir := t.TempDir()
581582
s, err := New(tempDir)
@@ -623,16 +624,20 @@ func TestStore_DisableAutoSaveIndex(t *testing.T) {
623624
}
624625

625626
// test tag
626-
err = s.Tag(ctx, desc, ref)
627+
err = s.Tag(ctx, desc, ref0)
627628
if err != nil {
628629
t.Fatal("Store.Tag() error =", err)
629630
}
630-
if got, want := len(internalResolver.Map()), 2; got != want {
631+
err = s.Tag(ctx, desc, ref1)
632+
if err != nil {
633+
t.Fatal("Store.Tag() error =", err)
634+
}
635+
if got, want := len(internalResolver.Map()), 3; got != want {
631636
t.Errorf("resolver.Map() = %v, want %v", got, want)
632637
}
633638

634639
// test resolving by digest
635-
gotDesc, err = s.Resolve(ctx, ref)
640+
gotDesc, err = s.Resolve(ctx, ref0)
636641
if err != nil {
637642
t.Fatal("Store.Resolve() error =", err)
638643
}
@@ -648,12 +653,31 @@ func TestStore_DisableAutoSaveIndex(t *testing.T) {
648653
t.Fatal("Store.SaveIndex() error =", err)
649654
}
650655
// test index file again
651-
if got, want := len(s.index.Manifests), 1; got != want {
656+
if got, want := len(s.index.Manifests), 2; got != want {
652657
t.Errorf("len(index.Manifests) = %v, want %v", got, want)
653658
}
654659
if _, err := os.Stat(s.indexPath); err != nil {
655660
t.Errorf("error: %s does not exist", s.indexPath)
656661
}
662+
663+
// test untag
664+
err = s.Untag(ctx, ref0)
665+
if err != nil {
666+
t.Fatal("Store.Untag() error =", err)
667+
}
668+
if got, want := len(internalResolver.Map()), 2; got != want {
669+
t.Errorf("resolver.Map() = %v, want %v", got, want)
670+
}
671+
if got, want := len(s.index.Manifests), 2; got != want {
672+
t.Errorf("len(index.Manifests) = %v, want %v", got, want)
673+
}
674+
if err := s.saveIndex(); err != nil {
675+
t.Fatal("Store.SaveIndex() error =", err)
676+
}
677+
// test index file again
678+
if got, want := len(s.index.Manifests), 1; got != want {
679+
t.Errorf("len(index.Manifests) = %v, want %v", got, want)
680+
}
657681
}
658682

659683
func TestStore_RepeatTag(t *testing.T) {
@@ -2237,6 +2261,123 @@ func TestStore_PredecessorsAndDelete(t *testing.T) {
22372261
}
22382262
}
22392263

2264+
func TestStore_Untag(t *testing.T) {
2265+
content := []byte("test delete")
2266+
desc := ocispec.Descriptor{
2267+
MediaType: "test-delete",
2268+
Digest: digest.FromBytes(content),
2269+
Size: int64(len(content)),
2270+
}
2271+
ref := "latest"
2272+
2273+
tempDir := t.TempDir()
2274+
s, err := New(tempDir)
2275+
if err != nil {
2276+
t.Fatal("NewDeletableStore() error =", err)
2277+
}
2278+
ctx := context.Background()
2279+
2280+
err = s.Push(ctx, desc, bytes.NewReader(content))
2281+
if err != nil {
2282+
t.Errorf("Store.Push() error = %v, wantErr %v", err, false)
2283+
}
2284+
2285+
err = s.Tag(ctx, desc, ref)
2286+
if err != nil {
2287+
t.Errorf("error tagging descriptor error = %v, wantErr %v", err, false)
2288+
}
2289+
2290+
exists, err := s.Exists(ctx, desc)
2291+
if err != nil {
2292+
t.Fatal("Store.Exists() error =", err)
2293+
}
2294+
if !exists {
2295+
t.Errorf("Store.Exists() = %v, want %v", exists, true)
2296+
}
2297+
2298+
resolvedDescr, err := s.Resolve(ctx, ref)
2299+
if err != nil {
2300+
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, false)
2301+
}
2302+
2303+
if !reflect.DeepEqual(resolvedDescr, desc) {
2304+
t.Errorf("Store.Resolve() = %v, want %v", resolvedDescr, desc)
2305+
}
2306+
2307+
err = s.Untag(ctx, ref)
2308+
if err != nil {
2309+
t.Errorf("Store.Untag() = %v, wantErr %v", err, nil)
2310+
}
2311+
2312+
_, err = s.Resolve(ctx, ref)
2313+
if !errors.Is(err, errdef.ErrNotFound) {
2314+
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, errdef.ErrNotFound)
2315+
}
2316+
2317+
exists, err = s.Exists(ctx, desc)
2318+
if err != nil {
2319+
t.Fatal("Store.Exists() error =", err)
2320+
}
2321+
if !exists {
2322+
t.Errorf("Store.Exists() = %v, want %v", exists, true)
2323+
}
2324+
}
2325+
2326+
func TestStore_UntagErrorPath(t *testing.T) {
2327+
content := []byte("test delete")
2328+
desc := ocispec.Descriptor{
2329+
MediaType: "test-delete",
2330+
Digest: digest.FromBytes(content),
2331+
Size: int64(len(content)),
2332+
}
2333+
ref := "latest"
2334+
2335+
tempDir := t.TempDir()
2336+
s, err := New(tempDir)
2337+
if err != nil {
2338+
t.Fatal("NewDeletableStore() error =", err)
2339+
}
2340+
ctx := context.Background()
2341+
2342+
err = s.Untag(ctx, "")
2343+
if !errors.Is(err, errdef.ErrMissingReference) {
2344+
t.Errorf("Store.Untag() error = %v, wantErr %v", err, errdef.ErrMissingReference)
2345+
}
2346+
2347+
err = s.Untag(ctx, "foobar")
2348+
if !errors.Is(err, errdef.ErrNotFound) {
2349+
t.Errorf("Store.Untag() error = %v, wantErr %v", err, errdef.ErrNotFound)
2350+
}
2351+
2352+
err = s.Push(ctx, desc, bytes.NewReader(content))
2353+
if err != nil {
2354+
t.Errorf("Store.Push() error = %v, wantErr %v", err, false)
2355+
}
2356+
2357+
err = s.Tag(ctx, desc, ref)
2358+
if err != nil {
2359+
t.Errorf("error tagging descriptor error = %v, wantErr %v", err, false)
2360+
}
2361+
2362+
exists, err := s.Exists(ctx, desc)
2363+
if err != nil {
2364+
t.Fatal("Store.Exists() error =", err)
2365+
}
2366+
if !exists {
2367+
t.Errorf("Store.Exists() = %v, want %v", exists, true)
2368+
}
2369+
2370+
resolvedDescr, err := s.Resolve(ctx, ref)
2371+
if err != nil {
2372+
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, false)
2373+
}
2374+
2375+
err = s.Untag(ctx, resolvedDescr.Digest.String())
2376+
if !errors.Is(err, errdef.ErrInvalidReference) {
2377+
t.Errorf("Store.Untag() error = %v, wantErr %v", err, errdef.ErrInvalidReference)
2378+
}
2379+
}
2380+
22402381
func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descriptor) bool {
22412382
if len(actual) != len(expected) {
22422383
return false

content/resolver.go

+6
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,9 @@ type TagResolver interface {
3939
Tagger
4040
Resolver
4141
}
42+
43+
// Untagger untags reference tags.
44+
type Untagger interface {
45+
// Untag untags the given reference string.
46+
Untag(ctx context.Context, reference string) error
47+
}

0 commit comments

Comments
 (0)