Skip to content

Commit

Permalink
Allow skipping unnamed blobs when pushing to file store
Browse files Browse the repository at this point in the history
Signed-off-by: Wei Meng <[email protected]>
  • Loading branch information
m5i-work committed Jul 27, 2022
1 parent eb13fdf commit eef7f68
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 4 deletions.
1 change: 1 addition & 0 deletions content/file/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import "errors"
var (
ErrMissingName = errors.New("missing name")
ErrDuplicateName = errors.New("duplicate name")
ErrSkipUnnamed = errors.New("unnamed descriptor skipped")
ErrPathTraversalDisallowed = errors.New("path traversal disallowed")
ErrOverwriteDisallowed = errors.New("overwrite disallowed")
ErrStoreClosed = errors.New("store already closed")
Expand Down
21 changes: 17 additions & 4 deletions content/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ type Store struct {
// When specified, saving files to existing paths will be disabled.
// Default value: false.
DisableOverwrite 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.
// 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 @@ -191,26 +196,34 @@ 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 either
// discarded (Store.IgnoreNoName is true) or pushed to the fallback storage
// (Store.IgnoreNoName is false).
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
}

return s.graph.Index(ctx, s, expected)
}

// 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 either
// discarded (Store.IgnoreNoName is true) or pushed to the fallback storage
// (Store.IgnoreNoName is false).
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
55 changes: 55 additions & 0 deletions content/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,61 @@ 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()
path := filepath.Join(tempDir, "manifest.json")
if err := ioutil.WriteFile(path, manifestJSON, 0444); err != nil {
t.Fatal("error calling WriteFile(), error =", err)
}
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 eef7f68

Please sign in to comment.