Skip to content

Commit

Permalink
Allow skipping unnamed blobs when pushing to file store (#254)
Browse files Browse the repository at this point in the history
Signed-off-by: Wei Meng <[email protected]>
  • Loading branch information
m5i-work authored Aug 4, 2022
1 parent fda65b9 commit e135557
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
2 changes: 2 additions & 0 deletions content/file/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ var (
ErrOverwriteDisallowed = errors.New("overwrite disallowed")
ErrStoreClosed = errors.New("store already closed")
)

var errSkipUnnamed = errors.New("unnamed descriptor skipped")
24 changes: 20 additions & 4 deletions content/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ type Store struct {
// ForceCAS can be specified to enforce CAS style dedup.
// Default value: false.
ForceCAS bool
// IgnoreNoName controls if push operations should ignore descriptors
// without a name. When specified, corresponding content will be discarded.
// Otherwise, content will be saved to a fallback storage.
// A typical scenario is pulling an arbitrary artifact masqueraded as OCI
// image to file store. This option can be specified to discard unnamed
// manifest and config file, while leaving only named layer files.
// Default value: false.
IgnoreNoName bool

workingDir string // the working directory of the file store
closed int32 // if the store is closed - 0: false, 1: true.
Expand Down Expand Up @@ -199,14 +207,18 @@ func (s *Store) Fetch(ctx context.Context, target ocispec.Descriptor) (io.ReadCl
}

// Push pushes the content, matching the expected descriptor.
// If name is not specified in the descriptor,
// the content will be pushed to the fallback storage.
// If name is not specified in the descriptor, the content will be pushed to
// the fallback storage by default, or will be discarded when
// Store.IgnoreNoName is true.
func (s *Store) Push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error {
if s.isClosedSet() {
return ErrStoreClosed
}

if err := s.push(ctx, expected, content); err != nil {
if errors.Is(err, errSkipUnnamed) {
return nil
}
return err
}

Expand All @@ -220,11 +232,15 @@ func (s *Store) Push(ctx context.Context, expected ocispec.Descriptor, content i
}

// push pushes the content, matching the expected descriptor.
// If name is not specified in the descriptor,
// the content will be pushed to the fallback storage.
// If name is not specified in the descriptor, the content will be pushed to
// the fallback storage by default, or will be discarded when
// Store.IgnoreNoName is true.
func (s *Store) push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error {
name := expected.Annotations[ocispec.AnnotationTitle]
if name == "" {
if s.IgnoreNoName {
return errSkipUnnamed
}
return s.fallbackStorage.Push(ctx, expected, content)
}

Expand Down
51 changes: 51 additions & 0 deletions content/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,57 @@ func TestStore_File_Push_DisableOverwrite(t *testing.T) {
}
}

func TestStore_File_Push_IgnoreNoName(t *testing.T) {
config := []byte("{}")
configDesc := ocispec.Descriptor{
MediaType: "config",
Digest: digest.FromBytes(config),
Size: int64(len(config)),
}
manifest := ocispec.Manifest{
MediaType: ocispec.MediaTypeImageManifest,
Config: configDesc,
Layers: []ocispec.Descriptor{},
}
manifestJSON, err := json.Marshal(manifest)
if err != nil {
t.Fatal("json.Marshal() error =", err)
}
manifestDesc := ocispec.Descriptor{
MediaType: ocispec.MediaTypeImageManifest,
Digest: digest.FromBytes(manifestJSON),
Size: int64(len(manifestJSON)),
}
tempDir := t.TempDir()
s := New(tempDir)
defer s.Close()
s.IgnoreNoName = true

// push an OCI manifest
ctx := context.Background()
err = s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON))
if err != nil {
t.Fatal("Store.Push() error = ", err)
}

// verify the manifest is not saved
exists, err := s.Exists(ctx, manifestDesc)
if err != nil {
t.Fatal("Store.Exists() error =", err)
}
if exists {
t.Errorf("Unnamed manifest is saved in file store")
}
// verify the manifest is not indexed
predecessors, err := s.Predecessors(ctx, configDesc)
if err != nil {
t.Fatal("Store.Predecessors() error = ", err)
}
if len(predecessors) != 0 {
t.Errorf("Unnamed manifest is indexed in file store")
}
}

func TestStore_File_Push_DisallowPathTraversal(t *testing.T) {
content := []byte("hello world")
name := "../test.txt"
Expand Down

0 comments on commit e135557

Please sign in to comment.