Skip to content

Commit

Permalink
feat: add Untag() to *oci.Store (#647)
Browse files Browse the repository at this point in the history
Signed-off-by: Francis Laniel <[email protected]>
  • Loading branch information
eiffel-fl authored Dec 21, 2023
1 parent 98758b7 commit 5073458
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 5 deletions.
23 changes: 23 additions & 0 deletions content/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,29 @@ func (s *Store) Resolve(ctx context.Context, reference string) (ocispec.Descript
return desc, nil
}

func (s *Store) Untag(ctx context.Context, reference string) error {
if reference == "" {
return errdef.ErrMissingReference
}

s.sync.RLock()
defer s.sync.RUnlock()

desc, err := s.tagResolver.Resolve(ctx, reference)
if err != nil {
return fmt.Errorf("resolving reference %q: %w", reference, err)
}
if reference == desc.Digest.String() {
return fmt.Errorf("reference %q is a digest and not a tag: %w", reference, errdef.ErrInvalidReference)
}

s.tagResolver.Untag(reference)
if s.AutoSaveIndex {
return s.saveIndex()
}
return nil
}

// Predecessors returns the nodes directly pointing to the current node.
// Predecessors returns nil without error if the node does not exists in the
// store.
Expand Down
151 changes: 146 additions & 5 deletions content/oci/oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,8 @@ func TestStore_DisableAutoSaveIndex(t *testing.T) {
Digest: digest.FromBytes(content),
Size: int64(len(content)),
}
ref := "foobar"
ref0 := "foobar"
ref1 := "barfoo"

tempDir := t.TempDir()
s, err := New(tempDir)
Expand Down Expand Up @@ -623,16 +624,20 @@ func TestStore_DisableAutoSaveIndex(t *testing.T) {
}

// test tag
err = s.Tag(ctx, desc, ref)
err = s.Tag(ctx, desc, ref0)
if err != nil {
t.Fatal("Store.Tag() error =", err)
}
if got, want := len(internalResolver.Map()), 2; got != want {
err = s.Tag(ctx, desc, ref1)
if err != nil {
t.Fatal("Store.Tag() error =", err)
}
if got, want := len(internalResolver.Map()), 3; got != want {
t.Errorf("resolver.Map() = %v, want %v", got, want)
}

// test resolving by digest
gotDesc, err = s.Resolve(ctx, ref)
gotDesc, err = s.Resolve(ctx, ref0)
if err != nil {
t.Fatal("Store.Resolve() error =", err)
}
Expand All @@ -648,12 +653,31 @@ func TestStore_DisableAutoSaveIndex(t *testing.T) {
t.Fatal("Store.SaveIndex() error =", err)
}
// test index file again
if got, want := len(s.index.Manifests), 1; got != want {
if got, want := len(s.index.Manifests), 2; got != want {
t.Errorf("len(index.Manifests) = %v, want %v", got, want)
}
if _, err := os.Stat(s.indexPath); err != nil {
t.Errorf("error: %s does not exist", s.indexPath)
}

// test untag
err = s.Untag(ctx, ref0)
if err != nil {
t.Fatal("Store.Untag() error =", err)
}
if got, want := len(internalResolver.Map()), 2; got != want {
t.Errorf("resolver.Map() = %v, want %v", got, want)
}
if got, want := len(s.index.Manifests), 2; got != want {
t.Errorf("len(index.Manifests) = %v, want %v", got, want)
}
if err := s.saveIndex(); err != nil {
t.Fatal("Store.SaveIndex() error =", err)
}
// test index file again
if got, want := len(s.index.Manifests), 1; got != want {
t.Errorf("len(index.Manifests) = %v, want %v", got, want)
}
}

func TestStore_RepeatTag(t *testing.T) {
Expand Down Expand Up @@ -2237,6 +2261,123 @@ func TestStore_PredecessorsAndDelete(t *testing.T) {
}
}

func TestStore_Untag(t *testing.T) {
content := []byte("test delete")
desc := ocispec.Descriptor{
MediaType: "test-delete",
Digest: digest.FromBytes(content),
Size: int64(len(content)),
}
ref := "latest"

tempDir := t.TempDir()
s, err := New(tempDir)
if err != nil {
t.Fatal("NewDeletableStore() error =", err)
}
ctx := context.Background()

err = s.Push(ctx, desc, bytes.NewReader(content))
if err != nil {
t.Errorf("Store.Push() error = %v, wantErr %v", err, false)
}

err = s.Tag(ctx, desc, ref)
if err != nil {
t.Errorf("error tagging descriptor error = %v, wantErr %v", err, false)
}

exists, err := s.Exists(ctx, desc)
if err != nil {
t.Fatal("Store.Exists() error =", err)
}
if !exists {
t.Errorf("Store.Exists() = %v, want %v", exists, true)
}

resolvedDescr, err := s.Resolve(ctx, ref)
if err != nil {
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, false)
}

if !reflect.DeepEqual(resolvedDescr, desc) {
t.Errorf("Store.Resolve() = %v, want %v", resolvedDescr, desc)
}

err = s.Untag(ctx, ref)
if err != nil {
t.Errorf("Store.Untag() = %v, wantErr %v", err, nil)
}

_, err = s.Resolve(ctx, ref)
if !errors.Is(err, errdef.ErrNotFound) {
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, errdef.ErrNotFound)
}

exists, err = s.Exists(ctx, desc)
if err != nil {
t.Fatal("Store.Exists() error =", err)
}
if !exists {
t.Errorf("Store.Exists() = %v, want %v", exists, true)
}
}

func TestStore_UntagErrorPath(t *testing.T) {
content := []byte("test delete")
desc := ocispec.Descriptor{
MediaType: "test-delete",
Digest: digest.FromBytes(content),
Size: int64(len(content)),
}
ref := "latest"

tempDir := t.TempDir()
s, err := New(tempDir)
if err != nil {
t.Fatal("NewDeletableStore() error =", err)
}
ctx := context.Background()

err = s.Untag(ctx, "")
if !errors.Is(err, errdef.ErrMissingReference) {
t.Errorf("Store.Untag() error = %v, wantErr %v", err, errdef.ErrMissingReference)
}

err = s.Untag(ctx, "foobar")
if !errors.Is(err, errdef.ErrNotFound) {
t.Errorf("Store.Untag() error = %v, wantErr %v", err, errdef.ErrNotFound)
}

err = s.Push(ctx, desc, bytes.NewReader(content))
if err != nil {
t.Errorf("Store.Push() error = %v, wantErr %v", err, false)
}

err = s.Tag(ctx, desc, ref)
if err != nil {
t.Errorf("error tagging descriptor error = %v, wantErr %v", err, false)
}

exists, err := s.Exists(ctx, desc)
if err != nil {
t.Fatal("Store.Exists() error =", err)
}
if !exists {
t.Errorf("Store.Exists() = %v, want %v", exists, true)
}

resolvedDescr, err := s.Resolve(ctx, ref)
if err != nil {
t.Errorf("error resolving descriptor error = %v, wantErr %v", err, false)
}

err = s.Untag(ctx, resolvedDescr.Digest.String())
if !errors.Is(err, errdef.ErrInvalidReference) {
t.Errorf("Store.Untag() error = %v, wantErr %v", err, errdef.ErrInvalidReference)
}
}

func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descriptor) bool {
if len(actual) != len(expected) {
return false
Expand Down
6 changes: 6 additions & 0 deletions content/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ type TagResolver interface {
Tagger
Resolver
}

// Untagger untags reference tags.
type Untagger interface {
// Untag untags the given reference string.
Untag(ctx context.Context, reference string) error
}

0 comments on commit 5073458

Please sign in to comment.