From 0380ecb6f7434d882ef4434784422d0eda0edf22 Mon Sep 17 00:00:00 2001 From: Avi Deitcher Date: Thu, 11 Mar 2021 21:31:39 +0200 Subject: [PATCH] filestore option to ignore noname error (#239) Signed-off-by: Avi Deitcher --- pkg/content/file.go | 36 +++++++++++++++++++++++--------- pkg/content/file_test.go | 45 ++++++++++++++++++++++++++++++++++++++++ pkg/content/opts.go | 18 +++++++++++++--- 3 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 pkg/content/file_test.go diff --git a/pkg/content/file.go b/pkg/content/file.go index 77b13335c..955e5da0e 100644 --- a/pkg/content/file.go +++ b/pkg/content/file.go @@ -31,19 +31,28 @@ type FileStore struct { // Reproducible enables stripping times from added files Reproducible bool - root string - descriptor *sync.Map // map[digest.Digest]ocispec.Descriptor - pathMap *sync.Map - tmpFiles *sync.Map + root string + descriptor *sync.Map // map[digest.Digest]ocispec.Descriptor + pathMap *sync.Map + tmpFiles *sync.Map + ignoreNoName bool } // NewFileStore creats a new file store -func NewFileStore(rootPath string) *FileStore { +func NewFileStore(rootPath string, opts ...WriterOpt) *FileStore { + // we have to process the opts to find if they told us to change defaults + var wOpts WriterOpts + for _, opt := range opts { + if err := opt(&wOpts); err != nil { + continue + } + } return &FileStore{ - root: rootPath, - descriptor: &sync.Map{}, - pathMap: &sync.Map{}, - tmpFiles: &sync.Map{}, + root: rootPath, + descriptor: &sync.Map{}, + pathMap: &sync.Map{}, + tmpFiles: &sync.Map{}, + ignoreNoName: wOpts.IgnoreNoName, } } @@ -198,7 +207,14 @@ func (s *FileStore) Writer(ctx context.Context, opts ...content.WriterOpt) (cont name, ok := ResolveName(desc) if !ok { - return nil, ErrNoName + // if we were not told to ignore NoName, then return an error + if !s.ignoreNoName { + return nil, ErrNoName + } + + // just return a nil writer - we do not want to calculate the hash, so just use + // whatever was passed in the descriptor + return NewIoContentWriter(ioutil.Discard, WithOutputHash(desc.Digest)), nil } path, err := s.resolveWritePath(name) if err != nil { diff --git a/pkg/content/file_test.go b/pkg/content/file_test.go new file mode 100644 index 000000000..a8e4bc306 --- /dev/null +++ b/pkg/content/file_test.go @@ -0,0 +1,45 @@ +package content_test + +import ( + "context" + "io/ioutil" + "os" + "testing" + + ctrcontent "github.com/containerd/containerd/content" + "github.com/deislabs/oras/pkg/content" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func TestFileStoreNoName(t *testing.T) { + testContent := []byte("Hello World!") + descriptor := ocispec.Descriptor{ + MediaType: ocispec.MediaTypeImageConfig, + Digest: digest.FromBytes(testContent), + Size: int64(len(testContent)), + // do NOT add the AnnotationTitle here; it is the essence of the test + } + + tests := []struct { + opts []content.WriterOpt + err error + }{ + {nil, content.ErrNoName}, + {[]content.WriterOpt{content.WithIgnoreNoName()}, nil}, + } + for _, tt := range tests { + rootPath, err := ioutil.TempDir("", "oras_filestore_test") + if err != nil { + t.Fatalf("error creating tempdir: %v", err) + } + defer os.RemoveAll(rootPath) + fileStore := content.NewFileStore(rootPath, tt.opts...) + ctx := context.Background() + refOpt := ctrcontent.WithDescriptor(descriptor) + if _, err := fileStore.Writer(ctx, refOpt); err != tt.err { + t.Errorf("mismatched error, actual '%v', expected '%v'", err, tt.err) + } + + } +} diff --git a/pkg/content/opts.go b/pkg/content/opts.go index 56d496959..b7ab86013 100644 --- a/pkg/content/opts.go +++ b/pkg/content/opts.go @@ -11,15 +11,17 @@ type WriterOpts struct { OutputHash *digest.Digest Blocksize int MultiWriterIngester bool + IgnoreNoName bool } type WriterOpt func(*WriterOpts) error func DefaultWriterOpts() WriterOpts { return WriterOpts{ - InputHash: nil, - OutputHash: nil, - Blocksize: DefaultBlocksize, + InputHash: nil, + OutputHash: nil, + Blocksize: DefaultBlocksize, + IgnoreNoName: false, } } @@ -71,3 +73,13 @@ func WithMultiWriterIngester() WriterOpt { return nil } } + +// WithIgnoreNoName some ingesters, when creating a Writer, return an error if +// the descriptor does not have a valid name on the descriptor. Passing WithIgnoreNoName +// tells the writer not to return an error, but rather to pass the data to a nil writer. +func WithIgnoreNoName() WriterOpt { + return func(w *WriterOpts) error { + w.IgnoreNoName = true + return nil + } +}