Skip to content

Commit

Permalink
Add Delete to storages
Browse files Browse the repository at this point in the history
Signed-off-by: carabasdaniel <dani@aserto.com>
  • Loading branch information
carabasdaniel committed Mar 23, 2023
1 parent e8225cb commit 008a185
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 0 deletions.
23 changes: 23 additions & 0 deletions content/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,29 @@ func (s *Store) Resolve(ctx context.Context, ref string) (ocispec.Descriptor, er
return s.resolver.Resolve(ctx, ref)
}

func (s *Store) Delete(ctx context.Context, target ocispec.Descriptor) error {
if s.isClosedSet() {
return ErrStoreClosed
}
exists, err := s.Exists(ctx, target)
if err != nil {
return err
}
if !exists {
return errdef.ErrNotFound
}
path, _ := s.digestToPath.Load(target.Digest)
pathString, ok := path.(string)
if !ok {
return errdef.ErrInvalidDigest
}
err = os.RemoveAll(s.absPath(pathString))
if err != nil {
return s.fallbackStorage.Delete(ctx, target)
}
return err
}

// Tag tags a descriptor with a reference string.
func (s *Store) Tag(ctx context.Context, desc ocispec.Descriptor, ref string) error {
if s.isClosedSet() {
Expand Down
30 changes: 30 additions & 0 deletions content/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,36 @@ func TestCopyGraph_FileToMemory_PartialCopy(t *testing.T) {
}
}

func TestStore_Delete(t *testing.T) {
tempDir := t.TempDir()
s, err := New(tempDir)
if err != nil {
t.Fatal("Store.New() error =", err)
}
defer s.Close()
ctx := context.Background()

blob := []byte("hello world")
name := "test.txt"
mediaType := "test"

path := filepath.Join(tempDir, name)
if err := os.WriteFile(path, blob, 0444); err != nil {
t.Fatal("error calling WriteFile(), error =", err)
}

// test blob add
gotDesc, err := s.Add(ctx, name, mediaType, path)
if err != nil {
t.Fatal("Store.Add() error =", err)
}

err = s.Delete(ctx, gotDesc)
if err != nil {
t.Fatal("Store.Delete() error =", err)
}
}

func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descriptor) bool {
if len(actual) != len(expected) {
return false
Expand Down
12 changes: 12 additions & 0 deletions content/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ func (s *Store) Resolve(ctx context.Context, reference string) (ocispec.Descript
return s.resolver.Resolve(ctx, reference)
}

// Delete a target descriptor for storage.
func (s *Store) Delete(ctx context.Context, target ocispec.Descriptor) error {
exists, err := s.storage.Exists(ctx, target)
if err != nil {
return err
}
if !exists {
return errdef.ErrNotFound
}
return s.storage.Delete(ctx, target)
}

// Tag tags a descriptor with a reference string.
// Returns ErrNotFound if the tagged content does not exist.
func (s *Store) Tag(ctx context.Context, desc ocispec.Descriptor, reference string) error {
Expand Down
45 changes: 45 additions & 0 deletions content/memory/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,51 @@ func TestStorePredecessors(t *testing.T) {
}
}

func TestStoreDelete(t *testing.T) {
content := []byte("hello world")
desc := ocispec.Descriptor{
MediaType: "test",
Digest: digest.FromBytes(content),
Size: int64(len(content)),
}

s := New()
ctx := context.Background()

err := s.Push(ctx, desc, bytes.NewReader(content))
if err != nil {
t.Fatal("Store.Push() error =", err)
}
ref := "foobar"
err = s.Tag(ctx, desc, ref)
if err != nil {
t.Fatal("Store.Tag() error =", err)
}

internalResolver := s.resolver.(*resolver.Memory)
if got := len(internalResolver.Map()); got != 1 {
t.Errorf("resolver.Map() = %v, want %v", got, 1)
}

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)
}

err = s.Delete(ctx, desc)
if err != nil {
t.Fatal("Store.Delete() error =", err)
}

internalStorage := s.storage.(*cas.Memory)
if got := len(internalStorage.Map()); got != 0 {
t.Errorf("storage.Map() = %v, want %v", got, 0)
}
}

func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descriptor) bool {
if len(actual) != len(expected) {
return false
Expand Down
16 changes: 16 additions & 0 deletions content/oci/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,22 @@ func (s *Store) Resolve(ctx context.Context, reference string) (ocispec.Descript
return desc, nil
}

func (s *Store) Delete(ctx context.Context, target ocispec.Descriptor) error {
resolvers := s.tagResolver.Map()
for reference, desc := range resolvers {
if content.Equal(desc, target) {
s.tagResolver.Remove(reference)
}
}
if s.AutoSaveIndex {
err := s.SaveIndex()
if err != nil {
return err
}
}
return s.storage.Delete(ctx, target)
}

// 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
111 changes: 111 additions & 0 deletions content/oci/oci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1993,3 +1993,114 @@ func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descript
}
return true
}

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

tempDir := t.TempDir()
s, err := New(tempDir)
if err != nil {
t.Fatal("New() 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)
}

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.Delete(ctx, resolvedDescr)
if err != nil {
t.Errorf("Store.Delete() = %v, wantErr %v", err, true)
}

_, err = s.Resolve(ctx, ref)
if !errors.Is(err, errdef.ErrNotFound) {
t.Errorf("descriptor should no longer exist in store = %v, wantErr %v", err, errdef.ErrNotFound)
}
}

func TestStore_DeleteDescriptoMultipleRefs(t *testing.T) {
content := []byte("hello world")
ref1 := "hello-world:0.0.1"
ref2 := "hello-world:0.0.2"
desc := ocispec.Descriptor{
MediaType: "test",
Digest: digest.FromBytes(content),
Size: int64(len(content)),
}

tempDir := t.TempDir()
s, err := New(tempDir)
s.AutoSaveIndex = true
if err != nil {
t.Fatal("New() 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)
}

if len(s.index.Manifests) != 0 {
t.Errorf("manifest should be empty but has %d elements", len(s.index.Manifests))
}

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

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

if len(s.index.Manifests) != 2 {
t.Errorf("manifest should have %d, but has %d", len(s.index.Manifests), 0)
}

resolvedDescr, err := s.Resolve(ctx, ref1)
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.Delete(ctx, resolvedDescr)
if err != nil {
t.Errorf("Store.Delete() = %v, wantErr %v", err, true)
}

if len(s.index.Manifests) != 0 {
t.Errorf("manifest should be empty after delete but has %d", len(s.index.Manifests))
}

_, err = s.Resolve(ctx, ref2)
if !errors.Is(err, errdef.ErrNotFound) {
t.Errorf("descriptor should no longer exist in store = %v, wantErr %v", err, errdef.ErrNotFound)
}
}
16 changes: 16 additions & 0 deletions content/oci/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,22 @@ func (s *Storage) Push(_ context.Context, expected ocispec.Descriptor, content i
return nil
}

// Delete removes the descriptor blob from the storage
func (s *Storage) Delete(ctx context.Context, target ocispec.Descriptor) error {
path, err := blobPath(target.Digest)
if err != nil {
return fmt.Errorf("%s: %s: %w", target.Digest, target.MediaType, errdef.ErrInvalidDigest)
}
fullpath := filepath.Join(s.root, path)

// check if the target content does not exists in the blob directory.
if _, err := os.Stat(fullpath); os.IsNotExist(err) {
return err
}

return os.Remove(fullpath)
}

// ingest write the content into a temporary ingest file.
func (s *Storage) ingest(expected ocispec.Descriptor, content io.Reader) (path string, ingestErr error) {
if err := ensureDir(s.ingestRoot); err != nil {
Expand Down
24 changes: 24 additions & 0 deletions content/oci/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,27 @@ func TestStorage_Fetch_Concurrent(t *testing.T) {
t.Fatal(err)
}
}

func TestStorage_Delete(t *testing.T) {
content := []byte("hello world")
desc := ocispec.Descriptor{
MediaType: "test",
Digest: digest.FromBytes(content),
Size: int64(len(content)),
}

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

if err := s.Push(ctx, desc, bytes.NewReader(content)); err != nil {
t.Fatal("Storage.Push() error =", err)
}
err = s.Delete(ctx, desc)
if err != nil {
t.Fatal("Storage.Delete() error =", err)
}
}
1 change: 1 addition & 0 deletions content/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Pusher interface {
type Storage interface {
ReadOnlyStorage
Pusher
Deleter
}

// ReadOnlyStorage represents a read-only Storage.
Expand Down
11 changes: 11 additions & 0 deletions internal/cas/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,14 @@ func (m *Memory) Map() map[descriptor.Descriptor][]byte {
})
return res
}

func (m *Memory) Delete(ctx context.Context, target ocispec.Descriptor) error {
key := descriptor.FromOCI(target)

// check if the content exists in advance to avoid reading from the content.
if _, exists := m.content.Load(key); !exists {
return fmt.Errorf("Cannot delete %s: %s: %w", key.Digest, key.MediaType, errdef.ErrNotFound)
}
m.content.Delete(key)
return nil
}
Loading

0 comments on commit 008a185

Please sign in to comment.