From 9f733c15f8f04e7fae53656f990e57bd917835cd Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 12 Oct 2022 19:08:54 +0200 Subject: [PATCH] prepare dynamic format recognition for object set serialization filesystem formats (#97) * prepare dynamic format recognition for object set serialization filesystem formats Switch from a static set of data for configure the filesystem format to a potentially dynamic format specification supporting format discovery based on the actual state of the filesystem. The static structure describing fixed values for various features is replaced by an interface with an additional discover method. * fix lint and introduce OCI format handling * make format configurable * format read/write tests * review * rebase and format --- cmds/helminstaller/testhelper/env.go | 4 +- .../ocicmds/artefacts/download/cmd_test.go | 6 +- .../ocmcmds/components/sign/cmd_test.go | 47 ++++- .../ocmcmds/components/transfer/cmd_test.go | 36 +--- .../ocmcmds/components/verify/cmd_test.go | 29 +-- .../commands/ocmcmds/ctf/transfer/cmd_test.go | 30 +-- cmds/ocm/testhelper/env.go | 4 +- pkg/common/accessio/format.go | 5 +- pkg/common/accessio/opts.go | 183 +++++++++++++----- pkg/common/accessobj/accessobject.go | 86 ++++++-- pkg/common/accessobj/format-directory.go | 67 ++++--- pkg/common/accessobj/format-tar.go | 69 +++++-- pkg/common/accessobj/format.go | 34 ++-- pkg/common/accessobj/utils.go | 27 +-- pkg/contexts/clictx/core/context.go | 12 +- .../repositories/artefactset/artefactset.go | 15 +- .../artefactset/artefactset_test.go | 63 ++++-- .../oci/repositories/artefactset/format.go | 137 +++++++++++-- .../oci/repositories/artefactset/options.go | 90 +++++++++ .../oci/repositories/artefactset/repo_test.go | 3 +- .../repositories/artefactset/repository.go | 2 +- .../artefactset/testhelper/formats.go | 37 ++++ .../oci/repositories/artefactset/type.go | 31 ++- .../oci/repositories/artefactset/uniform.go | 15 +- pkg/contexts/oci/repositories/ctf/ctf_test.go | 7 +- pkg/contexts/oci/repositories/ctf/format.go | 41 ++-- .../oci/repositories/ctf/repository.go | 6 +- .../oci/repositories/ctf/synthesis_test.go | 23 ++- pkg/contexts/oci/repositories/ctf/type.go | 20 +- pkg/contexts/oci/repositories/ctf/uniform.go | 2 +- pkg/contexts/oci/testhelper/manifests.go | 83 ++++++++ pkg/contexts/oci/testhelper/oci.go | 30 +++ .../accessmethods/ociartefact/method_test.go | 19 +- .../ocm/accessmethods/ociblob/method_test.go | 19 +- .../generic/ocirepo/upload_test.go | 5 +- .../digester/digesters/artefact/digester.go | 31 ++- .../repositories/comparch/componentarchive.go | 6 +- .../ocm/repositories/comparch/format.go | 30 ++- .../ocm/repositories/comparch/repository.go | 4 +- .../ocm/repositories/comparch/type.go | 14 +- .../ocm/repositories/comparch/uniform.go | 2 +- pkg/contexts/ocm/repositories/ctf/type.go | 9 +- .../repositories/genericocireg/repo_test.go | 7 +- pkg/contexts/ocm/signing/signing_test.go | 29 +-- .../transferhandler/spiff/handler_test.go | 42 ++-- .../transferhandler/standard/handler_test.go | 23 +-- pkg/env/env.go | 5 +- 47 files changed, 1032 insertions(+), 457 deletions(-) create mode 100644 pkg/contexts/oci/repositories/artefactset/options.go create mode 100644 pkg/contexts/oci/repositories/artefactset/testhelper/formats.go create mode 100644 pkg/contexts/oci/testhelper/manifests.go create mode 100644 pkg/contexts/oci/testhelper/oci.go diff --git a/cmds/helminstaller/testhelper/env.go b/cmds/helminstaller/testhelper/env.go index a9f81b84a..3104f7585 100644 --- a/cmds/helminstaller/testhelper/env.go +++ b/cmds/helminstaller/testhelper/env.go @@ -64,8 +64,8 @@ func NewTestEnv(opts ...env.Option) *TestEnv { } } -func (e *TestEnv) ApplyOption(opts *accessio.Options) { - e.Builder.ApplyOption(opts) +func (e *TestEnv) ApplyOption(opts accessio.Options) error { + return e.Builder.ApplyOption(opts) } func (e *TestEnv) ConfigContext() config.Context { diff --git a/cmds/ocm/commands/ocicmds/artefacts/download/cmd_test.go b/cmds/ocm/commands/ocicmds/artefacts/download/cmd_test.go index cfcce2e44..c4453585f 100644 --- a/cmds/ocm/commands/ocicmds/artefacts/download/cmd_test.go +++ b/cmds/ocm/commands/ocicmds/artefacts/download/cmd_test.go @@ -65,6 +65,10 @@ var _ = Describe("Test Environment", func() { /tmp/res: downloaded `)) Expect(env.DirExists(OUT)).To(BeTrue()) - Expect(env.ReadFile(OUT + "/" + artefactset.ArtefactSetDescriptorFileName)).To(Equal([]byte("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:2c3e2c59e0ac9c99864bf0a9f9727c09f21a66080f9f9b03b36a2dad3cce6ff9\",\"size\":342,\"annotations\":{\"cloud.gardener.ocm/tags\":\"v1\"}}],\"annotations\":{\"cloud.gardener.ocm/main\":\"sha256:2c3e2c59e0ac9c99864bf0a9f9727c09f21a66080f9f9b03b36a2dad3cce6ff9\"}}"))) + tags := "" + if artefactset.IsOCIDefaultFormat() { + tags = ",\"org.opencontainers.image.ref.name\":\"v1\"" + } + Expect(env.ReadFile(OUT + "/" + artefactset.DefaultArtefactSetDescriptorFileName)).To(Equal([]byte("{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:2c3e2c59e0ac9c99864bf0a9f9727c09f21a66080f9f9b03b36a2dad3cce6ff9\",\"size\":342,\"annotations\":{\"cloud.gardener.ocm/tags\":\"v1\"" + tags + "}}],\"annotations\":{\"cloud.gardener.ocm/main\":\"sha256:2c3e2c59e0ac9c99864bf0a9f9727c09f21a66080f9f9b03b36a2dad3cce6ff9\"}}"))) }) }) diff --git a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go index 591797b56..cfa937126 100644 --- a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go @@ -21,6 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/open-component-model/ocm/cmds/ocm/testhelper" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/mandelsoft/vfs/pkg/vfs" @@ -46,9 +47,6 @@ const COMPONENTA = "github.com/mandelsoft/test" const COMPONENTB = "github.com/mandelsoft/ref" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/value" -const OCINAMESPACE2 = "ocm/ref" -const OCIVERSION = "v2.0" const OCIHOST = "alias" const SIGNATURE = "test" @@ -78,6 +76,45 @@ var _ = Describe("access method", func() { }) Context("valid", func() { + BeforeEach(func() { + FakeOCIRepo(env.Builder, OCIPATH, OCIHOST) + + env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { + OCIManifest1(env.Builder) + OCIManifest2(env.Builder) + }) + + env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { + env.Component(COMPONENTA, func() { + env.Version(VERSION, func() { + env.Provider(PROVIDER) + env.Resource("testdata", "", "PlainText", metav1.LocalRelation, func() { + env.BlobStringData(mime.MIME_TEXT, "testdata") + }) + env.Resource("value", "", resourcetypes.OCI_IMAGE, metav1.LocalRelation, func() { + env.Access( + ociartefact.New(oci.StandardOCIRef(OCIHOST+".alias", OCINAMESPACE, OCIVERSION)), + ) + env.Label("transportByValue", true) + }) + env.Resource("ref", "", resourcetypes.OCI_IMAGE, metav1.LocalRelation, func() { + env.Access( + ociartefact.New(oci.StandardOCIRef(OCIHOST+".alias", OCINAMESPACE2, OCIVERSION)), + ) + }) + }) + }) + env.Component(COMPONENTB, func() { + env.Version(VERSION, func() { + env.Provider(PROVIDER) + env.Resource("otherdata", "", "PlainText", metav1.LocalRelation, func() { + env.BlobStringData(mime.MIME_TEXT, "otherdata") + }) + env.Reference("ref", COMPONENTA, VERSION) + }) + }) + }) + }) It("sign component archive", func() { prepareEnv(env, ARCH, ARCH) @@ -203,7 +240,9 @@ Error: {signing: failed resolving component reference ref[github.com/mandelsoft/ }) func prepareEnv(env *TestEnv, componentAArchive, componentBArchive string) { - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + spec, err := ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem())) + Expect(err).To(Succeed()) + env.OCIContext().SetAlias(OCIHOST, spec) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { env.Namespace(OCINAMESPACE, func() { diff --git a/cmds/ocm/commands/ocmcmds/components/transfer/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/transfer/cmd_test.go index 3dc63453e..5066066a6 100644 --- a/cmds/ocm/commands/ocmcmds/components/transfer/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/transfer/cmd_test.go @@ -21,6 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/open-component-model/ocm/cmds/ocm/testhelper" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/open-component-model/ocm/pkg/common/accessio" @@ -28,7 +29,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/oci" "github.com/open-component-model/ocm/pkg/contexts/oci/artdesc" "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" @@ -45,9 +45,6 @@ const COMPONENT = "github.com/mandelsoft/test" const COMPONENT2 = "github.com/mandelsoft/test2" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/value" -const OCINAMESPACE2 = "ocm/ref" -const OCIVERSION = "v2.0" const OCIHOST = "alias" func Check(env *TestEnv, ldesc *artdesc.Descriptor, out string) { @@ -68,11 +65,13 @@ func CheckComponent(env *TestEnv, ldesc *artdesc.Descriptor, tgt ocm.Repository) data, err := json.Marshal(comp.GetDescriptor().Resources[2].Access) Expect(err).To(Succeed()) - Expect(string(data)).To(Equal("{\"localReference\":\"sha256:f6a519fb1d0c8cef5e8d7811911fc7cb170462bbce19d6df067dae041250de7f\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"ocm/ref:v2.0\",\"type\":\"localBlob\"}")) + hash := HashManifest2(artefactset.DefaultArtefactSetDescriptorFileName) + Expect(string(data)).To(Equal("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"ocm/ref:v2.0\",\"type\":\"localBlob\"}")) data, err = json.Marshal(comp.GetDescriptor().Resources[1].Access) Expect(err).To(Succeed()) - Expect(string(data)).To(Equal("{\"localReference\":\"sha256:018520b2b249464a83e370619f544957b7936dd974468a128545eab88a0f53ed\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"ocm/value:v2.0\",\"type\":\"localBlob\"}")) + hash = HashManifest1(artefactset.DefaultArtefactSetDescriptorFileName) + Expect(string(data)).To(Equal("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"ocm/value:v2.0\",\"type\":\"localBlob\"}")) racc, err := comp.GetResourceByIndex(1) Expect(err).To(Succeed()) @@ -99,29 +98,12 @@ var _ = Describe("Test Environment", func() { _ = ldesc BeforeEach(func() { env = NewTestEnv() - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + + FakeOCIRepo(env.Builder, OCIPATH, OCIHOST) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - ldesc = env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) - env.Namespace(OCINAMESPACE2, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "otherlayer") - }) - }) - }) + ldesc = OCIManifest1(env.Builder) + OCIManifest2(env.Builder) }) env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { diff --git a/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go index d0ac74c9b..007e516b4 100644 --- a/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go @@ -21,6 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/open-component-model/ocm/cmds/ocm/testhelper" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/contexts/ocm/signing" . "github.com/open-component-model/ocm/pkg/testutils" @@ -31,7 +32,6 @@ import ( "github.com/open-component-model/ocm/pkg/common/accessobj" "github.com/open-component-model/ocm/pkg/contexts/datacontext" "github.com/open-component-model/ocm/pkg/contexts/oci" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/signingattr" @@ -50,9 +50,6 @@ const COMPONENTA = "github.com/mandelsoft/test" const COMPONENTB = "github.com/mandelsoft/ref" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/value" -const OCINAMESPACE2 = "ocm/ref" -const OCIVERSION = "v2.0" const OCIHOST = "alias" const SIGNATURE = "test" @@ -82,29 +79,11 @@ var _ = Describe("access method", func() { Expect(err).To(Succeed()) Expect(vfs.WriteFile(env.FileSystem(), PRIVKEY, data, os.ModePerm)).To(Succeed()) - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + FakeOCIRepo(env.Builder, OCIPATH, OCIHOST) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) - env.Namespace(OCINAMESPACE2, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "otherlayer") - }) - }) - }) + OCIManifest1(env.Builder) + OCIManifest2(env.Builder) }) env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { diff --git a/cmds/ocm/commands/ocmcmds/ctf/transfer/cmd_test.go b/cmds/ocm/commands/ocmcmds/ctf/transfer/cmd_test.go index fab58da9b..4e0ebad80 100644 --- a/cmds/ocm/commands/ocmcmds/ctf/transfer/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/ctf/transfer/cmd_test.go @@ -21,13 +21,13 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/open-component-model/ocm/cmds/ocm/testhelper" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/common/accessobj" "github.com/open-component-model/ocm/pkg/contexts/oci" "github.com/open-component-model/ocm/pkg/contexts/oci/artdesc" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" ctfocm "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ctf" @@ -41,9 +41,6 @@ const VERSION = "v1" const COMPONENT = "github.com/mandelsoft/test" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/value" -const OCINAMESPACE2 = "ocm/ref" -const OCIVERSION = "v2.0" const OCIHOST = "alias" var _ = Describe("Test Environment", func() { @@ -53,29 +50,12 @@ var _ = Describe("Test Environment", func() { _ = ldesc BeforeEach(func() { env = NewTestEnv() - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + + FakeOCIRepo(env.Builder, OCIPATH, OCIHOST) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - ldesc = env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) - env.Namespace(OCINAMESPACE2, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "otherlayer") - }) - }) - }) + OCIManifest1(env.Builder) + OCIManifest2(env.Builder) }) env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { diff --git a/cmds/ocm/testhelper/env.go b/cmds/ocm/testhelper/env.go index 6c1d27722..dbca92cf7 100644 --- a/cmds/ocm/testhelper/env.go +++ b/cmds/ocm/testhelper/env.go @@ -63,8 +63,8 @@ func NewTestEnv(opts ...env.Option) *TestEnv { } } -func (e *TestEnv) ApplyOption(opts *accessio.Options) { - e.Builder.ApplyOption(opts) +func (e *TestEnv) ApplyOption(opts accessio.Options) error { + return e.Builder.ApplyOption(opts) } func (e *TestEnv) ConfigContext() config.Context { diff --git a/pkg/common/accessio/format.go b/pkg/common/accessio/format.go index 1dd7a1e00..9fc8035c7 100644 --- a/pkg/common/accessio/format.go +++ b/pkg/common/accessio/format.go @@ -33,10 +33,11 @@ func (f FileFormat) String() string { return string(f) } -func (o FileFormat) ApplyOption(options *Options) { +func (o FileFormat) ApplyOption(options Options) error { if o != "" { - options.FileFormat = &o + options.SetFileFormat(o) } + return nil } const ( diff --git a/pkg/common/accessio/opts.go b/pkg/common/accessio/opts.go index 81eada0fd..b92609043 100644 --- a/pkg/common/accessio/opts.go +++ b/pkg/common/accessio/opts.go @@ -25,7 +25,34 @@ import ( "github.com/open-component-model/ocm/pkg/errors" ) -type Options struct { +type Options interface { + Option + + SetFileFormat(FileFormat) + GetFileFormat() *FileFormat + + SetPathFileSystem(vfs.FileSystem) + GetPathFileSystem() vfs.FileSystem + + SetRepresentation(vfs.FileSystem) + GetRepresentation() vfs.FileSystem + + SetFile(vfs.File) + GetFile() vfs.File + + SetReader(closer io.ReadCloser) + GetReader() io.ReadCloser + + ValidForPath(path string) error + WriterFor(path string, mode vfs.FileMode) (io.WriteCloser, error) + + DefaultFormat(fmt FileFormat) + Default() + + DefaultForPath(path string) error +} + +type StandardOptions struct { // FilePath is the path of the repository base in the filesystem FileFormat *FileFormat `json:"fileFormat"` // FileSystem is the virtual filesystem to evaluate the file path. Default is the OS filesytem @@ -46,62 +73,84 @@ type Options struct { Reader io.ReadCloser `json:"-"` } -var _ Option = &Options{} +var _ Options = (*StandardOptions)(nil) -var _osfs = osfs.New() +func (o *StandardOptions) SetFileFormat(format FileFormat) { + o.FileFormat = &format +} + +func (o *StandardOptions) GetFileFormat() *FileFormat { + return o.FileFormat +} + +func (o *StandardOptions) SetPathFileSystem(fs vfs.FileSystem) { + o.PathFileSystem = fs +} + +func (o *StandardOptions) GetPathFileSystem() vfs.FileSystem { + return o.PathFileSystem +} + +func (o *StandardOptions) SetRepresentation(fs vfs.FileSystem) { + o.Representation = fs +} + +func (o *StandardOptions) GetRepresentation() vfs.FileSystem { + return o.Representation +} + +func (o *StandardOptions) SetFile(file vfs.File) { + o.File = file +} -func (o Options) ApplyOption(options *Options) { +func (o *StandardOptions) GetFile() vfs.File { + return o.File +} + +func (o *StandardOptions) SetReader(r io.ReadCloser) { + o.Reader = r +} + +func (o *StandardOptions) GetReader() io.ReadCloser { + return o.Reader +} + +func (o *StandardOptions) ApplyOption(options Options) error { if o.PathFileSystem != nil { - options.PathFileSystem = o.PathFileSystem + options.SetPathFileSystem(o.PathFileSystem) } if o.Representation != nil { - options.Representation = o.Representation + options.SetRepresentation(o.Representation) } if o.FileFormat != nil { - options.FileFormat = o.FileFormat + options.SetFileFormat(*o.FileFormat) } if o.File != nil { - options.File = o.File + options.SetFile(o.File) } if o.Reader != nil { - options.Reader = o.Reader + options.SetReader(o.Reader) } + return nil } -func (o Options) Default() Options { +var _osfs = osfs.New() + +func (o *StandardOptions) Default() { if o.PathFileSystem == nil { o.PathFileSystem = _osfs } - return o } -func (o Options) DefaultFormat(fmt FileFormat) Options { +func (o *StandardOptions) DefaultFormat(fmt FileFormat) { if o.FileFormat == nil { o.FileFormat = &fmt } - return o } -func (o Options) ValidForPath(path string) error { - count := 0 - if path != "" { - count++ - } - if o.File != nil { - count++ - } - if o.Reader != nil { - count++ - } - if count > 1 { - return errors.ErrInvalid("only path,, file or reader can be set") - } - return nil -} - -func (o Options) DefaultForPath(path string) (Options, error) { +func (o *StandardOptions) DefaultForPath(path string) error { if err := o.ValidForPath(path); err != nil { - return o, err + return err } if o.FileFormat == nil { var fmt *FileFormat @@ -122,12 +171,29 @@ func (o Options) DefaultForPath(path string) (Options, error) { if err == nil { o.FileFormat = fmt } - return o, err + return err } - return o, nil + return nil } -func (o Options) WriterFor(path string, mode vfs.FileMode) (io.WriteCloser, error) { +func (o *StandardOptions) ValidForPath(path string) error { + count := 0 + if path != "" { + count++ + } + if o.File != nil { + count++ + } + if o.Reader != nil { + count++ + } + if count > 1 { + return errors.ErrInvalid("only path,, file or reader can be set") + } + return nil +} + +func (o *StandardOptions) WriterFor(path string, mode vfs.FileMode) (io.WriteCloser, error) { if err := o.ValidForPath(path); err != nil { return nil, err } @@ -142,20 +208,21 @@ func (o Options) WriterFor(path string, mode vfs.FileMode) (io.WriteCloser, erro return writer, err } -// ApplyOptions applies the given list options on these options, -// and then returns itself (for convenient chaining). -func (o Options) ApplyOptions(opts ...Option) Options { - for _, opt := range opts { +// ApplyOptions applies the given list options on these options. +func ApplyOptions(opts Options, olist ...Option) error { + for _, opt := range olist { if opt != nil { - opt.ApplyOption(&o) + if err := opt.ApplyOption(opts); err != nil { + return err + } } } - return o + return nil } // Option is the interface to specify different archive options. type Option interface { - ApplyOption(options *Options) + ApplyOption(options Options) error } // PathFileSystem set the evaluation filesystem for the path name. @@ -168,8 +235,9 @@ type optPfs struct { } // ApplyOption applies the configured path filesystem. -func (o optPfs) ApplyOption(options *Options) { - options.PathFileSystem = o.FileSystem +func (o optPfs) ApplyOption(options Options) error { + options.SetPathFileSystem(o.FileSystem) + return nil } // RepresentationFileSystem set the evaltuation filesystem for the path name. @@ -182,8 +250,9 @@ type optRfs struct { } // ApplyOption applies the configured path filesystem. -func (o optRfs) ApplyOption(options *Options) { - options.Representation = o.FileSystem +func (o optRfs) ApplyOption(options Options) error { + options.SetRepresentation(o.FileSystem) + return nil } // File set open file to use. @@ -196,8 +265,9 @@ type optF struct { } // ApplyOption applies the configured open file. -func (o optF) ApplyOption(options *Options) { - options.File = o.File +func (o optF) ApplyOption(options Options) error { + options.SetFile(o.File) + return nil } // Reader set open reader to use. @@ -210,12 +280,21 @@ type optR struct { } // ApplyOption applies the configured open file. -func (o optR) ApplyOption(options *Options) { - options.Reader = o.ReadCloser +func (o optR) ApplyOption(options Options) error { + options.SetReader(o.ReadCloser) + return nil } //////////////////////////////////////////////////////////////////////////////// -func AccessOptions(opts ...Option) Options { - return Options{}.ApplyOptions(opts...).Default() +func AccessOptions(opts Options, list ...Option) (Options, error) { + if opts == nil { + opts = &StandardOptions{} + } + err := ApplyOptions(opts, list...) + if err != nil { + return nil, err + } + opts.Default() + return opts, nil } diff --git a/pkg/common/accessobj/accessobject.go b/pkg/common/accessobj/accessobject.go index ac4d44b03..b57fb50d4 100644 --- a/pkg/common/accessobj/accessobject.go +++ b/pkg/common/accessobj/accessobject.go @@ -24,48 +24,105 @@ import ( "github.com/open-component-model/ocm/pkg/errors" ) -type DescriptorHandlerFactory func(system vfs.FileSystem) StateHandler +type DescriptorHandlerFactory func(fs vfs.FileSystem) StateHandler //////////////////////////////////////////////////////////////////////////////// -type AccessObjectInfo struct { +// AccessObjectInfo is used to control the persistence of +// a serialization format for sets of elements. +type AccessObjectInfo interface { + SetupFor(fs vfs.FileSystem) error + GetDescriptorFileName() string + GetObjectTypeName() string + GetElementTypeName() string + GetElementDirectoryName() string + GetAdditionalFiles(fs vfs.FileSystem) []string + SetupFileSystem(fs vfs.FileSystem, mode vfs.FileMode) error + SetupDescriptorState(fs vfs.FileSystem) StateHandler + SubPath(name string) string +} + +// DefaultAccessObjectInfo is a default implementation for AccessObjectInfo +// that can be used to describe a simple static configuration. +// The methods do not change the content, therefore an instance can be reused. +type DefaultAccessObjectInfo struct { DescriptorFileName string ObjectTypeName string ElementDirectoryName string ElementTypeName string DescriptorHandlerFactory DescriptorHandlerFactory + AdditionalFiles []string +} + +var _ AccessObjectInfo = (*DefaultAccessObjectInfo)(nil) + +func (i *DefaultAccessObjectInfo) SetupFor(fs vfs.FileSystem) error { + return nil +} + +func (i *DefaultAccessObjectInfo) GetDescriptorFileName() string { + return i.DescriptorFileName +} + +func (i *DefaultAccessObjectInfo) GetObjectTypeName() string { + return i.ObjectTypeName +} + +func (i *DefaultAccessObjectInfo) GetElementTypeName() string { + return i.ElementTypeName +} + +func (i *DefaultAccessObjectInfo) GetElementDirectoryName() string { + return i.ElementDirectoryName +} + +func (i *DefaultAccessObjectInfo) GetAdditionalFiles(fs vfs.FileSystem) []string { + return i.AdditionalFiles +} + +func (i *DefaultAccessObjectInfo) SetupFileSystem(fs vfs.FileSystem, mode vfs.FileMode) error { + if i.ElementDirectoryName != "" { + return fs.MkdirAll(i.ElementDirectoryName, mode) + } + return nil } -func (i *AccessObjectInfo) SubPath(name string) string { +func (i *DefaultAccessObjectInfo) SetupDescriptorState(fs vfs.FileSystem) StateHandler { + return i.DescriptorHandlerFactory(fs) +} + +func (i *DefaultAccessObjectInfo) SubPath(name string) string { return filepath.Join(i.ElementDirectoryName, name) } // AccessObject provides a basic functionality for descriptor based access objects // using a virtual filesystem for the internal representation. type AccessObject struct { - info *AccessObjectInfo + info AccessObjectInfo fs vfs.FileSystem mode vfs.FileMode state State closer Closer } -func NewAccessObject(info *AccessObjectInfo, acc AccessMode, fs vfs.FileSystem, setup Setup, closer Closer, mode vfs.FileMode) (*AccessObject, error) { - defaulted, fs, err := InternalRepresentationFilesystem(acc, fs, info.ElementDirectoryName, mode) +func NewAccessObject(info AccessObjectInfo, acc AccessMode, fs vfs.FileSystem, setup Setup, closer Closer, mode vfs.FileMode) (*AccessObject, error) { + defaulted, fs, err := InternalRepresentationFilesystem(acc, fs, info.SetupFileSystem, mode) if err != nil { return nil, err } if setup != nil { - err = setup.Setup(fs) - if err != nil { + if err := setup.Setup(fs); err != nil { return nil, err } } + if err := info.SetupFor(fs); err != nil { + return nil, err + } if defaulted { closer = FSCloser(closer) } - s, err := NewFileBasedState(acc, fs, info.DescriptorFileName, "", info.DescriptorHandlerFactory(fs), mode) + s, err := NewFileBasedState(acc, fs, info.GetDescriptorFileName(), "", info.SetupDescriptorState(fs), mode) if err != nil { return nil, err } @@ -83,7 +140,7 @@ func NewAccessObject(info *AccessObjectInfo, acc AccessMode, fs vfs.FileSystem, return obj, nil } -func (a *AccessObject) GetInfo() *AccessObjectInfo { +func (a *AccessObject) GetInfo() AccessObjectInfo { return a.info } @@ -119,11 +176,14 @@ func (a *AccessObject) Write(path string, mode vfs.FileMode, opts ...accessio.Op return accessio.ErrClosed } - o := accessio.AccessOptions(opts...) + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return err + } - f := GetFormat(*o.FileFormat) + f := GetFormat(*o.GetFileFormat()) if f == nil { - return errors.ErrUnknown("file format", string(*o.FileFormat)) + return errors.ErrUnknown("file format", string(*o.GetFileFormat())) } return f.Write(a, path, o, mode) diff --git a/pkg/common/accessobj/format-directory.go b/pkg/common/accessobj/format-directory.go index 476f09336..3d1b02c72 100644 --- a/pkg/common/accessobj/format-directory.go +++ b/pkg/common/accessobj/format-directory.go @@ -36,73 +36,88 @@ func init() { type DirectoryHandler struct{} // ApplyOption applies the configured path filesystem. -func (o DirectoryHandler) ApplyOption(options *accessio.Options) { - f := o.Format() - options.FileFormat = &f +func (o DirectoryHandler) ApplyOption(options accessio.Options) error { + options.SetFileFormat(o.Format()) + return nil } func (_ DirectoryHandler) Format() accessio.FileFormat { return accessio.FormatDirectory } -func (_ DirectoryHandler) Open(info *AccessObjectInfo, acc AccessMode, path string, opts accessio.Options) (*AccessObject, error) { +func (_ DirectoryHandler) Open(info AccessObjectInfo, acc AccessMode, path string, opts accessio.Options) (*AccessObject, error) { if err := opts.ValidForPath(path); err != nil { return nil, err } - if opts.File != nil || opts.Reader != nil { + if opts.GetFile() != nil || opts.GetReader() != nil { return nil, errors.ErrNotSupported("file or reader option") } - fs, err := projectionfs.New(opts.PathFileSystem, path) + fs, err := projectionfs.New(opts.GetPathFileSystem(), path) if err != nil { return nil, fmt.Errorf("unable to create projected filesystem from path %s: %w", path, err) } - opts.Representation = fs // TODO: use of temporary copy + opts.SetRepresentation(fs) // TODO: use of temporary copy return NewAccessObject(info, acc, fs, nil, nil, os.ModePerm) } -func (_ DirectoryHandler) Create(info *AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode) (*AccessObject, error) { +func (_ DirectoryHandler) Create(info AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode) (*AccessObject, error) { if err := opts.ValidForPath(path); err != nil { return nil, err } - if opts.File != nil || opts.Reader != nil { + if opts.GetFile() != nil || opts.GetReader() != nil { return nil, errors.ErrNotSupported("file or reader option") } - err := opts.PathFileSystem.Mkdir(path, mode) + err := opts.GetPathFileSystem().Mkdir(path, mode) if err != nil { return nil, err } - opts.Representation, err = projectionfs.New(opts.PathFileSystem, path) + rep, err := projectionfs.New(opts.GetPathFileSystem(), path) if err != nil { - return nil, fmt.Errorf("unable to create projected filesystem from path %s: %w", path, err) + return nil, errors.Wrapf(err, "unable to create projected filesystem from path %s", path) } - return NewAccessObject(info, ACC_CREATE, opts.Representation, nil, nil, mode) + opts.SetRepresentation(rep) + return NewAccessObject(info, ACC_CREATE, rep, nil, nil, mode) } // WriteToFilesystem writes the current object to a filesystem. func (_ DirectoryHandler) Write(obj *AccessObject, path string, opts accessio.Options, mode vfs.FileMode) error { // create the directory structure with the content directory - if err := opts.PathFileSystem.MkdirAll(filepath.Join(path, obj.info.ElementDirectoryName), mode|0o400); err != nil { - return fmt.Errorf("unable to create output directory %q: %w", path, err) + if err := opts.GetPathFileSystem().MkdirAll(filepath.Join(path, obj.info.GetElementDirectoryName()), mode|0o400); err != nil { + return errors.Wrapf(err, "unable to create output directory %q", path) } _, err := obj.updateDescriptor() if err != nil { - return fmt.Errorf("unable to update descriptor: %w", err) + return errors.Wrapf(err, "unable to update descriptor") } // copy descriptor - err = vfs.CopyFile(obj.fs, obj.info.DescriptorFileName, opts.PathFileSystem, filepath.Join(path, obj.info.DescriptorFileName)) + err = vfs.CopyFile(obj.fs, obj.info.GetDescriptorFileName(), opts.GetPathFileSystem(), filepath.Join(path, obj.info.GetDescriptorFileName())) if err != nil { - return fmt.Errorf("unable to copy file '%s': %w", obj.info.DescriptorFileName, err) + return errors.Wrapf(err, "unable to copy file '%s'", obj.info.GetDescriptorFileName()) + } + + // Copy additional files + for _, f := range obj.info.GetAdditionalFiles(obj.fs) { + ok, err := vfs.IsFile(obj.fs, f) + if err != nil { + return errors.Wrapf(err, "cannot check for file %q", f) + } + if ok { + err = vfs.CopyFile(obj.fs, f, opts.GetPathFileSystem(), filepath.Join(path, f)) + if err != nil { + return errors.Wrapf(err, "unable to copy file '%s'", f) + } + } } // copy all content - fileInfos, err := vfs.ReadDir(obj.fs, obj.info.ElementDirectoryName) + fileInfos, err := vfs.ReadDir(obj.fs, obj.info.GetElementDirectoryName()) if err != nil { if os.IsNotExist(err) { return nil } - return fmt.Errorf("unable to read '%s': %w", obj.info.ElementDirectoryName, err) + return errors.Wrapf(err, "unable to read '%s'", obj.info.GetElementDirectoryName()) } for _, fileInfo := range fileInfos { @@ -113,20 +128,20 @@ func (_ DirectoryHandler) Write(obj *AccessObject, path string, opts accessio.Op outpath := filepath.Join(path, inpath) content, err := obj.fs.Open(inpath) if err != nil { - return fmt.Errorf("unable to open input %s %q: %w", obj.info.ElementTypeName, inpath, err) + return errors.Wrapf(err, "unable to open input %s %q", obj.info.GetElementTypeName(), inpath) } - out, err := opts.PathFileSystem.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode|0o666) + out, err := opts.GetPathFileSystem().OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode|0o666) if err != nil { - return fmt.Errorf("unable to open output %s %q: %w", obj.info.ElementTypeName, outpath, err) + return errors.Wrapf(err, "unable to open output %s %q", obj.info.GetElementTypeName(), outpath) } if _, err := io.Copy(out, content); err != nil { - return fmt.Errorf("unable to copy %s from %q to %q: %w", obj.info.ElementTypeName, inpath, outpath, err) + return errors.Wrapf(err, "unable to copy %s from %q to %q", obj.info.GetElementTypeName(), inpath, outpath) } if err := out.Close(); err != nil { - return fmt.Errorf("unable to close output %s %s: %w", obj.info.ElementTypeName, outpath, err) + return errors.Wrapf(err, "unable to close output %s %s", obj.info.GetElementTypeName(), outpath) } if err := content.Close(); err != nil { - return fmt.Errorf("unable to close input %s %s: %w", obj.info.ElementTypeName, outpath, err) + return errors.Wrapf(err, "unable to close input %s %s", obj.info.GetElementTypeName(), outpath) } } diff --git a/pkg/common/accessobj/format-tar.go b/pkg/common/accessobj/format-tar.go index 19d4c2fb3..b51e4abf6 100644 --- a/pkg/common/accessobj/format-tar.go +++ b/pkg/common/accessobj/format-tar.go @@ -24,6 +24,7 @@ import ( "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/common/compression" + "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/utils" ) @@ -54,20 +55,20 @@ func NewTarHandlerWithCompression(format FileFormat, algorithm compression.Algor } // ApplyOption applies the configured path filesystem. -func (h *TarHandler) ApplyOption(options *accessio.Options) { - f := h.Format() - options.FileFormat = &f +func (h *TarHandler) ApplyOption(options accessio.Options) error { + options.SetFileFormat(h.Format()) + return nil } func (h *TarHandler) Format() accessio.FileFormat { return h.format } -func (h *TarHandler) Open(info *AccessObjectInfo, acc AccessMode, path string, opts accessio.Options) (*AccessObject, error) { +func (h *TarHandler) Open(info AccessObjectInfo, acc AccessMode, path string, opts accessio.Options) (*AccessObject, error) { return DefaultOpenOptsFileHandling(fmt.Sprintf("%s archive", h.format), info, acc, path, opts, h) } -func (h *TarHandler) Create(info *AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode) (*AccessObject, error) { +func (h *TarHandler) Create(info AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode) (*AccessObject, error) { return DefaultCreateOptsFileHandling(fmt.Sprintf("%s archive", h.format), info, path, opts, mode, h) } @@ -107,7 +108,7 @@ func (h TarHandler) WriteToStream(obj *AccessObject, writer io.Writer, opts acce tw := tar.NewWriter(writer) cdHeader := &tar.Header{ - Name: obj.info.DescriptorFileName, + Name: obj.info.GetDescriptorFileName(), Size: data.Size(), Mode: FileMode, ModTime: ModTime, @@ -121,30 +122,62 @@ func (h TarHandler) WriteToStream(obj *AccessObject, writer io.Writer, opts acce if err != nil { return fmt.Errorf("unable to get reader: %w", err) } - defer r.Close() if _, err := io.Copy(tw, r); err != nil { return fmt.Errorf("unable to write descriptor content: %w", err) } - // add all content + // Copy additional files + for _, f := range obj.info.GetAdditionalFiles(obj.fs) { + ok, err := vfs.IsFile(obj.fs, f) + if err != nil { + return errors.Wrapf(err, "cannot check for file %q", f) + } + if ok { + fi, err := obj.fs.Stat(f) + if err != nil { + return errors.Wrapf(err, "cannot stat file %q", f) + } + header := &tar.Header{ + Name: f, + Size: fi.Size(), + Mode: FileMode, + ModTime: ModTime, + } + if err := tw.WriteHeader(header); err != nil { + return errors.Wrapf(err, "unable to write descriptor header") + } + + r, err := obj.fs.Open(f) + if err != nil { + return errors.Wrapf(err, "unable to get reader") + } + if _, err := io.Copy(tw, r); err != nil { + r.Close() + return errors.Wrapf(err, "unable to write file %s", f) + } + r.Close() + } + } + + // add all element content err = tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeDir, - Name: obj.info.ElementDirectoryName, + Name: obj.info.GetElementDirectoryName(), Mode: DirMode, ModTime: ModTime, }) if err != nil { - return fmt.Errorf("unable to write %s directory: %w", obj.info.ElementTypeName, err) + return fmt.Errorf("unable to write %s directory: %w", obj.info.GetElementTypeName(), err) } - fileInfos, err := vfs.ReadDir(obj.fs, obj.info.ElementDirectoryName) + fileInfos, err := vfs.ReadDir(obj.fs, obj.info.GetElementDirectoryName()) if err != nil { if os.IsNotExist(err) { return nil } - return fmt.Errorf("unable to read %s directory: %w", obj.info.ElementTypeName, err) + return fmt.Errorf("unable to read %s directory: %w", obj.info.GetElementTypeName(), err) } for _, fileInfo := range fileInfos { @@ -156,25 +189,25 @@ func (h TarHandler) WriteToStream(obj *AccessObject, writer io.Writer, opts acce ModTime: ModTime, } if err := tw.WriteHeader(header); err != nil { - return fmt.Errorf("unable to write %s header: %w", obj.info.ElementTypeName, err) + return fmt.Errorf("unable to write %s header: %w", obj.info.GetElementTypeName(), err) } content, err := obj.fs.Open(path) if err != nil { - return fmt.Errorf("unable to open %s: %w", obj.info.ElementTypeName, err) + return fmt.Errorf("unable to open %s: %w", obj.info.GetElementTypeName(), err) } if _, err := io.Copy(tw, content); err != nil { - return fmt.Errorf("unable to write %s content: %w", obj.info.ElementTypeName, err) + return fmt.Errorf("unable to write %s content: %w", obj.info.GetElementTypeName(), err) } if err := content.Close(); err != nil { - return fmt.Errorf("unable to close %s %s: %w", obj.info.ElementTypeName, path, err) + return fmt.Errorf("unable to close %s %s: %w", obj.info.GetElementTypeName(), path, err) } } return tw.Close() } -func (h *TarHandler) NewFromReader(info *AccessObjectInfo, acc AccessMode, in io.Reader, opts accessio.Options, closer Closer) (*AccessObject, error) { +func (h *TarHandler) NewFromReader(info AccessObjectInfo, acc AccessMode, in io.Reader, opts accessio.Options, closer Closer) (*AccessObject, error) { if h.compression != nil { reader, err := h.compression.Decompressor(in) if err != nil { @@ -189,5 +222,5 @@ func (h *TarHandler) NewFromReader(info *AccessObjectInfo, acc AccessMode, in io } return nil } - return NewAccessObject(info, acc, opts.Representation, SetupFunction(setup), closer, DirMode) + return NewAccessObject(info, acc, opts.GetRepresentation(), SetupFunction(setup), closer, DirMode) } diff --git a/pkg/common/accessobj/format.go b/pkg/common/accessobj/format.go index 98cdb74d6..aae3c5aa3 100644 --- a/pkg/common/accessobj/format.go +++ b/pkg/common/accessobj/format.go @@ -42,8 +42,8 @@ type FormatHandler interface { Format() accessio.FileFormat - Open(info *AccessObjectInfo, acc AccessMode, path string, opts accessio.Options) (*AccessObject, error) - Create(info *AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode) (*AccessObject, error) + Open(info AccessObjectInfo, acc AccessMode, path string, opts accessio.Options) (*AccessObject, error) + Create(info AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode) (*AccessObject, error) Write(obj *AccessObject, path string, opts accessio.Options, mode vfs.FileMode) error } @@ -112,7 +112,7 @@ func FSCloser(closer Closer) Closer { } func (f fsCloser) Close(obj *AccessObject) error { - err := errors.ErrListf("cannot close %s", obj.info.ObjectTypeName) + err := errors.ErrListf("cannot close %s", obj.info.GetObjectTypeName()) if f.closer != nil { err.Add(f.closer.Close(obj)) } @@ -122,30 +122,30 @@ func (f fsCloser) Close(obj *AccessObject) error { type StandardReaderHandler interface { Write(obj *AccessObject, path string, opts accessio.Options, mode vfs.FileMode) error - NewFromReader(info *AccessObjectInfo, acc AccessMode, in io.Reader, opts accessio.Options, closer Closer) (*AccessObject, error) + NewFromReader(info AccessObjectInfo, acc AccessMode, in io.Reader, opts accessio.Options, closer Closer) (*AccessObject, error) } -func DefaultOpenOptsFileHandling(kind string, info *AccessObjectInfo, acc AccessMode, path string, opts accessio.Options, handler StandardReaderHandler) (*AccessObject, error) { +func DefaultOpenOptsFileHandling(kind string, info AccessObjectInfo, acc AccessMode, path string, opts accessio.Options, handler StandardReaderHandler) (*AccessObject, error) { if err := opts.ValidForPath(path); err != nil { return nil, err } - var reader io.ReadCloser var file vfs.File var err error var closer Closer + + reader := opts.GetReader() switch { - case opts.Reader != nil: - reader = opts.Reader - defer opts.Reader.Close() - case opts.File == nil: + case reader != nil: + defer reader.Close() + case opts.GetFile() == nil: // we expect that the path point to a tar - file, err = opts.PathFileSystem.Open(path) + file, err = opts.GetPathFileSystem().Open(path) if err != nil { return nil, fmt.Errorf("unable to open %s from %s: %w", kind, path, err) } defer file.Close() default: - file = opts.File + file = opts.GetFile() } if file != nil { reader = file @@ -158,15 +158,15 @@ func DefaultOpenOptsFileHandling(kind string, info *AccessObjectInfo, acc Access return handler.NewFromReader(info, acc, reader, opts, closer) } -func DefaultCreateOptsFileHandling(kind string, info *AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode, handler StandardReaderHandler) (*AccessObject, error) { +func DefaultCreateOptsFileHandling(kind string, info AccessObjectInfo, path string, opts accessio.Options, mode vfs.FileMode, handler StandardReaderHandler) (*AccessObject, error) { if err := opts.ValidForPath(path); err != nil { return nil, err } - if opts.Reader != nil { + if opts.GetReader() != nil { return nil, errors.ErrNotSupported("reader option not supported") } - if opts.File == nil { - ok, err := vfs.Exists(opts.PathFileSystem, path) + if opts.GetFile() == nil { + ok, err := vfs.Exists(opts.GetPathFileSystem(), path) if err != nil { return nil, err } @@ -175,5 +175,5 @@ func DefaultCreateOptsFileHandling(kind string, info *AccessObjectInfo, path str } } - return NewAccessObject(info, ACC_CREATE, opts.Representation, nil, CloserFunction(func(obj *AccessObject) error { return handler.Write(obj, path, opts, mode) }), DirMode) + return NewAccessObject(info, ACC_CREATE, opts.GetRepresentation(), nil, CloserFunction(func(obj *AccessObject) error { return handler.Write(obj, path, opts, mode) }), DirMode) } diff --git a/pkg/common/accessobj/utils.go b/pkg/common/accessobj/utils.go index 64f42f79c..06c68a9cb 100644 --- a/pkg/common/accessobj/utils.go +++ b/pkg/common/accessobj/utils.go @@ -22,8 +22,10 @@ import ( "github.com/open-component-model/ocm/pkg/errors" ) +type FilesystemSetup func(fs vfs.FileSystem, mode vfs.FileMode) error + // InternalRepresentationFilesystem defaults a filesystem to temp filesystem and adapts. -func InternalRepresentationFilesystem(acc AccessMode, fs vfs.FileSystem, dir string, mode vfs.FileMode) (bool, vfs.FileSystem, error) { +func InternalRepresentationFilesystem(acc AccessMode, fs vfs.FileSystem, setup FilesystemSetup, mode vfs.FileMode) (bool, vfs.FileSystem, error) { var err error tmp := false @@ -34,8 +36,8 @@ func InternalRepresentationFilesystem(acc AccessMode, fs vfs.FileSystem, dir str } tmp = true } - if !acc.IsReadonly() && dir != "" { - err = fs.MkdirAll(dir, mode) + if !acc.IsReadonly() && setup != nil { + err = setup(fs, mode) if err != nil { return false, nil, err } @@ -43,12 +45,14 @@ func InternalRepresentationFilesystem(acc AccessMode, fs vfs.FileSystem, dir str return tmp, fs, err } -func HandleAccessMode(acc AccessMode, path string, opts ...accessio.Option) (accessio.Options, bool, error) { - var err error +func HandleAccessMode(acc AccessMode, path string, opts accessio.Options, olist ...accessio.Option) (accessio.Options, bool, error) { ok := true - o := accessio.AccessOptions(opts...) - if o.File == nil && o.Reader == nil { - ok, err = vfs.Exists(o.PathFileSystem, path) + o, err := accessio.AccessOptions(opts, olist...) + if err != nil { + return nil, false, err + } + if o.GetFile() == nil && o.GetReader() == nil { + ok, err = vfs.Exists(o.GetPathFileSystem(), path) if err != nil { return o, false, err } @@ -57,13 +61,12 @@ func HandleAccessMode(acc AccessMode, path string, opts ...accessio.Option) (acc if !acc.IsCreate() { return o, false, errors.ErrNotFoundWrap(vfs.ErrNotExist, "file", path) } - if o.FileFormat == nil { - fmt := accessio.FormatDirectory - o.FileFormat = &fmt + if o.GetFileFormat() == nil { + o.SetFileFormat(accessio.FormatDirectory) } return o, true, nil } - o, err = o.DefaultForPath(path) + err = o.DefaultForPath(path) return o, false, err } diff --git a/pkg/contexts/clictx/core/context.go b/pkg/contexts/clictx/core/context.go index 12300c5ac..cf0d25b16 100644 --- a/pkg/contexts/clictx/core/context.go +++ b/pkg/contexts/clictx/core/context.go @@ -53,8 +53,9 @@ type FileSystem struct { vfs.FileSystem } -func (f *FileSystem) ApplyOption(options *accessio.Options) { - options.PathFileSystem = f.FileSystem +func (f *FileSystem) ApplyOption(options accessio.Options) error { + options.SetPathFileSystem(f.FileSystem) + return nil } type Context interface { @@ -72,7 +73,7 @@ type Context interface { OCI() OCI OCM() OCM - ApplyOption(options *accessio.Options) + ApplyOption(options accessio.Options) error out.Context WithStdIO(r io.Reader, o io.Writer, e io.Writer) Context @@ -172,8 +173,9 @@ func (c *_context) OCM() OCM { return c.ocm } -func (c *_context) ApplyOption(options *accessio.Options) { - options.PathFileSystem = c.FileSystem() +func (c *_context) ApplyOption(options accessio.Options) error { + options.SetPathFileSystem(c.FileSystem()) + return nil } func (c *_context) StdOut() io.Writer { diff --git a/pkg/contexts/oci/repositories/artefactset/artefactset.go b/pkg/contexts/oci/repositories/artefactset/artefactset.go index a00138375..e80c1b0d6 100644 --- a/pkg/contexts/oci/repositories/artefactset/artefactset.go +++ b/pkg/contexts/oci/repositories/artefactset/artefactset.go @@ -32,6 +32,8 @@ const ( MAINARTEFACT_ANNOTATION = "cloud.gardener.ocm/main" TAGS_ANNOTATION = "cloud.gardener.ocm/tags" TYPE_ANNOTATION = "cloud.gardener.ocm/type" + + OCITAG_ANNOTATION = "org.opencontainers.image.ref.name" ) // ArtefactSet provides an artefact set view on the artefact set implementation. @@ -67,8 +69,8 @@ var ( ) // New returns a new representation based element. -func New(acc accessobj.AccessMode, fs vfs.FileSystem, setup accessobj.Setup, closer accessobj.Closer, mode vfs.FileMode) (*ArtefactSet, error) { - return _Wrap(accessobj.NewAccessObject(accessObjectInfo, acc, fs, setup, closer, mode)) +func New(acc accessobj.AccessMode, fs vfs.FileSystem, setup accessobj.Setup, closer accessobj.Closer, mode vfs.FileMode, formatVersion string) (*ArtefactSet, error) { + return _Wrap(accessobj.NewAccessObject(NewAccessObjectInfo(formatVersion), acc, fs, setup, closer, mode)) } func _Wrap(obj *accessobj.AccessObject, err error) (*ArtefactSet, error) { @@ -105,6 +107,10 @@ func (a *artefactSetImpl) AddTags(digest digest.Digest, tags ...string) error { if a.IsClosed() { return accessio.ErrClosed } + if len(tags) == 0 { + return nil + } + a.base.Lock() defer a.base.Unlock() @@ -121,8 +127,9 @@ func (a *artefactSetImpl) AddTags(digest digest.Digest, tags ...string) error { } else { cur = strings.Join(tags, ",") } - if cur != "" { - e.Annotations[TAGS_ANNOTATION] = cur + e.Annotations[TAGS_ANNOTATION] = cur + if a.base.FileSystemBlobAccess.Access().GetInfo().GetDescriptorFileName() == OCIArtefactSetDescriptorFileName { + e.Annotations[OCITAG_ANNOTATION] = tags[0] } return nil } diff --git a/pkg/contexts/oci/repositories/artefactset/artefactset_test.go b/pkg/contexts/oci/repositories/artefactset/artefactset_test.go index 56fbd4acf..ca0815df5 100644 --- a/pkg/contexts/oci/repositories/artefactset/artefactset_test.go +++ b/pkg/contexts/oci/repositories/artefactset/artefactset_test.go @@ -21,6 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset/testhelper" . "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf/testhelper" "github.com/mandelsoft/vfs/pkg/osfs" @@ -47,14 +48,18 @@ var _ = Describe("artefact management", func() { t, err := osfs.NewTempFileSystem() Expect(err).To(Succeed()) tempfs = t - opts = accessio.AccessOptions(accessio.PathFileSystem(tempfs)) + opts, err = accessio.AccessOptions(nil, accessio.PathFileSystem(tempfs)) + Expect(err).To(Succeed()) }) AfterEach(func() { vfs.Cleanup(tempfs) }) - It("instantiate filesystem artefact", func() { + TestForAllFormats("instantiate filesystem artefact", func(format string) { + opts, err := accessio.AccessOptions(&artefactset.Options{}, opts, artefactset.StructureFormat(format)) + Expect(err).To(Succeed()) + a, err := artefactset.FormatDirectory.Create("test", opts, 0700) Expect(err).To(Succeed()) Expect(vfs.DirExists(tempfs, "test/"+artefactset.BlobsDirectoryName)).To(BeTrue()) @@ -62,7 +67,10 @@ var _ = Describe("artefact management", func() { defaultManifestFill(a) Expect(a.Close()).To(Succeed()) - Expect(vfs.FileExists(tempfs, "test/"+artefactset.ArtefactSetDescriptorFileName)).To(BeTrue()) + + desc := artefactset.DescriptorFileName(format) + Expect(vfs.FileExists(tempfs, "test/"+desc)).To(BeTrue()) + Expect(vfs.FileExists(tempfs, "test/"+artefactset.OCILayouFileName)).To(Equal(desc == artefactset.OCIArtefactSetDescriptorFileName)) infos, err := vfs.ReadDir(tempfs, "test/"+artefactset.BlobsDirectoryName) Expect(err).To(Succeed()) @@ -76,7 +84,10 @@ var _ = Describe("artefact management", func() { "sha256.810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50")) }) - It("instantiate tgz artefact", func() { + TestForAllFormats("instantiate tgz artefact", func(format string) { + opts, err := accessio.AccessOptions(&artefactset.Options{}, opts, artefactset.StructureFormat(format)) + Expect(err).To(Succeed()) + a, err := artefactset.FormatTGZ.Create("test.tgz", opts, 0600) Expect(err).To(Succeed()) @@ -110,19 +121,25 @@ var _ = Describe("artefact management", func() { files = append(files, header.Name) } } - Expect(files).To(ContainElements( - artefactset.ArtefactSetDescriptorFileName, + elems := []interface{}{ + artefactset.DescriptorFileName(format), "blobs/sha256.3d05e105e350edf5be64fe356f4906dd3f9bf442a279e4142db9879bba8e677a", "blobs/sha256.44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", - "blobs/sha256.810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50")) + "blobs/sha256.810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50", + } + if format == artefactset.FORMAT_OCI { + elems = append(elems, artefactset.OCILayouFileName) + } + Expect(files).To(ContainElements(elems)) }) - It("instantiate tgz artefact for open file object", func() { - file, err := vfs.TempFile(opts.PathFileSystem, "", "*.tgz") + TestForAllFormats("instantiate tgz artefact for open file object", func(format string) { + file, err := vfs.TempFile(opts.GetPathFileSystem(), "", "*.tgz") Expect(err).To(Succeed()) defer file.Close() - opts := accessio.AccessOptions(opts, accessio.File(file)) + opts, err := accessio.AccessOptions(&artefactset.Options{FormatVersion: format}, opts, accessio.File(file)) + Expect(err).To(Succeed()) a, err := artefactset.FormatTGZ.Create("", opts, 0600) Expect(err).To(Succeed()) @@ -156,15 +173,23 @@ var _ = Describe("artefact management", func() { files = append(files, header.Name) } } - Expect(files).To(ContainElements( - artefactset.ArtefactSetDescriptorFileName, + elems := []interface{}{ + artefactset.DescriptorFileName(format), "blobs/sha256.3d05e105e350edf5be64fe356f4906dd3f9bf442a279e4142db9879bba8e677a", "blobs/sha256.44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", - "blobs/sha256.810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50")) + "blobs/sha256.810ff2fb242a5dee4220f2cb0e6a519891fb67f2f828a6cab4ef8894633b1f50", + } + if format == artefactset.FORMAT_OCI { + elems = append(elems, artefactset.OCILayouFileName) + } + Expect(files).To(ContainElements(elems)) }) Context("manifest", func() { - It("read from filesystem artefact", func() { + TestForAllFormats("read from filesystem artefact", func(format string) { + opts, err := accessio.AccessOptions(&artefactset.Options{}, opts, artefactset.StructureFormat(format)) + Expect(err).To(Succeed()) + a, err := artefactset.FormatDirectory.Create("test", opts, 0700) Expect(err).To(Succeed()) Expect(vfs.DirExists(tempfs, "test/"+artefactset.BlobsDirectoryName)).To(BeTrue()) @@ -183,7 +208,11 @@ var _ = Describe("artefact management", func() { Expect(blob.Get()).To(Equal([]byte("testdata"))) Expect(blob.MimeType()).To(Equal(mime.MIME_OCTET)) }) - It("read from tgz artefact", func() { + + TestForAllFormats("read from tgz artefact", func(format string) { + opts, err := accessio.AccessOptions(&artefactset.Options{}, opts, artefactset.StructureFormat(format)) + Expect(err).To(Succeed()) + a, err := artefactset.FormatTGZ.Create("test.tgz", opts, 0700) Expect(err).To(Succeed()) defaultManifestFill(a) @@ -201,9 +230,5 @@ var _ = Describe("artefact management", func() { Expect(blob.Get()).To(Equal([]byte("testdata"))) Expect(blob.MimeType()).To(Equal(mime.MIME_OCTET)) }) - - }) - Context("index", func() { - }) }) diff --git a/pkg/contexts/oci/repositories/artefactset/format.go b/pkg/contexts/oci/repositories/artefactset/format.go index 8306ec3e2..9c7ef8286 100644 --- a/pkg/contexts/oci/repositories/artefactset/format.go +++ b/pkg/contexts/oci/repositories/artefactset/format.go @@ -29,16 +29,108 @@ import ( const ( ArtefactSetDescriptorFileName = format.ArtefactSetDescriptorFileName BlobsDirectoryName = format.BlobsDirectoryName + + OCIArtefactSetDescriptorFileName = "index.json" + OCILayouFileName = "oci-layout" ) -var accessObjectInfo = &accessobj.AccessObjectInfo{ - DescriptorFileName: ArtefactSetDescriptorFileName, - ObjectTypeName: "artefactset", - ElementDirectoryName: BlobsDirectoryName, - ElementTypeName: "blob", - DescriptorHandlerFactory: NewStateHandler, +var DefaultArtefactSetDescriptorFileName = OCIArtefactSetDescriptorFileName + +func IsOCIDefaultFormat() bool { + return DefaultArtefactSetDescriptorFileName == OCIArtefactSetDescriptorFileName +} + +func DescriptorFileName(format string) string { + switch format { + case FORMAT_OCI: + return OCIArtefactSetDescriptorFileName + case FORMAT_OCM: + return ArtefactSetDescriptorFileName + case "": + return DefaultArtefactSetDescriptorFileName + } + return "" +} + +type accessObjectInfo struct { + accessobj.DefaultAccessObjectInfo +} + +var _ accessobj.AccessObjectInfo = (*accessObjectInfo)(nil) + +func NewAccessObjectInfo(fmts ...string) accessobj.AccessObjectInfo { + a := &accessObjectInfo{ + accessobj.DefaultAccessObjectInfo{ + ObjectTypeName: "artefactset", + ElementDirectoryName: BlobsDirectoryName, + ElementTypeName: "blob", + DescriptorHandlerFactory: NewStateHandler, + }, + } + oci := IsOCIDefaultFormat() + if len(fmts) > 0 { + switch fmts[0] { + case FORMAT_OCM: + oci = false + case FORMAT_OCI: + oci = true + case "": + } + } + if oci { + a.setOCI() + } else { + a.setOCM() + } + return a +} + +func (a *accessObjectInfo) setOCI() { + a.DescriptorFileName = OCIArtefactSetDescriptorFileName + a.AdditionalFiles = []string{OCILayouFileName} +} + +func (a *accessObjectInfo) setOCM() { + a.DescriptorFileName = ArtefactSetDescriptorFileName + a.AdditionalFiles = nil +} + +func (a *accessObjectInfo) setupOCIFS(fs vfs.FileSystem, mode vfs.FileMode) error { + data := `{ + "imageLayoutVersion": "1.0.0" +} +` + return vfs.WriteFile(fs, OCILayouFileName, []byte(data), mode) +} + +func (a *accessObjectInfo) SetupFileSystem(fs vfs.FileSystem, mode vfs.FileMode) error { + if err := a.SetupFor(fs); err != nil { + return err + } + if err := a.DefaultAccessObjectInfo.SetupFileSystem(fs, mode); err != nil { + return err + } + if len(a.AdditionalFiles) > 0 { + return a.setupOCIFS(fs, mode) + } + return nil } +func (a *accessObjectInfo) SetupFor(fs vfs.FileSystem) error { + ok, err := vfs.FileExists(fs, OCILayouFileName) + if err != nil { + return err + } + if ok { + a.setOCI() + } else { //nolint: staticcheck // keep comment for else + // keep configured format + } + return nil +} + +//////////////////////////////////////////////////////////////////////////////// + type Object = ArtefactSet type FormatHandler interface { @@ -95,8 +187,11 @@ func SupportedFormats() []accessio.FileFormat { //////////////////////////////////////////////////////////////////////////////// func OpenFromBlob(acc accessobj.AccessMode, blob accessio.BlobAccess, opts ...accessio.Option) (*Object, error) { - o := accessio.AccessOptions(opts...) - if o.File != nil || o.Reader != nil { + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return nil, err + } + if o.GetFile() != nil || o.GetReader() != nil { return nil, errors.ErrInvalid("file or reader option nor possible for blob access") } reader, err := blob.Reader() @@ -104,25 +199,25 @@ func OpenFromBlob(acc accessobj.AccessMode, blob accessio.BlobAccess, opts ...ac return nil, err } defer reader.Close() - o.Reader = reader + o.SetReader(reader) fmt := accessio.FormatTar mime := blob.MimeType() if mime2.IsGZip(mime) { fmt = accessio.FormatTGZ } - o.FileFormat = &fmt + o.SetFileFormat(fmt) return Open(acc&accessobj.ACC_READONLY, "", 0, o) } -func Open(acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) { - o, create, err := accessobj.HandleAccessMode(acc, path, opts...) +func Open(acc accessobj.AccessMode, path string, mode vfs.FileMode, olist ...accessio.Option) (*Object, error) { + o, create, err := accessobj.HandleAccessMode(acc, path, &Options{}, olist...) if err != nil { return nil, err } - h, ok := fileFormats[*o.FileFormat] + h, ok := fileFormats[*o.GetFileFormat()] if !ok { - return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.FileFormat.String()) + return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.GetFileFormat().String()) } if create { return h.Create(path, o, mode) @@ -131,10 +226,14 @@ func Open(acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...acce } func Create(acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) { - o := accessio.AccessOptions(opts...).DefaultFormat(accessio.FormatDirectory) - h, ok := fileFormats[*o.FileFormat] + o, err := accessio.AccessOptions(&Options{}, opts...) + if err != nil { + return nil, err + } + o.DefaultFormat(accessio.FormatDirectory) + h, ok := fileFormats[*o.GetFileFormat()] if !ok { - return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.FileFormat.String()) + return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.GetFileFormat().String()) } return h.Create(path, o, mode) } @@ -142,11 +241,11 @@ func Create(acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...ac //////////////////////////////////////////////////////////////////////////////// func (h *formatHandler) Open(acc accessobj.AccessMode, path string, opts accessio.Options) (*Object, error) { - return _Wrap(h.FormatHandler.Open(accessObjectInfo, acc, path, opts)) + return _Wrap(h.FormatHandler.Open(NewAccessObjectInfo(GetFormatVersion(opts)), acc, path, opts)) } func (h *formatHandler) Create(path string, opts accessio.Options, mode vfs.FileMode) (*Object, error) { - return _Wrap(h.FormatHandler.Create(accessObjectInfo, path, opts, mode)) + return _Wrap(h.FormatHandler.Create(NewAccessObjectInfo(GetFormatVersion(opts)), path, opts, mode)) } // WriteToFilesystem writes the current object to a filesystem. diff --git a/pkg/contexts/oci/repositories/artefactset/options.go b/pkg/contexts/oci/repositories/artefactset/options.go new file mode 100644 index 000000000..07b0c0855 --- /dev/null +++ b/pkg/contexts/oci/repositories/artefactset/options.go @@ -0,0 +1,90 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package artefactset + +import ( + "github.com/open-component-model/ocm/pkg/common/accessio" + "github.com/open-component-model/ocm/pkg/errors" +) + +type Options struct { + accessio.StandardOptions + + FormatVersion string `json:"formatVersion,omitempty"` +} + +func NewOptions(olist ...accessio.Option) (*Options, error) { + opts := &Options{} + err := accessio.ApplyOptions(opts, olist...) + if err != nil { + return nil, err + } + return opts, nil +} + +type FormatVersionOption interface { + SetFormatVersion(string) + GetFormatVersion() string +} + +func GetFormatVersion(opts accessio.Options) string { + if o, ok := opts.(FormatVersionOption); ok { + return o.GetFormatVersion() + } + return "" +} + +var _ FormatVersionOption = (*Options)(nil) + +func (o *Options) SetFormatVersion(s string) { + o.FormatVersion = s +} + +func (o *Options) GetFormatVersion() string { + return o.FormatVersion +} + +func (o *Options) ApplyOption(opts accessio.Options) error { + err := o.StandardOptions.ApplyOption(opts) + if err != nil { + return err + } + if o.FormatVersion != "" { + if s, ok := opts.(FormatVersionOption); ok { + s.SetFormatVersion(o.FormatVersion) + } else { + return errors.ErrNotSupported("format version option") + } + } + return nil +} + +type optFmt struct { + format string +} + +var _ accessio.Option = (*optFmt)(nil) + +func StructureFormat(fmt string) accessio.Option { + return &optFmt{fmt} +} + +func (o *optFmt) ApplyOption(opts accessio.Options) error { + if s, ok := opts.(FormatVersionOption); ok { + s.SetFormatVersion(o.format) + return nil + } + return errors.ErrNotSupported("format version option") +} diff --git a/pkg/contexts/oci/repositories/artefactset/repo_test.go b/pkg/contexts/oci/repositories/artefactset/repo_test.go index accc55325..6520267cb 100644 --- a/pkg/contexts/oci/repositories/artefactset/repo_test.go +++ b/pkg/contexts/oci/repositories/artefactset/repo_test.go @@ -51,7 +51,8 @@ var _ = Describe("", func() { }) }) - spec := artefactset.NewRepositorySpec(accessobj.ACC_READONLY, "/tmp/set", accessio.PathFileSystem(env)) + spec, err := artefactset.NewRepositorySpec(accessobj.ACC_READONLY, "/tmp/set", accessio.PathFileSystem(env)) + Expect(err).To(Succeed()) r, err := cpi.DefaultContext.RepositoryForSpec(spec) Expect(err).To(Succeed()) diff --git a/pkg/contexts/oci/repositories/artefactset/repository.go b/pkg/contexts/oci/repositories/artefactset/repository.go index a058c29c5..938edc34d 100644 --- a/pkg/contexts/oci/repositories/artefactset/repository.go +++ b/pkg/contexts/oci/repositories/artefactset/repository.go @@ -49,7 +49,7 @@ func (r *Repository) Get() *ArtefactSet { } func (r *Repository) Open() (*ArtefactSet, error) { - a, err := Open(r.spec.AccessMode, r.spec.FilePath, 0o700, r.spec.Options, accessio.PathFileSystem(r.spec.PathFileSystem)) + a, err := Open(r.spec.AccessMode, r.spec.FilePath, 0o700, &Options{}, &r.spec.Options, accessio.PathFileSystem(r.spec.PathFileSystem)) if err != nil { return nil, err } diff --git a/pkg/contexts/oci/repositories/artefactset/testhelper/formats.go b/pkg/contexts/oci/repositories/artefactset/testhelper/formats.go new file mode 100644 index 000000000..fc939e910 --- /dev/null +++ b/pkg/contexts/oci/repositories/artefactset/testhelper/formats.go @@ -0,0 +1,37 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testhelper + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + + "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset" +) + +func TestForAllFormats(msg string, f func(fmt string)) { + DescribeTable(fmt.Sprintf("%s: structure format handling", msg), f, + Entry("OCM format", artefactset.FORMAT_OCM), + Entry("OCI format", artefactset.FORMAT_OCI), + ) +} + +func FTestForAllFormats(msg string, f func(fmt string)) { + FDescribeTable(fmt.Sprintf("%s: structure format handling", msg), f, + Entry("OCM format", artefactset.FORMAT_OCM), + Entry("OCI format", artefactset.FORMAT_OCI), + ) +} diff --git a/pkg/contexts/oci/repositories/artefactset/type.go b/pkg/contexts/oci/repositories/artefactset/type.go index 11c13f846..c14d8a22e 100644 --- a/pkg/contexts/oci/repositories/artefactset/type.go +++ b/pkg/contexts/oci/repositories/artefactset/type.go @@ -34,31 +34,48 @@ func init() { cpi.RegisterRepositoryType(TypeV1, cpi.NewRepositoryType(TypeV1, &RepositorySpec{})) } +const ( + FORMAT_OCI = "oci/v1" + FORMAT_OCM = "ocm/v1" +) + type RepositorySpec struct { runtime.ObjectVersionedType `json:",inline"` - accessio.Options `json:",inline"` + Options `json:",inline"` // FileFormat is the format of the repository file FilePath string `json:"filePath"` // AccessMode can be set to request readonly access or creation AccessMode accessobj.AccessMode `json:"accessMode,omitempty"` + + FormatVersion string `json:"formatVersion,omitempty"` } // NewRepositorySpec creates a new RepositorySpec. -func NewRepositorySpec(acc accessobj.AccessMode, filePath string, opts ...accessio.Option) *RepositorySpec { - o := accessio.AccessOptions(opts...) +func NewRepositorySpec(acc accessobj.AccessMode, filePath string, opts ...accessio.Option) (*RepositorySpec, error) { + o, err := accessio.AccessOptions(&Options{}, opts...) + if err != nil { + return nil, err + } return &RepositorySpec{ ObjectVersionedType: runtime.NewVersionedObjectType(Type), FilePath: filePath, - Options: o, + Options: *o.(*Options), AccessMode: acc, - } + }, nil } func (s *RepositorySpec) Name() string { return s.FilePath } +func (s *RepositorySpec) GetFormatVersion() string { + if s.FormatVersion == "" { + return FORMAT_OCM + } + return s.FormatVersion +} + func (s *RepositorySpec) UniformRepositorySpec() *cpi.UniformRepositorySpec { u := &cpi.UniformRepositorySpec{ Type: Type, @@ -76,8 +93,8 @@ func (a *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentia } func (a *RepositorySpec) AsUniformSpec(cpi.Context) cpi.UniformRepositorySpec { - opts := a.Options.Default() - p, err := vfs.Canonical(opts.PathFileSystem, a.FilePath, false) + opts, _ := NewOptions(&a.Options) // now unknown option possible (same Options type) + p, err := vfs.Canonical(opts.GetPathFileSystem(), a.FilePath, false) if err != nil { return cpi.UniformRepositorySpec{Type: a.GetKind(), Info: a.FilePath} } diff --git a/pkg/contexts/oci/repositories/artefactset/uniform.go b/pkg/contexts/oci/repositories/artefactset/uniform.go index 358dbe0cb..3691c1cb8 100644 --- a/pkg/contexts/oci/repositories/artefactset/uniform.go +++ b/pkg/contexts/oci/repositories/artefactset/uniform.go @@ -46,18 +46,19 @@ func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepository if !u.CreateIfMissing { hint = "" } + create, ok, err := accessobj.CheckFile(Type, hint, accessio.TypeForType(u.Type) == Type, path, fs, ArtefactSetDescriptorFileName) + if err == nil && !ok { + create, ok, err = accessobj.CheckFile(Type, hint, accessio.TypeForType(u.Type) == Type, path, fs, OCIArtefactSetDescriptorFileName) + } + if !ok || err != nil { - if err != nil { - return nil, err - } - if !ok { - return nil, nil - } + return nil, err } + mode := accessobj.ACC_WRITABLE if create { mode |= accessobj.ACC_CREATE } - return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)), nil + return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)) } diff --git a/pkg/contexts/oci/repositories/ctf/ctf_test.go b/pkg/contexts/oci/repositories/ctf/ctf_test.go index 021f8356b..252a5e191 100644 --- a/pkg/contexts/oci/repositories/ctf/ctf_test.go +++ b/pkg/contexts/oci/repositories/ctf/ctf_test.go @@ -47,7 +47,8 @@ var _ = Describe("ctf management", func() { Expect(err).To(Succeed()) tempfs = t - spec = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory) + spec, err = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory) + Expect(err).To(Succeed()) }) AfterEach(func() { @@ -55,7 +56,7 @@ var _ = Describe("ctf management", func() { }) It("instantiate filesystem ctf", func() { - r, err := ctf.FormatDirectory.Create(oci.DefaultContext(), "test", spec.Options, 0700) + r, err := ctf.FormatDirectory.Create(oci.DefaultContext(), "test", &spec.StandardOptions, 0700) Expect(err).To(Succeed()) Expect(vfs.DirExists(tempfs, "test/"+ctf.BlobsDirectoryName)).To(BeTrue()) @@ -111,7 +112,7 @@ var _ = Describe("ctf management", func() { }) It("instantiate tgz artefact", func() { - ctf.FormatTGZ.ApplyOption(&spec.Options) + ctf.FormatTGZ.ApplyOption(&spec.StandardOptions) spec.FilePath = "test.tgz" r, err := spec.Repository(nil, nil) Expect(err).To(Succeed()) diff --git a/pkg/contexts/oci/repositories/ctf/format.go b/pkg/contexts/oci/repositories/ctf/format.go index 6e8e9d931..43bfcf45f 100644 --- a/pkg/contexts/oci/repositories/ctf/format.go +++ b/pkg/contexts/oci/repositories/ctf/format.go @@ -33,7 +33,7 @@ const ( BlobsDirectoryName = format.BlobsDirectoryName ) -var accessObjectInfo = &accessobj.AccessObjectInfo{ +var accessObjectInfo = &accessobj.DefaultAccessObjectInfo{ DescriptorFileName: ArtefactIndexFileName, ObjectTypeName: "repository", ElementDirectoryName: BlobsDirectoryName, @@ -98,8 +98,11 @@ func SupportedFormats() []accessio.FileFormat { //////////////////////////////////////////////////////////////////////////////// func OpenFromBlob(ctx cpi.Context, acc accessobj.AccessMode, blob accessio.BlobAccess, opts ...accessio.Option) (*Object, error) { - o := accessio.AccessOptions(opts...) - if o.File != nil || o.Reader != nil { + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return nil, err + } + if o.GetFile() != nil || o.GetReader() != nil { return nil, errors.ErrInvalid("file or reader option nor possible for blob access") } reader, err := blob.Reader() @@ -107,24 +110,24 @@ func OpenFromBlob(ctx cpi.Context, acc accessobj.AccessMode, blob accessio.BlobA return nil, err } defer reader.Close() - o.Reader = reader + o.SetReader(reader) fmt := accessio.FormatTar mime := blob.MimeType() if strings.HasSuffix(mime, "+gzip") { fmt = accessio.FormatTGZ } - o.FileFormat = &fmt + o.SetFileFormat(fmt) return Open(ctx, acc&accessobj.ACC_READONLY, "", 0, o) } func Open(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) { - o, create, err := accessobj.HandleAccessMode(acc, path, opts...) + o, create, err := accessobj.HandleAccessMode(acc, path, nil, opts...) if err != nil { return nil, err } - h, ok := fileFormats[*o.FileFormat] + h, ok := fileFormats[*o.GetFileFormat()] if !ok { - return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.FileFormat.String()) + return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.GetFileFormat().String()) } if create { return h.Create(ctx, path, o, mode) @@ -133,22 +136,34 @@ func Open(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileM } func Create(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) { - o := accessio.AccessOptions(opts...).DefaultFormat(accessio.FormatDirectory) - h, ok := fileFormats[*o.FileFormat] + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return nil, err + } + o.DefaultFormat(accessio.FormatDirectory) + h, ok := fileFormats[*o.GetFileFormat()] if !ok { - return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.FileFormat.String()) + return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.GetFileFormat().String()) } return h.Create(ctx, path, o, mode) } func (h *formatHandler) Open(ctx cpi.Context, acc accessobj.AccessMode, path string, opts accessio.Options) (*Object, error) { obj, err := h.FormatHandler.Open(accessObjectInfo, acc, path, opts) - return _Wrap(ctx, NewRepositorySpec(acc, path, opts), obj, err) + if err != nil { + return nil, err + } + spec, err := NewRepositorySpec(acc, path, opts) + return _Wrap(ctx, spec, obj, err) } func (h *formatHandler) Create(ctx cpi.Context, path string, opts accessio.Options, mode vfs.FileMode) (*Object, error) { obj, err := h.FormatHandler.Create(accessObjectInfo, path, opts, mode) - return _Wrap(ctx, NewRepositorySpec(accessobj.ACC_CREATE, path, opts), obj, err) + if err != nil { + return nil, err + } + spec, err := NewRepositorySpec(accessobj.ACC_CREATE, path, opts) + return _Wrap(ctx, spec, obj, err) } // WriteToFilesystem writes the current object to a filesystem. diff --git a/pkg/contexts/oci/repositories/ctf/repository.go b/pkg/contexts/oci/repositories/ctf/repository.go index cd8089137..93283c069 100644 --- a/pkg/contexts/oci/repositories/ctf/repository.go +++ b/pkg/contexts/oci/repositories/ctf/repository.go @@ -70,10 +70,10 @@ var _ cpi.Repository = (*Repository)(nil) // New returns a new representation based repository. func New(ctx cpi.Context, spec *RepositorySpec, setup accessobj.Setup, closer accessobj.Closer, mode vfs.FileMode) (*Repository, error) { - if spec.PathFileSystem == nil { - spec.PathFileSystem = vfsattr.Get(ctx) + if spec.GetPathFileSystem() == nil { + spec.SetPathFileSystem(vfsattr.Get(ctx)) } - base, err := accessobj.NewAccessObject(accessObjectInfo, spec.AccessMode, spec.Options.Representation, setup, closer, mode) + base, err := accessobj.NewAccessObject(accessObjectInfo, spec.AccessMode, spec.GetRepresentation(), setup, closer, mode) return _Wrap(ctx, spec, base, err) } diff --git a/pkg/contexts/oci/repositories/ctf/synthesis_test.go b/pkg/contexts/oci/repositories/ctf/synthesis_test.go index 4f35fd1b5..5f79f58fa 100644 --- a/pkg/contexts/oci/repositories/ctf/synthesis_test.go +++ b/pkg/contexts/oci/repositories/ctf/synthesis_test.go @@ -64,14 +64,18 @@ func CheckBlob(blob accessio.BlobAccess) oci.NamespaceAccess { Expect(idx.Annotations).To(Equal(map[string]string{ artefactset.MAINARTEFACT_ANNOTATION: "sha256:" + DIGEST_MANIFEST, })) + annos := map[string]string{ + "cloud.gardener.ocm/tags": "v1", + } + if artefactset.IsOCIDefaultFormat() { + annos[artefactset.OCITAG_ANNOTATION] = "v1" + } Expect(idx.Manifests).To(Equal([]artdesc.Descriptor{ { - MediaType: artdesc.MediaTypeImageManifest, - Digest: "sha256:" + DIGEST_MANIFEST, - Size: 362, - Annotations: map[string]string{ - "cloud.gardener.ocm/tags": "v1", - }, + MediaType: artdesc.MediaTypeImageManifest, + Digest: "sha256:" + DIGEST_MANIFEST, + Size: 362, + Annotations: annos, }, })) @@ -103,7 +107,8 @@ var _ = Describe("syntheses", func() { t, err := osfs.NewTempFileSystem() Expect(err).To(Succeed()) tempfs = t - spec = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory) + spec, err = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory) + Expect(err).To(Succeed()) }) AfterEach(func() { @@ -111,7 +116,7 @@ var _ = Describe("syntheses", func() { }) It("synthesize", func() { - r, err := ctf.FormatDirectory.Create(oci.DefaultContext(), "test", spec.Options, 0700) + r, err := ctf.FormatDirectory.Create(oci.DefaultContext(), "test", &spec.StandardOptions, 0700) Expect(err).To(Succeed()) n, err := r.LookupNamespace("mandelsoft/test") Expect(err).To(Succeed()) @@ -119,7 +124,7 @@ var _ = Describe("syntheses", func() { Expect(n.Close()).To(Succeed()) Expect(r.Close()).To(Succeed()) - r, err = ctf.Open(oci.DefaultContext(), accessobj.ACC_READONLY, "test", 0, spec.Options) + r, err = ctf.Open(oci.DefaultContext(), accessobj.ACC_READONLY, "test", 0, &spec.StandardOptions) Expect(err).To(Succeed()) defer Close(r, "ctf") n, err = r.LookupNamespace("mandelsoft/test") diff --git a/pkg/contexts/oci/repositories/ctf/type.go b/pkg/contexts/oci/repositories/ctf/type.go index 436317eec..4c2ad9e4c 100644 --- a/pkg/contexts/oci/repositories/ctf/type.go +++ b/pkg/contexts/oci/repositories/ctf/type.go @@ -37,7 +37,7 @@ func init() { // RepositorySpec describes an OCI registry interface backed by an oci registry. type RepositorySpec struct { runtime.ObjectVersionedType `json:",inline"` - accessio.Options `json:",inline"` + accessio.StandardOptions `json:",inline"` // FileFormat is the format of the repository file FilePath string `json:"filePath"` @@ -50,22 +50,26 @@ var _ cpi.RepositorySpec = (*RepositorySpec)(nil) var _ cpi.IntermediateRepositorySpecAspect = (*RepositorySpec)(nil) // NewRepositorySpec creates a new RepositorySpec. -func NewRepositorySpec(mode accessobj.AccessMode, filePath string, opts ...accessio.Option) *RepositorySpec { - o := accessio.AccessOptions(opts...) - if o.FileFormat == nil { +func NewRepositorySpec(mode accessobj.AccessMode, filePath string, opts ...accessio.Option) (*RepositorySpec, error) { + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return nil, err + } + if o.GetFileFormat() == nil { for _, v := range SupportedFormats() { if strings.HasSuffix(filePath, "."+v.String()) { - o.FileFormat = &v + o.SetFileFormat(v) break } } } + o.Default() return &RepositorySpec{ ObjectVersionedType: runtime.NewVersionedObjectType(Type), FilePath: filePath, - Options: o.Default(), + StandardOptions: *o.(*accessio.StandardOptions), AccessMode: mode, - } + }, nil } func (a *RepositorySpec) IsIntermediate() bool { @@ -89,5 +93,5 @@ func (s *RepositorySpec) UniformRepositorySpec() *cpi.UniformRepositorySpec { } func (a *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentials) (cpi.Repository, error) { - return Open(ctx, a.AccessMode, a.FilePath, 0o700, a.Options) + return Open(ctx, a.AccessMode, a.FilePath, 0o700, &a.StandardOptions) } diff --git a/pkg/contexts/oci/repositories/ctf/uniform.go b/pkg/contexts/oci/repositories/ctf/uniform.go index 5108bdf3b..edce95d0c 100644 --- a/pkg/contexts/oci/repositories/ctf/uniform.go +++ b/pkg/contexts/oci/repositories/ctf/uniform.go @@ -63,5 +63,5 @@ func MapReference(ctx cpi.Context, u *cpi.UniformRepositorySpec) (cpi.Repository if create { mode |= accessobj.ACC_CREATE } - return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)), nil + return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)) } diff --git a/pkg/contexts/oci/testhelper/manifests.go b/pkg/contexts/oci/testhelper/manifests.go new file mode 100644 index 000000000..3c485191f --- /dev/null +++ b/pkg/contexts/oci/testhelper/manifests.go @@ -0,0 +1,83 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testhelper + +import ( + "github.com/open-component-model/ocm/pkg/contexts/oci/artdesc" + "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset" + "github.com/open-component-model/ocm/pkg/env/builder" + "github.com/open-component-model/ocm/pkg/mime" +) + +//////////////////////////////////////////////////////////////////////////////// +// manifestlaver + +const ( + OCINAMESPACE = "ocm/value" + OCIVERSION = "v2.0" +) + +func OCIManifest1(env *builder.Builder) *artdesc.Descriptor { + var ldesc *artdesc.Descriptor + + env.Namespace(OCINAMESPACE, func() { + env.Manifest(OCIVERSION, func() { + env.Config(func() { + env.BlobStringData(mime.MIME_JSON, "{}") + }) + ldesc = env.Layer(func() { + env.BlobStringData(mime.MIME_TEXT, "manifestlayer") + }) + }) + }) + return ldesc +} + +func HashManifest1(fmt string) string { + hash := "sha256:018520b2b249464a83e370619f544957b7936dd974468a128545eab88a0f53ed" + if fmt == artefactset.FORMAT_OCI || fmt == artefactset.OCIArtefactSetDescriptorFileName { + hash = "sha256:334b587868e607fe2ce74c27d7f75e90b6391fe91b808b2d42ad1bfcc5651a66" + } + return hash +} + +//////////////////////////////////////////////////////////////////////////////// +// otherlayer + +const OCINAMESPACE2 = "ocm/ref" + +func OCIManifest2(env *builder.Builder) *artdesc.Descriptor { + var ldesc *artdesc.Descriptor + + env.Namespace(OCINAMESPACE2, func() { + env.Manifest(OCIVERSION, func() { + env.Config(func() { + env.BlobStringData(mime.MIME_JSON, "{}") + }) + ldesc = env.Layer(func() { + env.BlobStringData(mime.MIME_TEXT, "otherlayer") + }) + }) + }) + return ldesc +} + +func HashManifest2(fmt string) string { + hash := "sha256:f6a519fb1d0c8cef5e8d7811911fc7cb170462bbce19d6df067dae041250de7f" + if fmt == artefactset.FORMAT_OCI || fmt == artefactset.OCIArtefactSetDescriptorFileName { + hash = "sha256:253c2a52cd0e229ae97613b953e1aa5c0b8146ff653988904e858a676507d4f4" + } + return hash +} diff --git a/pkg/contexts/oci/testhelper/oci.go b/pkg/contexts/oci/testhelper/oci.go new file mode 100644 index 000000000..e4a376777 --- /dev/null +++ b/pkg/contexts/oci/testhelper/oci.go @@ -0,0 +1,30 @@ +// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testhelper + +import ( + . "github.com/onsi/gomega" + + "github.com/open-component-model/ocm/pkg/common/accessio" + "github.com/open-component-model/ocm/pkg/common/accessobj" + "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" + "github.com/open-component-model/ocm/pkg/env/builder" +) + +func FakeOCIRepo(env *builder.Builder, path string, host string) { + spec, err := ctf.NewRepositorySpec(accessobj.ACC_READONLY, path, accessio.PathFileSystem(env.FileSystem())) + ExpectWithOffset(1, err).To(Succeed()) + env.OCIContext().SetAlias(host, spec) +} diff --git a/pkg/contexts/ocm/accessmethods/ociartefact/method_test.go b/pkg/contexts/ocm/accessmethods/ociartefact/method_test.go index 93e51b45c..d32e4e8c9 100644 --- a/pkg/contexts/ocm/accessmethods/ociartefact/method_test.go +++ b/pkg/contexts/ocm/accessmethods/ociartefact/method_test.go @@ -17,21 +17,17 @@ package ociartefact_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/env" . "github.com/open-component-model/ocm/pkg/env/builder" "github.com/open-component-model/ocm/pkg/common/accessio" - "github.com/open-component-model/ocm/pkg/common/accessobj" "github.com/open-component-model/ocm/pkg/contexts/oci" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/mime" ) const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/test" -const OCIVERSION = "v2.0" const OCIHOST = "alias" var _ = Describe("Method", func() { @@ -47,19 +43,10 @@ var _ = Describe("Method", func() { It("accesses artefact", func() { env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) + OCIManifest1(env) }) - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + FakeOCIRepo(env, OCIPATH, OCIHOST) spec := ociartefact.New(oci.StandardOCIRef(OCIHOST+".alias", OCINAMESPACE, OCIVERSION)) diff --git a/pkg/contexts/ocm/accessmethods/ociblob/method_test.go b/pkg/contexts/ocm/accessmethods/ociblob/method_test.go index 2ad79d230..ca590be92 100644 --- a/pkg/contexts/ocm/accessmethods/ociblob/method_test.go +++ b/pkg/contexts/ocm/accessmethods/ociblob/method_test.go @@ -17,22 +17,18 @@ package ociblob_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/env" . "github.com/open-component-model/ocm/pkg/env/builder" "github.com/open-component-model/ocm/pkg/common/accessio" - "github.com/open-component-model/ocm/pkg/common/accessobj" "github.com/open-component-model/ocm/pkg/contexts/oci/artdesc" "github.com/open-component-model/ocm/pkg/contexts/oci/grammar" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociblob" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/mime" ) const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/test" -const OCIVERSION = "v2.0" const OCIHOST = "alias" var _ = Describe("Method", func() { @@ -49,19 +45,10 @@ var _ = Describe("Method", func() { It("accesses artefact", func() { var desc *artdesc.Descriptor env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - desc = env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) + desc = OCIManifest1(env) }) - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + FakeOCIRepo(env, OCIPATH, OCIHOST) spec := ociblob.New(OCIHOST+".alias"+grammar.RepositorySeparator+OCINAMESPACE, desc.Digest, "", -1) diff --git a/pkg/contexts/ocm/blobhandler/generic/ocirepo/upload_test.go b/pkg/contexts/ocm/blobhandler/generic/ocirepo/upload_test.go index f9fd878dd..8c47606a7 100644 --- a/pkg/contexts/ocm/blobhandler/generic/ocirepo/upload_test.go +++ b/pkg/contexts/ocm/blobhandler/generic/ocirepo/upload_test.go @@ -63,7 +63,10 @@ var _ = Describe("upload", func() { env = NewBuilder(tenv.NewEnvironment()) // fake OCI registry - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + spec, err := ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem())) + Expect(err).To(Succeed()) + env.OCIContext().SetAlias(OCIHOST, spec) + env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { env.Namespace(OCINAMESPACE, func() { env.Manifest(OCIVERSION, func() { diff --git a/pkg/contexts/ocm/digester/digesters/artefact/digester.go b/pkg/contexts/ocm/digester/digesters/artefact/digester.go index fb76b498f..e71540c2c 100644 --- a/pkg/contexts/ocm/digester/digesters/artefact/digester.go +++ b/pkg/contexts/ocm/digester/digesters/artefact/digester.go @@ -70,6 +70,7 @@ func (d *Digester) DetermineDigest(reftyp string, acc cpi.AccessMethod, preferre return nil, err } defer r.Close() + var reader io.Reader = r if strings.HasSuffix(mime, "+gzip") { reader, err = gzip.NewReader(reader) @@ -78,20 +79,37 @@ func (d *Digester) DetermineDigest(reftyp string, acc cpi.AccessMethod, preferre } } tr := tar.NewReader(reader) + + var desc *cpi.DigestDescriptor + oci := false + layout := false for { header, err := tr.Next() if err != nil { if errors.Is(err, io.EOF) { - err = fmt.Errorf("descriptor not found in archive") - return nil, errors.ErrInvalidWrap(err, "artefact archive") + if oci { + if layout { + return desc, nil + } else { + err = fmt.Errorf("oci-layout not found") + } + } else { + err = fmt.Errorf("descriptor not found in archive") + } } - return nil, err + return nil, errors.ErrInvalidWrap(err, "artefact archive") } switch header.Typeflag { case tar.TypeDir: case tar.TypeReg: - if header.Name == artefactset.ArtefactSetDescriptorFileName { + switch header.Name { + case artefactset.OCILayouFileName: + layout = true + case artefactset.OCIArtefactSetDescriptorFileName: + oci = true + fallthrough + case artefactset.ArtefactSetDescriptorFileName: data, err := io.ReadAll(tr) if err != nil { return nil, fmt.Errorf("unable to read descriptor from archive: %w", err) @@ -110,7 +128,10 @@ func (d *Digester) DetermineDigest(reftyp string, acc cpi.AccessMethod, preferre if digest.Digest(main).Algorithm() != digest.Algorithm(d.GetType().HashAlgorithm) { return nil, nil } - return cpi.NewDigestDescriptor(digest.Digest(main).Hex(), d.GetType()), nil + desc = cpi.NewDigestDescriptor(digest.Digest(main).Hex(), d.GetType()) + if !oci { + return desc, nil + } } } } diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index 4c38f85f6..948d6d249 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -42,7 +42,11 @@ var _ support.ComponentVersionContainer = (*ComponentArchive)(nil) // New returns a new representation based element. func New(ctx cpi.Context, acc accessobj.AccessMode, fs vfs.FileSystem, setup accessobj.Setup, closer accessobj.Closer, mode vfs.FileMode) (*ComponentArchive, error) { obj, err := accessobj.NewAccessObject(accessObjectInfo, acc, fs, setup, closer, mode) - return _Wrap(ctx, obj, NewRepositorySpec(acc, ""), err) + if err != nil { + return nil, err + } + spec, err := NewRepositorySpec(acc, "") + return _Wrap(ctx, obj, spec, err) } func _Wrap(ctx cpi.Context, obj *accessobj.AccessObject, spec *RepositorySpec, err error) (*ComponentArchive, error) { diff --git a/pkg/contexts/ocm/repositories/comparch/format.go b/pkg/contexts/ocm/repositories/comparch/format.go index d0f9743ab..4ab299115 100644 --- a/pkg/contexts/ocm/repositories/comparch/format.go +++ b/pkg/contexts/ocm/repositories/comparch/format.go @@ -32,7 +32,7 @@ const ComponentDescriptorFileName = compdesc.ComponentDescriptorFileName // BlobsDirectoryName is the name of the blob directory in the tar. const BlobsDirectoryName = "blobs" -var accessObjectInfo = &accessobj.AccessObjectInfo{ +var accessObjectInfo = &accessobj.DefaultAccessObjectInfo{ DescriptorFileName: ComponentDescriptorFileName, ObjectTypeName: "artefactset", ElementDirectoryName: BlobsDirectoryName, @@ -90,13 +90,13 @@ func GetFormat(name accessio.FileFormat) FormatHandler { //////////////////////////////////////////////////////////////////////////////// func Open(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) { - o, create, err := accessobj.HandleAccessMode(acc, path, opts...) + o, create, err := accessobj.HandleAccessMode(acc, path, nil, opts...) if err != nil { return nil, err } - h, ok := fileFormats[*o.FileFormat] + h, ok := fileFormats[*o.GetFileFormat()] if !ok { - return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.FileFormat.String()) + return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.GetFileFormat().String()) } if create { return h.Create(ctx, path, o, mode) @@ -105,10 +105,14 @@ func Open(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileM } func Create(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) { - o := accessio.AccessOptions(opts...).DefaultFormat(accessio.FormatDirectory) - h, ok := fileFormats[*o.FileFormat] + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return nil, err + } + o.DefaultFormat(accessio.FormatDirectory) + h, ok := fileFormats[*o.GetFileFormat()] if !ok { - return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.FileFormat.String()) + return nil, errors.ErrUnknown(accessobj.KIND_FILEFORMAT, o.GetFileFormat().String()) } return h.Create(ctx, path, o, mode) } @@ -117,12 +121,20 @@ func Create(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.Fil func (h *formatHandler) Open(ctx cpi.Context, acc accessobj.AccessMode, path string, opts accessio.Options) (*Object, error) { obj, err := h.FormatHandler.Open(accessObjectInfo, acc, path, opts) - return _Wrap(ctx, obj, NewRepositorySpec(acc, path, opts), err) + if err != nil { + return nil, err + } + spec, err := NewRepositorySpec(acc, path, opts) + return _Wrap(ctx, obj, spec, err) } func (h *formatHandler) Create(ctx cpi.Context, path string, opts accessio.Options, mode vfs.FileMode) (*Object, error) { obj, err := h.FormatHandler.Create(accessObjectInfo, path, opts, mode) - return _Wrap(ctx, obj, NewRepositorySpec(accessobj.ACC_CREATE, path, opts), err) + if err != nil { + return nil, err + } + spec, err := NewRepositorySpec(accessobj.ACC_CREATE, path, opts) + return _Wrap(ctx, obj, spec, err) } // WriteToFilesystem writes the current object to a filesystem. diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 19d31a1f5..e24492af6 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -35,8 +35,8 @@ type Repository struct { var _ cpi.Repository = (*Repository)(nil) func NewRepository(ctx cpi.Context, s *RepositorySpec) (*Repository, error) { - if s.PathFileSystem == nil { - s.PathFileSystem = vfsattr.Get(ctx) + if s.GetPathFileSystem() == nil { + s.SetPathFileSystem(vfsattr.Get(ctx)) } a, err := Open(ctx, s.AccessMode, s.FilePath, 0o700, s) if err != nil { diff --git a/pkg/contexts/ocm/repositories/comparch/type.go b/pkg/contexts/ocm/repositories/comparch/type.go index 986781572..7299183d3 100644 --- a/pkg/contexts/ocm/repositories/comparch/type.go +++ b/pkg/contexts/ocm/repositories/comparch/type.go @@ -51,14 +51,17 @@ var ( ) // NewRepositorySpec creates a new RepositorySpec. -func NewRepositorySpec(acc accessobj.AccessMode, filePath string, opts ...accessio.Option) *RepositorySpec { - o := accessio.AccessOptions(opts...) +func NewRepositorySpec(acc accessobj.AccessMode, filePath string, opts ...accessio.Option) (*RepositorySpec, error) { + o, err := accessio.AccessOptions(nil, opts...) + if err != nil { + return nil, err + } return &RepositorySpec{ ObjectVersionedType: runtime.NewVersionedObjectType(Type), FilePath: filePath, Options: o, AccessMode: acc, - } + }, nil } func (a *RepositorySpec) IsIntermediate() bool { @@ -74,8 +77,9 @@ func (a *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentia } func (a *RepositorySpec) AsUniformSpec(cpi.Context) cpi.UniformRepositorySpec { - opts := a.Options.Default() - p, err := vfs.Canonical(opts.PathFileSystem, a.FilePath, false) + opts := &accessio.StandardOptions{} + opts.Default() + p, err := vfs.Canonical(opts.GetPathFileSystem(), a.FilePath, false) if err != nil { return cpi.UniformRepositorySpec{Type: a.GetKind(), SubPath: a.FilePath} } diff --git a/pkg/contexts/ocm/repositories/comparch/uniform.go b/pkg/contexts/ocm/repositories/comparch/uniform.go index 23522992e..352979886 100644 --- a/pkg/contexts/ocm/repositories/comparch/uniform.go +++ b/pkg/contexts/ocm/repositories/comparch/uniform.go @@ -62,5 +62,5 @@ func (h *repospechandler) MapReference(ctx cpi.Context, u *cpi.UniformRepository if create { mode |= accessobj.ACC_CREATE } - return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)), nil + return NewRepositorySpec(mode, path, accessio.FileFormatForType(u.Type), accessio.PathFileSystem(fs)) } diff --git a/pkg/contexts/ocm/repositories/ctf/type.go b/pkg/contexts/ocm/repositories/ctf/type.go index 153b58db1..bef0e8262 100644 --- a/pkg/contexts/ocm/repositories/ctf/type.go +++ b/pkg/contexts/ocm/repositories/ctf/type.go @@ -24,9 +24,12 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg" ) -func NewRepositorySpec(acc accessobj.AccessMode, path string, opts ...accessio.Option) *genericocireg.RepositorySpec { - spec := ctf.NewRepositorySpec(acc, path, opts...) - return genericocireg.NewRepositorySpec(spec, nil) +func NewRepositorySpec(acc accessobj.AccessMode, path string, opts ...accessio.Option) (*genericocireg.RepositorySpec, error) { + spec, err := ctf.NewRepositorySpec(acc, path, opts...) + if err != nil { + return nil, err + } + return genericocireg.NewRepositorySpec(spec, nil), nil } func Open(ctx cpi.Context, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (cpi.Repository, error) { diff --git a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go index 7144e3940..0e5311cb6 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go @@ -59,7 +59,8 @@ var _ = Describe("component repository mapping", func() { Expect(err).To(Succeed()) tempfs = t - ocispec = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory) + ocispec, err = ctf.NewRepositorySpec(accessobj.ACC_CREATE, "test", accessio.PathFileSystem(tempfs), accessobj.FormatDirectory) + Expect(err).To(Succeed()) spec = genericocireg.NewRepositorySpec(ocispec, nil) }) @@ -155,7 +156,9 @@ var _ = Describe("component repository mapping", func() { ctx := ocm.WithBlobHandlers(ocm.DefaultBlobHandlers().Copy().Register(ocirepo.NewArtefactHandler(base), cpi.ForMimeType(mime))).New() // create artefactset - r, err := artefactset.FormatTGZ.Create("test.tgz", accessio.AccessOptions(accessio.PathFileSystem(tempfs)), 0700) + opts, err := accessio.AccessOptions(nil, accessio.PathFileSystem(tempfs)) + Expect(err).To(Succeed()) + r, err := artefactset.FormatTGZ.Create("test.tgz", opts, 0700) Expect(err).To(Succeed()) testhelper.DefaultManifestFill(r) r.Annotate(artefactset.MAINARTEFACT_ANNOTATION, "sha256:"+testhelper.DIGEST_MANIFEST) diff --git a/pkg/contexts/ocm/signing/signing_test.go b/pkg/contexts/ocm/signing/signing_test.go index e4ac6df96..0c575c133 100644 --- a/pkg/contexts/ocm/signing/signing_test.go +++ b/pkg/contexts/ocm/signing/signing_test.go @@ -17,6 +17,7 @@ package signing_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/contexts/ocm/signing" . "github.com/open-component-model/ocm/pkg/env/builder" . "github.com/open-component-model/ocm/pkg/testutils" @@ -25,7 +26,6 @@ import ( "github.com/open-component-model/ocm/pkg/common/accessobj" "github.com/open-component-model/ocm/pkg/contexts/datacontext" "github.com/open-component-model/ocm/pkg/contexts/oci" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/signingattr" @@ -47,9 +47,6 @@ const COMPONENTA = "github.com/mandelsoft/test" const COMPONENTB = "github.com/mandelsoft/ref" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/value" -const OCINAMESPACE2 = "ocm/ref" -const OCIVERSION = "v2.0" const OCIHOST = "alias" const SIGNATURE = "test" @@ -69,29 +66,11 @@ var _ = Describe("access method", func() { Context("valid", func() { BeforeEach(func() { - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + FakeOCIRepo(env, OCIPATH, OCIHOST) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) - env.Namespace(OCINAMESPACE2, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "otherlayer") - }) - }) - }) + OCIManifest1(env) + OCIManifest2(env) }) env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { diff --git a/pkg/contexts/ocm/transfer/transferhandler/spiff/handler_test.go b/pkg/contexts/ocm/transfer/transferhandler/spiff/handler_test.go index 468f5bb0d..44c51fa11 100644 --- a/pkg/contexts/ocm/transfer/transferhandler/spiff/handler_test.go +++ b/pkg/contexts/ocm/transfer/transferhandler/spiff/handler_test.go @@ -16,9 +16,11 @@ package spiff_test import ( "encoding/json" + "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/env" . "github.com/open-component-model/ocm/pkg/env/builder" @@ -27,7 +29,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/oci" "github.com/open-component-model/ocm/pkg/contexts/oci/artdesc" "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" @@ -47,9 +48,6 @@ const COMPONENT = "github.com/mandelsoft/test" const COMPONENT2 = "github.com/mandelsoft/test2" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "ocm/value" -const OCINAMESPACE2 = "ocm/ref" -const OCIVERSION = "v2.0" const OCIHOST = "alias" const script1 = ` @@ -130,29 +128,12 @@ var _ = Describe("Transfer handler", func() { BeforeEach(func() { env = NewBuilder(NewEnvironment()) - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + + FakeOCIRepo(env, OCIPATH, OCIHOST) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - ldesc = env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) - env.Namespace(OCINAMESPACE2, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "otherlayer") - }) - }) - }) + ldesc = OCIManifest1(env) + OCIManifest2(env) }) env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { @@ -229,11 +210,15 @@ process: (( (*(rules[mode] || rules.default)).process )) data, err := json.Marshal(comp.GetDescriptor().Resources[2].Access) Expect(err).To(Succeed()) - Expect(string(data)).To(Equal("{\"localReference\":\"sha256:f6a519fb1d0c8cef5e8d7811911fc7cb170462bbce19d6df067dae041250de7f\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE2 + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) + fmt.Printf("%s\n", string(data)) + hash := HashManifest2(artefactset.DefaultArtefactSetDescriptorFileName) + Expect(string(data)).To(Equal("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE2 + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) data, err = json.Marshal(comp.GetDescriptor().Resources[1].Access) Expect(err).To(Succeed()) - Expect(string(data)).To(Equal("{\"localReference\":\"sha256:018520b2b249464a83e370619f544957b7936dd974468a128545eab88a0f53ed\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) + fmt.Printf("%s\n", string(data)) + hash = HashManifest1(artefactset.DefaultArtefactSetDescriptorFileName) + Expect(string(data)).To(Equal("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) racc, err := comp.GetResourceByIndex(1) Expect(err).To(Succeed()) @@ -281,7 +266,8 @@ process: (( (*(rules[mode] || rules.default)).process )) // index 1: by value data, err = json.Marshal(comp.GetDescriptor().Resources[1].Access) Expect(err).To(Succeed()) - Expect(string(data)).To(Equal("{\"localReference\":\"sha256:018520b2b249464a83e370619f544957b7936dd974468a128545eab88a0f53ed\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) + hash := HashManifest1(artefactset.DefaultArtefactSetDescriptorFileName) + Expect(string(data)).To(Equal("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) racc, err := comp.GetResourceByIndex(1) Expect(err).To(Succeed()) diff --git a/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go b/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go index 3cffba380..932adb9ca 100644 --- a/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go +++ b/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go @@ -16,9 +16,11 @@ package standard_test import ( "encoding/json" + "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/open-component-model/ocm/pkg/contexts/oci/testhelper" . "github.com/open-component-model/ocm/pkg/env" . "github.com/open-component-model/ocm/pkg/env/builder" @@ -27,7 +29,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/oci" "github.com/open-component-model/ocm/pkg/contexts/oci/artdesc" "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/artefactset" - ctfoci "github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartefact" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/signingattr" @@ -50,8 +51,6 @@ const COMPONENT = "github.com/mandelsoft/test" const COMPONENT2 = "github.com/mandelsoft/test2" const OUT = "/tmp/res" const OCIPATH = "/tmp/oci" -const OCINAMESPACE = "oci/test" -const OCIVERSION = "v2.0" const OCIHOST = "alias" const SIGNATURE = "test" const SIGN_ALGO = rsa.Algorithm @@ -66,16 +65,7 @@ var _ = Describe("Transfer handler", func() { env.RSAKeyPair(SIGNATURE) env.OCICommonTransport(OCIPATH, accessio.FormatDirectory, func() { - env.Namespace(OCINAMESPACE, func() { - env.Manifest(OCIVERSION, func() { - env.Config(func() { - env.BlobStringData(mime.MIME_JSON, "{}") - }) - ldesc = env.Layer(func() { - env.BlobStringData(mime.MIME_TEXT, "manifestlayer") - }) - }) - }) + ldesc = OCIManifest1(env) }) env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { @@ -103,7 +93,7 @@ var _ = Describe("Transfer handler", func() { }) }) - env.OCIContext().SetAlias(OCIHOST, ctfoci.NewRepositorySpec(accessobj.ACC_READONLY, OCIPATH, accessio.PathFileSystem(env.FileSystem()))) + FakeOCIRepo(env, OCIPATH, OCIHOST) }) AfterEach(func() { @@ -132,7 +122,10 @@ var _ = Describe("Transfer handler", func() { Expect(len(comp.GetDescriptor().Resources)).To(Equal(2)) data, err := json.Marshal(comp.GetDescriptor().Resources[1].Access) Expect(err).To(Succeed()) - Expect(string(data)).To(Equal("{\"localReference\":\"sha256:018520b2b249464a83e370619f544957b7936dd974468a128545eab88a0f53ed\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) + + fmt.Printf("%s\n", string(data)) + hash := HashManifest1(artefactset.DefaultArtefactSetDescriptorFileName) + Expect(string(data)).To(Equal("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}")) r, err := comp.GetResourceByIndex(1) Expect(err).To(Succeed()) diff --git a/pkg/env/env.go b/pkg/env/env.go index de2c58bb1..009f1a6f3 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -172,8 +172,9 @@ func NewEnvironment(opts ...Option) *Environment { var _ accessio.Option = (*Environment)(nil) -func (e *Environment) ApplyOption(options *accessio.Options) { - options.PathFileSystem = e.FileSystem() +func (e *Environment) ApplyOption(options accessio.Options) error { + options.SetPathFileSystem(e.FileSystem()) + return nil } func (e *Environment) OCMContext() ocm.Context {