From e0d07eb62d64d2108e0f364bd89dfad7d7cd57e0 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 7 Nov 2023 11:11:42 -0800 Subject: [PATCH 01/18] work --- pkg/contexts/oci/cpi/support/artifact.go | 1 - pkg/contexts/ocm/cpi/repocpi/base_c.go | 55 ++ pkg/contexts/ocm/cpi/repocpi/base_cv.go | 197 +++++ pkg/contexts/ocm/cpi/repocpi/blobcache.go | 85 ++ .../helperinterfaces.go} | 28 +- pkg/contexts/ocm/cpi/repocpi/view.go | 167 ++++ pkg/contexts/ocm/cpi/repocpi/view_c.go | 179 ++++ .../ocm/cpi/{view.go => repocpi/view_cv.go} | 810 ++++-------------- .../ocm/cpi/support/compversaccess.go | 137 --- pkg/contexts/ocm/cpi/support/doc.go | 19 - pkg/contexts/ocm/cpi/support/error.go | 59 -- pkg/contexts/ocm/interface.go | 3 +- .../comparch/accessmethod_localfs.go | 6 +- .../repositories/comparch/componentarchive.go | 15 +- .../ocm/repositories/comparch/repository.go | 32 +- .../ocm/repositories/ctf/repo_test.go | 59 ++ .../repositories/genericocireg/component.go | 16 +- .../genericocireg/componentversion.go | 14 +- .../repositories/genericocireg/repo_test.go | 11 +- .../repositories/genericocireg/repository.go | 14 +- .../ocm/repositories/virtual/component.go | 16 +- .../repositories/virtual/componentversion.go | 14 +- .../ocm/repositories/virtual/repository.go | 11 +- pkg/refmgmt/refcloser.go | 1 + pkg/refmgmt/refmgmt.go | 11 +- 25 files changed, 1010 insertions(+), 950 deletions(-) create mode 100644 pkg/contexts/ocm/cpi/repocpi/base_c.go create mode 100644 pkg/contexts/ocm/cpi/repocpi/base_cv.go create mode 100644 pkg/contexts/ocm/cpi/repocpi/blobcache.go rename pkg/contexts/ocm/cpi/{support/container.go => repocpi/helperinterfaces.go} (57%) create mode 100644 pkg/contexts/ocm/cpi/repocpi/view.go create mode 100644 pkg/contexts/ocm/cpi/repocpi/view_c.go rename pkg/contexts/ocm/cpi/{view.go => repocpi/view_cv.go} (53%) delete mode 100644 pkg/contexts/ocm/cpi/support/compversaccess.go delete mode 100644 pkg/contexts/ocm/cpi/support/doc.go delete mode 100644 pkg/contexts/ocm/cpi/support/error.go diff --git a/pkg/contexts/oci/cpi/support/artifact.go b/pkg/contexts/oci/cpi/support/artifact.go index 016bfe213b..131f861600 100644 --- a/pkg/contexts/oci/cpi/support/artifact.go +++ b/pkg/contexts/oci/cpi/support/artifact.go @@ -38,7 +38,6 @@ func NewArtifactForBlob(container NamespaceAccessImpl, blob blobaccess.BlobAcces if err != nil { return nil, err } - return newArtifact(container, state, closer...) } diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go new file mode 100644 index 0000000000..54fe929977 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "io" + + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" +) + +// ComponentAccessImpl is the provider implementation +// interface for component versions. +type ComponentAccessImpl interface { + resource.ResourceImplementation[cpi.ComponentAccess] + internal.ComponentAccessImpl + + IsReadOnly() bool + GetName() string + + IsOwned(access cpi.ComponentVersionAccess) bool + + AddVersion(cv cpi.ComponentVersionAccess) error +} + +type _ComponentAccessImplBase = resource.ResourceImplBase[cpi.ComponentAccess] + +type ComponentAccessImplBase struct { + *_ComponentAccessImplBase + ctx cpi.Context + name string +} + +func NewComponentAccessImplBase(ctx cpi.Context, name string, repo RepositoryViewManager, closer ...io.Closer) (*ComponentAccessImplBase, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentAccess](repo, closer...) + if err != nil { + return nil, err + } + return &ComponentAccessImplBase{ + _ComponentAccessImplBase: base, + ctx: ctx, + name: name, + }, nil +} + +func (b *ComponentAccessImplBase) GetContext() cpi.Context { + return b.ctx +} + +func (b *ComponentAccessImplBase) GetName() string { + return b.name +} diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go new file mode 100644 index 0000000000..a2a0058c6c --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -0,0 +1,197 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "io" + + "github.com/open-component-model/ocm/pkg/common" + "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" +) + +// here, we define the common implementation agnostic parts +// for component version objects referred to by a ComponentVersionView. + +// ComponentVersionAccessImpl is the provider implementation +// interface for component versions. +type ComponentVersionAccessImpl interface { + GetContext() cpi.Context + SetImplementation(base ComponentVersionAccessBase) + GetParentViewManager() ComponentAccessViewManager + + Repository() cpi.Repository + + IsReadOnly() bool + + GetDescriptor() *compdesc.ComponentDescriptor + + AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) + GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string + + Update() error + + BlobContainer + io.Closer +} + +type _componentVersionAccessImplBase = resource.ResourceImplBase[cpi.ComponentVersionAccess] + +// componentVersionAccessBase is the counterpart to views, all views +// created by Dup calls use this base object to work on. +// Besides some functionality covered by view objects these base objects +// implement provider-agnostic parts of the ComponentVersionAccess API. +type componentVersionAccessBase struct { + *_componentVersionAccessImplBase + ctx cpi.Context + name string + version string + + blobcache BlobCache + + lazy bool + directAccess bool + persistent bool + discardChanges bool + + impl ComponentVersionAccessImpl +} + +var _ ComponentVersionAccessBase = (*componentVersionAccessBase)(nil) + +func NewComponentVersionAccessBase(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBase, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess](impl.GetParentViewManager(), closer...) + if err != nil { + return nil, err + } + b := &componentVersionAccessBase{ + _componentVersionAccessImplBase: base, + ctx: impl.GetContext(), + name: name, + version: version, + blobcache: NewBlobCache(), + lazy: lazy, + persistent: persistent, + directAccess: direct, + impl: impl, + } + impl.SetImplementation(b) + return b, nil +} + +func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersionAccess) (T, error) { + var _nil T + + impl, err := GetComponentVersionAccessBase(cv) + if err != nil { + return _nil, err + } + if mine, ok := impl.(*componentVersionAccessBase); ok { + cont, ok := mine.impl.(T) + if ok { + return cont, nil + } + return _nil, errors.Newf("non-matching component version implementation %T", mine.impl) + } + return _nil, errors.Newf("non-matching component version implementation %T", impl) +} + +func (b *componentVersionAccessBase) Close() error { + list := errors.ErrListf("closing component version %s", common.VersionedElementKey(b)) + refmgmt.AllocLog.Trace("closing component version base", "name", common.VersionedElementKey(b)) + list.Add(b.impl.Close()) + list.Add(b._componentVersionAccessImplBase.Close()) + list.Add(b.blobcache.Clear()) + refmgmt.AllocLog.Trace("closed component version base", "name", common.VersionedElementKey(b)) + return list.Result() +} + +func (b *componentVersionAccessBase) GetContext() cpi.Context { + return b.ctx +} + +func (b *componentVersionAccessBase) GetName() string { + return b.name +} + +func (b *componentVersionAccessBase) GetVersion() string { + return b.version +} + +func (b *componentVersionAccessBase) GetBlobCache() BlobCache { + return b.blobcache +} + +func (b *componentVersionAccessBase) EnablePersistence() bool { + if b.discardChanges { + return false + } + b.persistent = true + b.GetStorageContext() + return true +} + +func (b *componentVersionAccessBase) IsPersistent() bool { + return b.persistent +} + +func (b *componentVersionAccessBase) UseDirectAccess() bool { + return b.directAccess +} + +func (b *componentVersionAccessBase) DiscardChanges() { + b.discardChanges = true +} + +func (b *componentVersionAccessBase) Repository() cpi.Repository { + return b.impl.Repository() +} + +func (b *componentVersionAccessBase) IsReadOnly() bool { + return b.impl.IsReadOnly() +} + +//////////////////////////////////////////////////////////////////////////////// +// with access to actual view + +func (b *componentVersionAccessBase) AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) { + return b.impl.AccessMethod(acc, cv) +} + +func (b *componentVersionAccessBase) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { + return b.impl.GetInexpensiveContentVersionIdentity(acc, cv) +} + +func (b *componentVersionAccessBase) GetDescriptor() *compdesc.ComponentDescriptor { + return b.impl.GetDescriptor() +} + +func (b *componentVersionAccessBase) GetStorageContext() cpi.StorageContext { + return b.impl.GetStorageContext() +} + +func (b *componentVersionAccessBase) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { + return b.impl.AddBlobFor(blob, refName, global) +} + +func (b *componentVersionAccessBase) ShouldUpdate(final bool) bool { + if b.discardChanges { + return false + } + if final { + return b.persistent + } + return !b.lazy && b.directAccess && b.persistent +} + +func (b *componentVersionAccessBase) Update(final bool) error { + if b.ShouldUpdate(final) { + return b.impl.Update() + } + return nil +} diff --git a/pkg/contexts/ocm/cpi/repocpi/blobcache.go b/pkg/contexts/ocm/cpi/repocpi/blobcache.go new file mode 100644 index 0000000000..b324c97bd1 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/blobcache.go @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "sync" + + "github.com/open-component-model/ocm/pkg/blobaccess" + "github.com/open-component-model/ocm/pkg/errors" +) + +type ( + BlobCacheEntry = blobaccess.BlobAccess + BlobCacheKey = interface{} +) + +type BlobCache interface { + // AddBlobFor stores blobs for added blobs not yet accessible + // by generated access method until version is finally added. + AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error + + // GetBlobFor retrieves the original blob access for + // a given access specification. + GetBlobFor(acc BlobCacheKey) BlobCacheEntry + + RemoveBlobFor(acc BlobCacheKey) + Clear() error +} + +type blobCache struct { + lock sync.Mutex + blobcache map[BlobCacheKey]BlobCacheEntry +} + +func NewBlobCache() BlobCache { + return &blobCache{ + blobcache: map[BlobCacheKey]BlobCacheEntry{}, + } +} + +func (c *blobCache) RemoveBlobFor(acc BlobCacheKey) { + c.lock.Lock() + defer c.lock.Unlock() + if b := c.blobcache[acc]; b != nil { + b.Close() + delete(c.blobcache, acc) + } +} + +func (c *blobCache) AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error { + if s, ok := acc.(string); ok && s == "" { + return errors.ErrInvalid("blob key") + } + c.lock.Lock() + defer c.lock.Unlock() + + if c.blobcache[acc] == nil { + l, err := blob.Dup() + if err != nil { + return err + } + c.blobcache[acc] = l + } + return nil +} + +func (c *blobCache) GetBlobFor(acc BlobCacheKey) BlobCacheEntry { + c.lock.Lock() + defer c.lock.Unlock() + + return c.blobcache[acc] +} + +func (c *blobCache) Clear() error { + list := errors.ErrList() + c.lock.Lock() + defer c.lock.Unlock() + for _, b := range c.blobcache { + list.Add(b.Close()) + } + c.blobcache = map[BlobCacheKey]BlobCacheEntry{} + return list.Result() +} diff --git a/pkg/contexts/ocm/cpi/support/container.go b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go similarity index 57% rename from pkg/contexts/ocm/cpi/support/container.go rename to pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go index df9a1bc895..47cbba36e1 100644 --- a/pkg/contexts/ocm/cpi/support/container.go +++ b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go @@ -1,15 +1,11 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. // // SPDX-License-Identifier: Apache-2.0 -package support +package repocpi import ( - "io" - - "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/refmgmt" ) // BlobContainer is the interface for an element capable to store blobs. @@ -30,23 +26,3 @@ type BlobContainer interface { // This is the direct technical storage, without caring about any handler. AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) } - -// ComponentVersionContainer is the interface of an element hosting a component version. -type ComponentVersionContainer interface { - SetImplementation(impl ComponentVersionAccessImpl) - - GetParentViewManager() cpi.ComponentAccessViewManager - - GetContext() cpi.Context - Repository() cpi.Repository - - IsReadOnly() bool - Update() error - - GetDescriptor() *compdesc.ComponentDescriptor - BlobContainer - AccessMethod(a cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) - GetInexpensiveContentVersionIdentity(a cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string - - io.Closer -} diff --git a/pkg/contexts/ocm/cpi/repocpi/view.go b/pkg/contexts/ocm/cpi/repocpi/view.go new file mode 100644 index 0000000000..86931ed7fa --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/view.go @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "fmt" + "io" + + "github.com/open-component-model/ocm/pkg/contexts/credentials" + cpi2 "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" + "github.com/open-component-model/ocm/pkg/utils" +) + +// View objects are the user facing generic implementations of the context interfaces. +// They are responsible to handle the reference counting and use +// shared implementations objects for th concrete type-specific implementations. +// Additionally, they are used to implement interface functionality which is +// common to all implementations and NOT dependent on the backend system technology. + +var ( + ErrClosed = resource.ErrClosed + ErrTempVersion = fmt.Errorf("temporary component version cannot be updated") +) + +//////////////////////////////////////////////////////////////////////////////// + +type _RepositoryView interface { + resource.ResourceViewInt[cpi2.Repository] // here you have to redeclare +} + +type RepositoryViewManager = resource.ViewManager[cpi2.Repository] // here you have to use an alias + +type RepositoryImpl interface { + resource.ResourceImplementation[cpi2.Repository] + internal.RepositoryImpl +} + +type _RepositoryImplBase = resource.ResourceImplBase[cpi2.Repository] + +type RepositoryImplBase struct { + _RepositoryImplBase + ctx cpi2.Context +} + +func (b *RepositoryImplBase) GetContext() cpi2.Context { + return b.ctx +} + +func NewRepositoryImplBase(ctx cpi2.Context, closer ...io.Closer) *RepositoryImplBase { + base, _ := resource.NewResourceImplBase[cpi2.Repository, io.Closer](nil, closer...) + return &RepositoryImplBase{ + _RepositoryImplBase: *base, + ctx: ctx, + } +} + +type repositoryView struct { + _RepositoryView + impl RepositoryImpl +} + +var ( + _ cpi2.Repository = (*repositoryView)(nil) + _ credentials.ConsumerIdentityProvider = (*repositoryView)(nil) + _ utils.Unwrappable = (*repositoryView)(nil) +) + +func GetRepositoryImplementation(n cpi2.Repository) (RepositoryImpl, error) { + if v, ok := n.(*repositoryView); ok { + return v.impl, nil + } + return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) +} + +func repositoryViewCreator(i RepositoryImpl, v resource.CloserView, d RepositoryViewManager) cpi2.Repository { + return &repositoryView{ + _RepositoryView: resource.NewView[cpi2.Repository](v, d), + impl: i, + } +} + +// NewNoneRefRepositoryView provides a repository reflecting the state of the +// view manager without holding an additional reference. +func NewNoneRefRepositoryView(i RepositoryImpl) cpi2.Repository { + return &repositoryView{ + _RepositoryView: resource.NewView[cpi2.Repository](resource.NewNonRefView[cpi2.Repository](i), i), + impl: i, + } +} + +func NewRepository(impl RepositoryImpl, name ...string) cpi2.Repository { + return resource.NewResource[cpi2.Repository](impl, repositoryViewCreator, utils.OptionalDefaulted("OCM repo", name...), true) +} + +func (r *repositoryView) Unwrap() interface{} { + return r.impl +} + +func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { + return credentials.GetProvidedConsumerId(r.impl, uctx...) +} + +func (r *repositoryView) GetIdentityMatcher() string { + return credentials.GetProvidedIdentityMatcher(r.impl) +} + +func (r *repositoryView) GetSpecification() cpi2.RepositorySpec { + return r.impl.GetSpecification() +} + +func (r *repositoryView) GetContext() cpi2.Context { + return r.impl.GetContext() +} + +func (r *repositoryView) ComponentLister() cpi2.ComponentLister { + return r.impl.ComponentLister() +} + +func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) { + err = r.Execute(func() error { + ok, err = r.impl.ExistsComponentVersion(name, version) + return err + }) + return ok, err +} + +func (r *repositoryView) LookupComponentVersion(name string, version string) (acc cpi2.ComponentVersionAccess, err error) { + err = r.Execute(func() error { + acc, err = r.impl.LookupComponentVersion(name, version) + return err + }) + return acc, err +} + +func (r *repositoryView) LookupComponent(name string) (acc cpi2.ComponentAccess, err error) { + err = r.Execute(func() error { + acc, err = r.impl.LookupComponent(name) + return err + }) + return acc, err +} + +func (r *repositoryView) NewComponentVersion(comp, vers string, overrides ...bool) (cpi2.ComponentVersionAccess, error) { + c, err := refmgmt.ToLazy(r.LookupComponent(comp)) + if err != nil { + return nil, err + } + defer c.Close() + + return c.NewVersion(vers, overrides...) +} + +func (r *repositoryView) AddComponentVersion(cv cpi2.ComponentVersionAccess, overrides ...bool) error { + c, err := refmgmt.ToLazy(r.LookupComponent(cv.GetName())) + if err != nil { + return err + } + defer c.Close() + + return c.AddVersion(cv, overrides...) +} diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go new file mode 100644 index 0000000000..1ea93335c1 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -0,0 +1,179 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "fmt" + + "github.com/open-component-model/ocm/pkg/common/accessio" + "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" + "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/finalizer" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" + "github.com/open-component-model/ocm/pkg/utils" +) + +type _componentAccessView interface { + resource.ResourceViewInt[cpi.ComponentAccess] // here you have to redeclare +} + +type ComponentAccessViewManager = resource.ViewManager[cpi.ComponentAccess] // here you have to use an alias + +type ComponentAccessBase interface { + resource.ResourceImplementation[cpi.ComponentAccess] + internal.ComponentAccessImpl + + IsReadOnly() bool + GetName() string + + IsOwned(access cpi.ComponentVersionAccess) bool + + AddVersion(cv cpi.ComponentVersionAccess) error +} + +type componentAccessView struct { + _componentAccessView + base ComponentAccessBase +} + +var ( + _ cpi.ComponentAccess = (*componentAccessView)(nil) + _ utils.Unwrappable = (*componentAccessView)(nil) +) + +func GetComponentAccessBase(n cpi.ComponentAccess) (ComponentAccessBase, error) { + if v, ok := n.(*componentAccessView); ok { + return v.base, nil + } + return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", n)) +} + +func componentAccessViewCreator(i ComponentAccessBase, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess { + return &componentAccessView{ + _componentAccessView: resource.NewView[cpi.ComponentAccess](v, d), + base: i, + } +} + +func NewComponentAccess(impl ComponentAccessBase, kind ...string) cpi.ComponentAccess { + return resource.NewResource[cpi.ComponentAccess](impl, componentAccessViewCreator, fmt.Sprintf("%s %s", utils.OptionalDefaulted("component", kind...), impl.GetName()), true) +} + +func (c *componentAccessView) Unwrap() interface{} { + return c.base +} + +func (c *componentAccessView) GetContext() cpi.Context { + return c.base.GetContext() +} + +func (c *componentAccessView) GetName() string { + return c.base.GetName() +} + +func (c *componentAccessView) ListVersions() (list []string, err error) { + err = c.Execute(func() error { + list, err = c.base.ListVersions() + return err + }) + return list, err +} + +func (c *componentAccessView) LookupVersion(version string) (acc cpi.ComponentVersionAccess, err error) { + err = c.Execute(func() error { + acc, err = c.base.LookupVersion(version) + return err + }) + return acc, err +} + +func (c *componentAccessView) AddVersion(acc cpi.ComponentVersionAccess, overrides ...bool) error { + if acc.GetName() != c.GetName() { + return errors.ErrInvalid("component name", acc.GetName()) + } + return c.Execute(func() error { + return c.addVersion(acc, overrides...) + }) +} + +func (c *componentAccessView) addVersion(acc cpi.ComponentVersionAccess, overrides ...bool) (ferr error) { + var finalize finalizer.Finalizer + defer finalize.FinalizeWithErrorPropagation(&ferr) + + ctx := acc.GetContext() + + impl, err := GetComponentVersionAccessBase(acc) + if err != nil { + return err + } + + var ( + d *compdesc.ComponentDescriptor + sel func(cpi.AccessSpec) bool + eff cpi.ComponentVersionAccess + ) + + opts := cpi.NewBlobUploadOptions() + + forcestore := c.base.IsOwned(acc) + if !forcestore { + // transfer all local blobs into a new owned version. + sel = func(spec cpi.AccessSpec) bool { return spec.IsLocal(ctx) } + + eff, err = c.base.NewVersion(acc.GetVersion(), overrides...) + if err != nil { + return err + } + finalize.With(func() error { + return eff.Close() + }) + impl, err = GetComponentVersionAccessBase(eff) + if err != nil { + return err + } + + d = eff.GetDescriptor() + *d = *acc.GetDescriptor().Copy() + } else { + // transfer composition blobs into local blobs + opts.UseNoDefaultIfNotSet = true + opts.BlobHandlerProvider = nil + sel = compose.Is + d = acc.GetDescriptor() + eff = acc + } + + err = setupLocalBlobs(ctx, "resource", acc, nil, impl, d.Resources, sel, forcestore, opts) + if err == nil { + err = setupLocalBlobs(ctx, "source", acc, nil, impl, d.Sources, sel, forcestore, opts) + } + if err != nil { + return err + } + + return c.base.AddVersion(eff) +} + +func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc cpi.ComponentVersionAccess, err error) { + err = c.Execute(func() error { + if c.base.IsReadOnly() { + return accessio.ErrReadOnly + } + acc, err = c.base.NewVersion(version, overrides...) + return err + }) + return acc, err +} + +func (c *componentAccessView) HasVersion(vers string) (ok bool, err error) { + err = c.Execute(func() error { + ok, err = c.base.HasVersion(vers) + return err + }) + return ok, err +} diff --git a/pkg/contexts/ocm/cpi/view.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go similarity index 53% rename from pkg/contexts/ocm/cpi/view.go rename to pkg/contexts/ocm/cpi/repocpi/view_cv.go index 490d294e3d..8a023ce1cd 100644 --- a/pkg/contexts/ocm/cpi/view.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -2,32 +2,30 @@ // // SPDX-License-Identifier: Apache-2.0 -package cpi +package repocpi import ( "encoding/json" "fmt" "io" "strconv" - "sync" + "github.com/open-component-model/ocm/pkg/finalizer" "github.com/opencontainers/go-digest" "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/common/accessio" - "github.com/open-component-model/ocm/pkg/contexts/credentials" - "github.com/open-component-model/ocm/pkg/contexts/oci/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor" "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/finalizer" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" "github.com/open-component-model/ocm/pkg/utils" @@ -40,426 +38,44 @@ import ( // Additionally, they are used to implement interface functionality which is // common to all implementations and NOT dependent on the backend system technology. -var ( - ErrClosed = resource.ErrClosed - ErrTempVersion = fmt.Errorf("temporary component version cannot be updated") -) - -//////////////////////////////////////////////////////////////////////////////// - -type _RepositoryView interface { - resource.ResourceViewInt[Repository] // here you have to redeclare -} - -type RepositoryViewManager = resource.ViewManager[Repository] // here you have to use an alias - -type RepositoryImpl interface { - resource.ResourceImplementation[Repository] - internal.RepositoryImpl -} - -type _RepositoryImplBase = resource.ResourceImplBase[Repository] - -type RepositoryImplBase struct { - _RepositoryImplBase - ctx Context -} - -func (b *RepositoryImplBase) GetContext() Context { - return b.ctx -} - -func NewRepositoryImplBase(ctx Context, closer ...io.Closer) *RepositoryImplBase { - base, _ := resource.NewResourceImplBase[Repository, io.Closer](nil, closer...) - return &RepositoryImplBase{ - _RepositoryImplBase: *base, - ctx: ctx, - } -} - -type repositoryView struct { - _RepositoryView - impl RepositoryImpl -} - -var ( - _ Repository = (*repositoryView)(nil) - _ credentials.ConsumerIdentityProvider = (*repositoryView)(nil) - _ utils.Unwrappable = (*repositoryView)(nil) -) - -func GetRepositoryImplementation(n Repository) (RepositoryImpl, error) { - if v, ok := n.(*repositoryView); ok { - return v.impl, nil - } - return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) -} - -func repositoryViewCreator(i RepositoryImpl, v resource.CloserView, d RepositoryViewManager) Repository { - return &repositoryView{ - _RepositoryView: resource.NewView[Repository](v, d), - impl: i, - } -} +// here are the views implementing the user facing ComponentVersionAccess +// interface. -// NewNoneRefRepositoryView provides a repository reflecting the state of the -// view manager without holding an additional reference. -func NewNoneRefRepositoryView(i RepositoryImpl) Repository { - return &repositoryView{ - _RepositoryView: resource.NewView[Repository](resource.NewNonRefView[Repository](i), i), - impl: i, - } -} - -func NewRepository(impl RepositoryImpl, name ...string) Repository { - return resource.NewResource[Repository](impl, repositoryViewCreator, utils.OptionalDefaulted("OCM repo", name...), true) -} - -func (r *repositoryView) Unwrap() interface{} { - return r.impl +type _componentVersionAccessView interface { + resource.ResourceViewInt[cpi.ComponentVersionAccess] } -func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { - return credentials.GetProvidedConsumerId(r.impl, uctx...) -} - -func (r *repositoryView) GetIdentityMatcher() string { - return credentials.GetProvidedIdentityMatcher(r.impl) -} - -func (r *repositoryView) GetSpecification() RepositorySpec { - return r.impl.GetSpecification() -} - -func (r *repositoryView) GetContext() Context { - return r.impl.GetContext() -} - -func (r *repositoryView) ComponentLister() ComponentLister { - return r.impl.ComponentLister() -} - -func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) { - err = r.Execute(func() error { - ok, err = r.impl.ExistsComponentVersion(name, version) - return err - }) - return ok, err -} - -func (r *repositoryView) LookupComponentVersion(name string, version string) (acc ComponentVersionAccess, err error) { - err = r.Execute(func() error { - acc, err = r.impl.LookupComponentVersion(name, version) - return err - }) - return acc, err -} - -func (r *repositoryView) LookupComponent(name string) (acc ComponentAccess, err error) { - err = r.Execute(func() error { - acc, err = r.impl.LookupComponent(name) - return err - }) - return acc, err -} - -func (r *repositoryView) NewComponentVersion(comp, vers string, overrides ...bool) (ComponentVersionAccess, error) { - c, err := refmgmt.ToLazy(r.LookupComponent(comp)) - if err != nil { - return nil, err - } - defer c.Close() - - return c.NewVersion(vers, overrides...) -} - -func (r *repositoryView) AddComponentVersion(cv ComponentVersionAccess, overrides ...bool) error { - c, err := refmgmt.ToLazy(r.LookupComponent(cv.GetName())) - if err != nil { - return err - } - defer c.Close() - - return c.AddVersion(cv, overrides...) -} - -//////////////////////////////////////////////////////////////////////////////// - -type _ComponentAccessView interface { - resource.ResourceViewInt[ComponentAccess] // here you have to redeclare -} - -type ComponentAccessViewManager = resource.ViewManager[ComponentAccess] // here you have to use an alias - -type ComponentAccessImpl interface { - resource.ResourceImplementation[ComponentAccess] - internal.ComponentAccessImpl - - IsReadOnly() bool - GetName() string - - IsOwned(access ComponentVersionAccess) bool - - AddVersion(cv ComponentVersionAccess) error -} - -type _ComponentAccessImplBase = resource.ResourceImplBase[ComponentAccess] - -type ComponentAccessImplBase struct { - *_ComponentAccessImplBase - ctx Context - name string -} - -func NewComponentAccessImplBase(ctx Context, name string, repo RepositoryViewManager, closer ...io.Closer) (*ComponentAccessImplBase, error) { - base, err := resource.NewResourceImplBase[ComponentAccess](repo, closer...) - if err != nil { - return nil, err - } - return &ComponentAccessImplBase{ - _ComponentAccessImplBase: base, - ctx: ctx, - name: name, - }, nil -} - -func (b *ComponentAccessImplBase) GetContext() Context { - return b.ctx -} - -func (b *ComponentAccessImplBase) GetName() string { - return b.name -} - -type componentAccessView struct { - _ComponentAccessView - impl ComponentAccessImpl -} - -var ( - _ ComponentAccess = (*componentAccessView)(nil) - _ utils.Unwrappable = (*componentAccessView)(nil) -) - -func GetComponentAccessImplementation(n ComponentAccess) (ComponentAccessImpl, error) { - if v, ok := n.(*componentAccessView); ok { - return v.impl, nil - } - return nil, errors.ErrNotSupported("component implementation type", fmt.Sprintf("%T", n)) -} - -func componentAccessViewCreator(i ComponentAccessImpl, v resource.CloserView, d ComponentAccessViewManager) ComponentAccess { - return &componentAccessView{ - _ComponentAccessView: resource.NewView[ComponentAccess](v, d), - impl: i, - } -} - -func NewComponentAccess(impl ComponentAccessImpl, kind ...string) ComponentAccess { - return resource.NewResource[ComponentAccess](impl, componentAccessViewCreator, fmt.Sprintf("%s %s", utils.OptionalDefaulted("component", kind...), impl.GetName()), true) -} - -func (c *componentAccessView) Unwrap() interface{} { - return c.impl -} - -func (c *componentAccessView) GetContext() Context { - return c.impl.GetContext() -} - -func (c *componentAccessView) GetName() string { - return c.impl.GetName() -} - -func (c *componentAccessView) ListVersions() (list []string, err error) { - err = c.Execute(func() error { - list, err = c.impl.ListVersions() - return err - }) - return list, err -} - -func (c *componentAccessView) LookupVersion(version string) (acc ComponentVersionAccess, err error) { - err = c.Execute(func() error { - acc, err = c.impl.LookupVersion(version) - return err - }) - return acc, err -} - -func (c *componentAccessView) AddVersion(acc ComponentVersionAccess, overrides ...bool) error { - if acc.GetName() != c.GetName() { - return errors.ErrInvalid("component name", acc.GetName()) - } - return c.Execute(func() error { - return c.addVersion(acc, overrides...) - }) -} - -func (c *componentAccessView) addVersion(acc ComponentVersionAccess, overrides ...bool) (ferr error) { - var finalize finalizer.Finalizer - defer finalize.FinalizeWithErrorPropagation(&ferr) - - ctx := acc.GetContext() - - impl, err := GetComponentVersionAccessImplementation(acc) - if err != nil { - return err - } - - var ( - d *compdesc.ComponentDescriptor - sel func(AccessSpec) bool - eff ComponentVersionAccess - ) - - opts := NewBlobUploadOptions() - - forcestore := c.impl.IsOwned(acc) - if !forcestore { - // transfer all local blobs into a new owned version. - sel = func(spec AccessSpec) bool { return spec.IsLocal(ctx) } - - eff, err = c.impl.NewVersion(acc.GetVersion(), overrides...) - if err != nil { - return err - } - finalize.With(func() error { - return eff.Close() - }) - impl, err = GetComponentVersionAccessImplementation(eff) - if err != nil { - return err - } - - d = eff.GetDescriptor() - *d = *acc.GetDescriptor().Copy() - } else { - // transfer composition blobs into local blobs - opts.UseNoDefaultIfNotSet = true - opts.BlobHandlerProvider = nil - sel = compose.Is - d = acc.GetDescriptor() - eff = acc - } - - err = setupLocalBobs(ctx, "resource", acc, nil, impl, d.Resources, sel, forcestore, opts) - if err == nil { - err = setupLocalBobs(ctx, "source", acc, nil, impl, d.Sources, sel, forcestore, opts) - } - if err != nil { - return err - } - - return c.impl.AddVersion(eff) -} +type ComponentVersionAccessViewManager = resource.ViewManager[cpi.ComponentVersionAccess] -func setupLocalBobs(ctx Context, kind string, src ComponentVersionAccess, accprov func(AccessSpec) (AccessMethod, error), tgtimpl ComponentVersionAccessImpl, it compdesc.ArtifactAccessor, sel func(AccessSpec) bool, forcestore bool, opts *BlobUploadOptions) (ferr error) { - var finalize finalizer.Finalizer - defer finalize.FinalizeWithErrorPropagation(&ferr) - - for i := 0; i < it.Len(); i++ { - nested := finalize.Nested() - a := it.GetArtifact(i) - spec, err := ctx.AccessSpecForSpec(a.GetAccess()) - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } - if sel(spec) { - blob, err := blobAccessForLocalAccessSpec(spec, src, accprov) - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } - nested.Close(blob) - - var effspec AccessSpec - if forcestore { - effspec, err = tgtimpl.AddBlobFor(blob, ReferenceHint(spec, src), GlobalAccess(spec, ctx)) - } else { - effspec, err = addBlob(tgtimpl, a.GetType(), ReferenceHint(spec, src), blob, GlobalAccess(spec, ctx)) - } - if err != nil { - return errors.Wrapf(err, "cannot store %s %d", kind, i) - } - a.SetAccess(effspec) - } - err = nested.Finalize() - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } - } - return nil -} - -func blobAccessForLocalAccessSpec(spec AccessSpec, cv ComponentVersionAccess, accprov func(AccessSpec) (AccessMethod, error)) (blobaccess.BlobAccess, error) { - var m AccessMethod - var err error - if accprov != nil { - m, err = accprov(spec) - } else { - m, err = spec.AccessMethod(cv) - } - if err != nil { - return nil, err - } - return m.AsBlobAccess(), nil -} - -func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc ComponentVersionAccess, err error) { - err = c.Execute(func() error { - if c.impl.IsReadOnly() { - return accessio.ErrReadOnly - } - acc, err = c.impl.NewVersion(version, overrides...) - return err - }) - return acc, err -} - -func (c *componentAccessView) HasVersion(vers string) (ok bool, err error) { - err = c.Execute(func() error { - ok, err = c.impl.HasVersion(vers) - return err - }) - return ok, err -} - -//////////////////////////////////////////////////////////////////////////////// - -type _ComponentVersionAccessView interface { - resource.ResourceViewInt[ComponentVersionAccess] -} - -type ComponentVersionAccessViewManager = resource.ViewManager[ComponentVersionAccess] - -type ComponentVersionAccessImpl interface { - resource.ResourceImplementation[ComponentVersionAccess] +type ComponentVersionAccessBase interface { + resource.ResourceImplementation[cpi.ComponentVersionAccess] common.VersionedElement io.Closer - GetContext() Context - Repository() Repository + GetContext() cpi.Context + Repository() cpi.Repository + EnablePersistence() bool DiscardChanges() IsPersistent() bool GetDescriptor() *compdesc.ComponentDescriptor - AccessMethod(AccessSpec, refmgmt.ExtendedAllocatable) (AccessMethod, error) - GetInexpensiveContentVersionIdentity(AccessSpec, refmgmt.ExtendedAllocatable) string + AccessMethod(cpi.AccessSpec, refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) + GetInexpensiveContentVersionIdentity(cpi.AccessSpec, refmgmt.ExtendedAllocatable) string // GetStorageContext creates a storage context for blobs // that is used to feed blob handlers for specific blob storage methods. // If no handler accepts the blob, the AddBlobFor method will // be used to store the blob - GetStorageContext() StorageContext + GetStorageContext() cpi.StorageContext // AddBlobFor stores a local blob together with the component and // potentially provides a global reference. // The resulting access information (global and local) is provided as // an access method specification usable in a component descriptor. // This is the direct technical storage, without caring about any handler. - AddBlobFor(blob BlobAccess, refName string, global AccessSpec) (AccessSpec, error) + AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) IsReadOnly() bool @@ -482,165 +98,44 @@ type ComponentVersionAccessImpl interface { Update(final bool) error } -type ( - BlobCacheEntry = blobaccess.BlobAccess - BlobCacheKey = interface{} -) - -type BlobCache interface { - // AddBlobFor stores blobs for added blobs not yet accessible - // by generated access method until version is finally added. - AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error - - // GetBlobFor retrieves the original blob access for - // a given access specification. - GetBlobFor(acc BlobCacheKey) BlobCacheEntry - - RemoveBlobFor(acc BlobCacheKey) - Clear() error -} - -type blobCache struct { - lock sync.Mutex - blobcache map[BlobCacheKey]BlobCacheEntry -} - -func NewBlobCache() BlobCache { - return &blobCache{ - blobcache: map[BlobCacheKey]BlobCacheEntry{}, - } -} - -func (c *blobCache) RemoveBlobFor(acc BlobCacheKey) { - c.lock.Lock() - defer c.lock.Unlock() - if b := c.blobcache[acc]; b != nil { - b.Close() - delete(c.blobcache, acc) - } -} - -func (c *blobCache) AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error { - if s, ok := acc.(string); ok && s == "" { - return errors.ErrInvalid("blob key") - } - c.lock.Lock() - defer c.lock.Unlock() - - if c.blobcache[acc] == nil { - l, err := blob.Dup() - if err != nil { - return err - } - c.blobcache[acc] = l - } - return nil -} - -func (c *blobCache) GetBlobFor(acc BlobCacheKey) BlobCacheEntry { - c.lock.Lock() - defer c.lock.Unlock() - - return c.blobcache[acc] -} - -func (c *blobCache) Clear() error { - list := errors.ErrList() - c.lock.Lock() - defer c.lock.Unlock() - for _, b := range c.blobcache { - list.Add(b.Close()) - } - c.blobcache = map[BlobCacheKey]BlobCacheEntry{} - return list.Result() -} - -type _ComponentVersionAccessImplBase = resource.ResourceImplBase[ComponentVersionAccess] - -type ComponentVersionAccessImplBase struct { - *_ComponentVersionAccessImplBase - ctx Context - name string - version string - - blobcache BlobCache -} - -func NewComponentVersionAccessImplBase(ctx Context, name, version string, repo ComponentAccessViewManager, closer ...io.Closer) (*ComponentVersionAccessImplBase, error) { - base, err := resource.NewResourceImplBase[ComponentVersionAccess](repo, closer...) - if err != nil { - return nil, err - } - return &ComponentVersionAccessImplBase{ - _ComponentVersionAccessImplBase: base, - ctx: ctx, - name: name, - version: version, - blobcache: NewBlobCache(), - }, nil -} - -func (b *ComponentVersionAccessImplBase) Close() error { - list := errors.ErrListf("closing %s", common.VersionedElementKey(b)) - list.Add(b._ComponentVersionAccessImplBase.Close()) - list.Add(b.blobcache.Clear()) - return list.Result() -} - -func (b *ComponentVersionAccessImplBase) GetContext() Context { - return b.ctx -} - -func (b *ComponentVersionAccessImplBase) GetName() string { - return b.name -} - -func (b *ComponentVersionAccessImplBase) GetVersion() string { - return b.version -} - -func (b *ComponentVersionAccessImplBase) GetBlobCache() BlobCache { - return b.blobcache -} - type componentVersionAccessView struct { - _ComponentVersionAccessView - impl ComponentVersionAccessImpl + _componentVersionAccessView + base ComponentVersionAccessBase err error } var ( - _ ComponentVersionAccess = (*componentVersionAccessView)(nil) - _ utils.Unwrappable = (*componentVersionAccessView)(nil) + _ cpi.ComponentVersionAccess = (*componentVersionAccessView)(nil) + _ utils.Unwrappable = (*componentVersionAccessView)(nil) ) -func GetComponentVersionAccessImplementation(n ComponentVersionAccess) (ComponentVersionAccessImpl, error) { +func GetComponentVersionAccessBase(n cpi.ComponentVersionAccess) (ComponentVersionAccessBase, error) { if v, ok := n.(*componentVersionAccessView); ok { - return v.impl, nil + return v.base, nil } - return nil, errors.ErrNotSupported("component version implementation type", fmt.Sprintf("%T", n)) + return nil, errors.ErrNotSupported("component version base type", fmt.Sprintf("%T", n)) } -func artifactAccessViewCreator(i ComponentVersionAccessImpl, v resource.CloserView, d resource.ViewManager[ComponentVersionAccess]) ComponentVersionAccess { +func artifactAccessViewCreator(i ComponentVersionAccessBase, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess { cv := &componentVersionAccessView{ - _ComponentVersionAccessView: resource.NewView[ComponentVersionAccess](v, d), - impl: i, + _componentVersionAccessView: resource.NewView[cpi.ComponentVersionAccess](v, d), + base: i, } v.Allocatable().BeforeCleanup(refmgmt.CleanupHandlerFunc(cv.finish)) return cv } -func NewComponentVersionAccess(impl ComponentVersionAccessImpl) ComponentVersionAccess { - return resource.NewResource[ComponentVersionAccess](impl, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", impl.GetName(), impl.GetVersion()), true) +func NewComponentVersionAccess(impl ComponentVersionAccessBase) cpi.ComponentVersionAccess { + return resource.NewResource[cpi.ComponentVersionAccess](impl, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", impl.GetName(), impl.GetVersion()), true) } func (c *componentVersionAccessView) Unwrap() interface{} { - return c.impl + return c.base } func (c *componentVersionAccessView) Close() error { list := errors.ErrListf("closing %s", common.VersionedElementKey(c)) - err := c._ComponentVersionAccessView.Close() + err := c._componentVersionAccessView.Close() return list.Add(c.err, err).Result() } @@ -654,24 +149,24 @@ func (c *componentVersionAccessView) finish() { } } -func (c *componentVersionAccessView) Repository() Repository { - return c.impl.Repository() +func (c *componentVersionAccessView) Repository() cpi.Repository { + return c.base.Repository() } func (c *componentVersionAccessView) GetContext() internal.Context { - return c.impl.GetContext() + return c.base.GetContext() } func (c *componentVersionAccessView) GetName() string { - return c.impl.GetName() + return c.base.GetName() } func (c *componentVersionAccessView) GetVersion() string { - return c.impl.GetVersion() + return c.base.GetVersion() } func (c *componentVersionAccessView) GetDescriptor() *compdesc.ComponentDescriptor { - return c.impl.GetDescriptor() + return c.base.GetDescriptor() } func (c *componentVersionAccessView) GetProvider() *compdesc.Provider { @@ -685,7 +180,7 @@ func (c *componentVersionAccessView) SetProvider(p *compdesc.Provider) error { }) } -func (c *componentVersionAccessView) AccessMethod(spec AccessSpec) (meth AccessMethod, err error) { +func (c *componentVersionAccessView) AccessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) { spec, err = c.GetContext().AccessSpecForSpec(spec) if err != nil { return nil, err @@ -698,7 +193,7 @@ func (c *componentVersionAccessView) AccessMethod(spec AccessSpec) (meth AccessM return meth, err } -func (c *componentVersionAccessView) accessMethod(spec AccessSpec) (meth AccessMethod, err error) { +func (c *componentVersionAccessView) accessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) { switch { case compose.Is(spec): cspec, ok := spec.(*compose.AccessSpec) @@ -713,7 +208,7 @@ func (c *componentVersionAccessView) accessMethod(spec AccessSpec) (meth AccessM case !spec.IsLocal(c.GetContext()): meth, err = spec.AccessMethod(c) default: - meth, err = c.impl.AccessMethod(spec, c.Allocatable()) + meth, err = c.base.AccessMethod(spec, c.Allocatable()) if err == nil { if blob := c.getLocalBlob(spec); blob != nil { meth, err = newFakeMethod(meth, blob) @@ -723,7 +218,7 @@ func (c *componentVersionAccessView) accessMethod(spec AccessSpec) (meth AccessM return meth, err } -func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec AccessSpec) string { +func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec cpi.AccessSpec) string { var err error spec, err = c.GetContext().AccessSpecForSpec(spec) @@ -739,7 +234,7 @@ func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec A return id } -func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec AccessSpec) string { +func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec cpi.AccessSpec) string { switch { case compose.Is(spec): fallthrough @@ -747,13 +242,13 @@ func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec A // fall back to original version return spec.GetInexpensiveContentVersionIdentity(c) default: - return c.impl.GetInexpensiveContentVersionIdentity(spec, c.Allocatable()) + return c.base.GetInexpensiveContentVersionIdentity(spec, c.Allocatable()) } } func (c *componentVersionAccessView) Update() error { return c.Execute(func() error { - if !c.impl.IsPersistent() { + if !c.base.IsPersistent() { return ErrTempVersion } return c.update(true) @@ -761,37 +256,37 @@ func (c *componentVersionAccessView) Update() error { } func (c *componentVersionAccessView) update(final bool) error { - if !c.impl.ShouldUpdate(final) { + if !c.base.ShouldUpdate(final) { return nil } ctx := c.GetContext() d := c.GetDescriptor() - impl, err := GetComponentVersionAccessImplementation(c) + impl, err := GetComponentVersionAccessBase(c) if err != nil { return err } // TODO: exceute for separately lockable view - err = setupLocalBobs(ctx, "resource", c, c.accessMethod, impl, d.Resources, compose.Is, true, nil) + err = setupLocalBlobs(ctx, "resource", c, c.accessMethod, impl, d.Resources, compose.Is, true, nil) if err == nil { - err = setupLocalBobs(ctx, "source", c, c.accessMethod, impl, d.Sources, compose.Is, true, nil) + err = setupLocalBlobs(ctx, "source", c, c.accessMethod, impl, d.Sources, compose.Is, true, nil) } if err != nil { return err } - err = c.impl.Update(true) + err = c.base.Update(true) if err != nil { return err } - return c.impl.GetBlobCache().Clear() + return c.base.GetBlobCache().Clear() } -func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refName string, global AccessSpec, opts ...internal.BlobUploadOption) (AccessSpec, error) { +func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, opts ...internal.BlobUploadOption) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } - if c.impl.IsReadOnly() { + if c.base.IsReadOnly() { return nil, accessio.ErrReadOnly } blob, err := blob.Dup() @@ -804,10 +299,10 @@ func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refNa return nil, errors.Wrapf(err, "invalid blob access") } - return addBlob(c.impl, artType, refName, blob, global) + return addBlob(c.base, artType, refName, blob, global) } -func addBlob(impl ComponentVersionAccessImpl, artType, refName string, blob BlobAccess, global AccessSpec) (AccessSpec, error) { +func addBlob(impl ComponentVersionAccessBase, artType, refName string, blob cpi.BlobAccess, global cpi.AccessSpec) (cpi.AccessSpec, error) { storagectx := impl.GetStorageContext() ctx := impl.GetContext() h := ctx.BlobHandlers().LookupHandler(storagectx.GetImplementationRepositoryType(), artType, blob.MimeType()) @@ -831,15 +326,15 @@ func addBlob(impl ComponentVersionAccessImpl, artType, refName string, blob Blob return cacheLocalBlob(impl, acc, blob) } -func (c *componentVersionAccessView) getLocalBlob(acc AccessSpec) BlobAccess { +func (c *componentVersionAccessView) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { key, err := json.Marshal(acc) if err != nil { return nil } - return c.impl.GetBlobCache().GetBlobFor(string(key)) + return c.base.GetBlobCache().GetBlobFor(string(key)) } -func cacheLocalBlob(impl ComponentVersionAccessImpl, acc AccessSpec, blob BlobAccess) (AccessSpec, error) { +func cacheLocalBlob(impl ComponentVersionAccessBase, acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { key, err := json.Marshal(acc) if err != nil { return nil, errors.Wrapf(err, "cannot marshal access spec") @@ -864,43 +359,43 @@ func cacheLocalBlob(impl ComponentVersionAccessImpl, acc AccessSpec, blob BlobAc return acc, nil } -func (c *componentVersionAccessView) AdjustResourceAccess(meta *ResourceMeta, acc compdesc.AccessSpec, opts ...internal.ModificationOption) error { +func (c *componentVersionAccessView) AdjustResourceAccess(meta *cpi.ResourceMeta, acc compdesc.AccessSpec, opts ...internal.ModificationOption) error { cd := c.GetDescriptor() if idx := cd.GetResourceIndex(meta); idx >= 0 { return c.SetResource(&cd.Resources[idx].ResourceMeta, acc, opts...) } - return errors.ErrUnknown(KIND_RESOURCE, meta.GetIdentity(cd.Resources).String()) + return errors.ErrUnknown(cpi.KIND_RESOURCE, meta.GetIdentity(cd.Resources).String()) } // SetResourceBlob adds a blob resource to the component version. -func (c *componentVersionAccessView) SetResourceBlob(meta *ResourceMeta, blob cpi.BlobAccess, refName string, global AccessSpec, opts ...internal.BlobModificationOption) error { - Logger(c).Debug("adding resource blob", "resource", meta.Name) +func (c *componentVersionAccessView) SetResourceBlob(meta *cpi.ResourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec, opts ...internal.BlobModificationOption) error { + cpi.Logger(c).Debug("adding resource blob", "resource", meta.Name) if err := utils.ValidateObject(blob); err != nil { return err } - eff := NewBlobModificationOptions(opts...) + eff := cpi.NewBlobModificationOptions(opts...) acc, err := c.AddBlob(blob, meta.Type, refName, global, eff) if err != nil { return fmt.Errorf("unable to add blob (component %s:%s resource %s): %w", c.GetName(), c.GetVersion(), meta.GetName(), err) } - if err := c.SetResource(meta, acc, eff, ModifyResource()); err != nil { + if err := c.SetResource(meta, acc, eff, cpi.ModifyResource()); err != nil { return fmt.Errorf("unable to set resource: %w", err) } return nil } -func (c *componentVersionAccessView) AdjustSourceAccess(meta *SourceMeta, acc compdesc.AccessSpec) error { +func (c *componentVersionAccessView) AdjustSourceAccess(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error { cd := c.GetDescriptor() if idx := cd.GetSourceIndex(meta); idx >= 0 { return c.SetSource(&cd.Sources[idx].SourceMeta, acc) } - return errors.ErrUnknown(KIND_RESOURCE, meta.GetIdentity(cd.Resources).String()) + return errors.ErrUnknown(cpi.KIND_RESOURCE, meta.GetIdentity(cd.Resources).String()) } -func (c *componentVersionAccessView) SetSourceBlob(meta *SourceMeta, blob BlobAccess, refName string, global AccessSpec) error { - Logger(c).Debug("adding source blob", "source", meta.Name) +func (c *componentVersionAccessView) SetSourceBlob(meta *cpi.SourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec) error { + cpi.Logger(c).Debug("adding source blob", "source", meta.Name) if err := utils.ValidateObject(blob); err != nil { return err } @@ -917,7 +412,7 @@ func (c *componentVersionAccessView) SetSourceBlob(meta *SourceMeta, blob BlobAc } type fakeMethod struct { - spec AccessSpec + spec cpi.AccessSpec local bool mime string blob blobaccess.BlobAccess @@ -925,7 +420,7 @@ type fakeMethod struct { var _ accspeccpi.AccessMethodImpl = (*fakeMethod)(nil) -func newFakeMethod(m AccessMethod, blob BlobAccess) (AccessMethod, error) { +func newFakeMethod(m cpi.AccessMethod, blob cpi.BlobAccess) (cpi.AccessMethod, error) { b, err := blob.Dup() if err != nil { return nil, errors.Wrapf(err, "cannot remember blob for access method") @@ -974,9 +469,9 @@ func (f *fakeMethod) Get() ([]byte, error) { func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessView, kind string, art A, set func(*T, compdesc.AccessSpec) error, - setblob func(*T, BlobAccess, string, AccessSpec) error, + setblob func(*T, cpi.BlobAccess, string, cpi.AccessSpec) error, ) error { - if c.impl.IsReadOnly() { + if c.base.IsReadOnly() { return accessio.ErrReadOnly } meta := art.Meta() @@ -989,9 +484,9 @@ func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessVie } var ( - blob BlobAccess + blob cpi.BlobAccess hint string - global AccessSpec + global cpi.AccessSpec ) if acc != nil { @@ -1003,8 +498,8 @@ func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessVie if err != nil && errors.IsErrNotFoundElem(err, "", blobaccess.KIND_BLOB) { return err } - hint = ReferenceHint(acc, c) - global = GlobalAccess(acc, c.GetContext()) + hint = cpi.ReferenceHint(acc, c) + global = cpi.GlobalAccess(acc, c.GetContext()) } if blob == nil { blob, err = art.BlobAccess() @@ -1025,18 +520,18 @@ func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessVie return setblob(meta, blob, hint, global) } -func (c *componentVersionAccessView) SetResourceAccess(art ResourceAccess, modopts ...BlobModificationOption) error { +func (c *componentVersionAccessView) SetResourceAccess(art cpi.ResourceAccess, modopts ...cpi.BlobModificationOption) error { return setAccess(c, "resource", art, - func(meta *ResourceMeta, acc compdesc.AccessSpec) error { - return c.SetResource(meta, acc, NewBlobModificationOptions(modopts...)) + func(meta *cpi.ResourceMeta, acc compdesc.AccessSpec) error { + return c.SetResource(meta, acc, cpi.NewBlobModificationOptions(modopts...)) }, - func(meta *ResourceMeta, blob BlobAccess, hint string, global AccessSpec) error { + func(meta *cpi.ResourceMeta, blob cpi.BlobAccess, hint string, global cpi.AccessSpec) error { return c.SetResourceBlob(meta, blob, hint, global, modopts...) }) } -func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, acc compdesc.AccessSpec, modopts ...ModificationOption) error { - if c.impl.IsReadOnly() { +func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, acc compdesc.AccessSpec, modopts ...cpi.ModificationOption) error { + if c.base.IsReadOnly() { return accessio.ErrReadOnly } @@ -1045,11 +540,11 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac Access: acc, } - ctx := c.impl.GetContext() + ctx := c.base.GetContext() opts := internal.NewModificationOptions(modopts...) - CompleteModificationOptions(ctx, opts) + cpi.CompleteModificationOptions(ctx, opts) - spec, err := c.impl.GetContext().AccessSpecForSpec(acc) + spec, err := c.base.GetContext().AccessSpecForSpec(acc) if err != nil { return err } @@ -1087,14 +582,14 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } } - cd := c.impl.GetDescriptor() + cd := c.base.GetDescriptor() idx := cd.GetResourceIndex(&res.ResourceMeta) if idx >= 0 { old = &cd.Resources[idx] } if old == nil { - if !opts.IsModifyResource() && c.impl.IsPersistent() { + if !opts.IsModifyResource() && c.base.IsPersistent() { return fmt.Errorf("new resource would invalidate signature") } } @@ -1107,7 +602,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } if !compdesc.IsNoneAccessKind(res.Access.GetKind()) { - var calculatedDigest *DigestDescriptor + var calculatedDigest *cpi.DigestDescriptor if (!opts.IsSkipVerify() && digest != "") || (!opts.IsSkipDigest() && digest == "") { dig, err := ctx.BlobDigesters().DetermineDigests(res.Type, hasher, opts.HasherProvider, meth, digester) if err != nil { @@ -1140,7 +635,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac if old != nil { eq := res.Equivalent(old) - if !eq.IsLocalHashEqual() && c.impl.IsPersistent() { + if !eq.IsLocalHashEqual() && c.base.IsPersistent() { if !opts.IsModifyResource() { return fmt.Errorf("resource would invalidate signature") } @@ -1158,8 +653,8 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } // evaluateResourceDigest evaluate given potentially partly set digest to determine defaults. -func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.Resource, opts ModificationOptions) (string, DigesterType, string) { - var digester DigesterType +func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.Resource, opts cpi.ModificationOptions) (string, cpi.DigesterType, string) { + var digester cpi.DigesterType hashAlgo := opts.DefaultHashAlgorithm value := "" @@ -1171,7 +666,7 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R hashAlgo = res.Digest.HashAlgorithm } if res.Digest.NormalisationAlgorithm != "" { - digester = DigesterType{ + digester = cpi.DigesterType{ HashAlgorithm: hashAlgo, NormalizationAlgorithm: res.Digest.NormalisationAlgorithm, } @@ -1184,7 +679,7 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R if !old.Digest.IsNone() { digester.HashAlgorithm = old.Digest.HashAlgorithm digester.NormalizationAlgorithm = old.Digest.NormalisationAlgorithm - if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.impl.IsPersistent() { + if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.base.IsPersistent() { res.Digest = old.Digest value = old.Digest.Value } @@ -1193,13 +688,13 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R return hashAlgo, digester, value } -func (c *componentVersionAccessView) SetSourceByAccess(art SourceAccess) error { +func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess) error { return setAccess(c, "source", art, c.SetSource, c.SetSourceBlob) } -func (c *componentVersionAccessView) SetSource(meta *SourceMeta, acc compdesc.AccessSpec) error { - if c.impl.IsReadOnly() { +func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error { + if c.base.IsReadOnly() { return accessio.ErrReadOnly } @@ -1209,9 +704,9 @@ func (c *componentVersionAccessView) SetSource(meta *SourceMeta, acc compdesc.Ac } return c.Execute(func() error { if res.Version == "" { - res.Version = c.impl.GetVersion() + res.Version = c.base.GetVersion() } - cd := c.impl.GetDescriptor() + cd := c.base.GetDescriptor() if idx := cd.GetSourceIndex(&res.SourceMeta); idx == -1 { cd.Sources = append(cd.Sources, *res) } else { @@ -1221,9 +716,9 @@ func (c *componentVersionAccessView) SetSource(meta *SourceMeta, acc compdesc.Ac }) } -func (c *componentVersionAccessView) SetReference(ref *ComponentReference) error { +func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference) error { return c.Execute(func() error { - cd := c.impl.GetDescriptor() + cd := c.base.GetDescriptor() if idx := cd.GetComponentReferenceIndex(*ref); idx == -1 { cd.References = append(cd.References, *ref) } else { @@ -1234,74 +729,74 @@ func (c *componentVersionAccessView) SetReference(ref *ComponentReference) error } func (c *componentVersionAccessView) DiscardChanges() { - c.impl.DiscardChanges() + c.base.DiscardChanges() } func (c *componentVersionAccessView) IsPersistent() bool { - return c.impl.IsPersistent() + return c.base.IsPersistent() } func (c *componentVersionAccessView) UseDirectAccess() bool { - return c.impl.UseDirectAccess() + return c.base.UseDirectAccess() } //////////////////////////////////////////////////////////////////////////////// // Standard Implementation for descriptor based methods -func (c *componentVersionAccessView) GetResource(id metav1.Identity) (ResourceAccess, error) { +func (c *componentVersionAccessView) GetResource(id metav1.Identity) (cpi.ResourceAccess, error) { r, err := c.GetDescriptor().GetResourceByIdentity(id) if err != nil { return nil, err } - return NewResourceAccess(c, r.Access, r.ResourceMeta), nil + return cpi.NewResourceAccess(c, r.Access, r.ResourceMeta), nil } func (c *componentVersionAccessView) GetResourceIndex(id metav1.Identity) int { return c.GetDescriptor().GetResourceIndexByIdentity(id) } -func (c *componentVersionAccessView) GetResourceByIndex(i int) (ResourceAccess, error) { +func (c *componentVersionAccessView) GetResourceByIndex(i int) (cpi.ResourceAccess, error) { if i < 0 || i >= len(c.GetDescriptor().Resources) { return nil, errors.ErrInvalid("resource index", strconv.Itoa(i)) } r := c.GetDescriptor().Resources[i] - return NewResourceAccess(c, r.Access, r.ResourceMeta), nil + return cpi.NewResourceAccess(c, r.Access, r.ResourceMeta), nil } -func (c *componentVersionAccessView) GetResourcesByName(name string, selectors ...compdesc.IdentitySelector) ([]ResourceAccess, error) { +func (c *componentVersionAccessView) GetResourcesByName(name string, selectors ...compdesc.IdentitySelector) ([]cpi.ResourceAccess, error) { resources, err := c.GetDescriptor().GetResourcesByName(name, selectors...) if err != nil { return nil, err } - result := []ResourceAccess{} + result := []cpi.ResourceAccess{} for _, resource := range resources { - result = append(result, NewResourceAccess(c, resource.Access, resource.ResourceMeta)) + result = append(result, cpi.NewResourceAccess(c, resource.Access, resource.ResourceMeta)) } return result, nil } -func (c *componentVersionAccessView) GetResources() []ResourceAccess { - result := []ResourceAccess{} +func (c *componentVersionAccessView) GetResources() []cpi.ResourceAccess { + result := []cpi.ResourceAccess{} for _, r := range c.GetDescriptor().Resources { - result = append(result, NewResourceAccess(c, r.Access, r.ResourceMeta)) + result = append(result, cpi.NewResourceAccess(c, r.Access, r.ResourceMeta)) } return result } // GetResourcesByIdentitySelectors returns resources that match the given identity selectors. -func (c *componentVersionAccessView) GetResourcesByIdentitySelectors(selectors ...compdesc.IdentitySelector) ([]ResourceAccess, error) { +func (c *componentVersionAccessView) GetResourcesByIdentitySelectors(selectors ...compdesc.IdentitySelector) ([]cpi.ResourceAccess, error) { return c.GetResourcesBySelectors(selectors, nil) } // GetResourcesByResourceSelectors returns resources that match the given resource selectors. -func (c *componentVersionAccessView) GetResourcesByResourceSelectors(selectors ...compdesc.ResourceSelector) ([]ResourceAccess, error) { +func (c *componentVersionAccessView) GetResourcesByResourceSelectors(selectors ...compdesc.ResourceSelector) ([]cpi.ResourceAccess, error) { return c.GetResourcesBySelectors(nil, selectors) } // GetResourcesBySelectors returns resources that match the given selector. -func (c *componentVersionAccessView) GetResourcesBySelectors(selectors []compdesc.IdentitySelector, resourceSelectors []compdesc.ResourceSelector) ([]ResourceAccess, error) { - resources := make([]ResourceAccess, 0) +func (c *componentVersionAccessView) GetResourcesBySelectors(selectors []compdesc.IdentitySelector, resourceSelectors []compdesc.ResourceSelector) ([]cpi.ResourceAccess, error) { + resources := make([]cpi.ResourceAccess, 0) rscs := c.GetDescriptor().Resources for i := range rscs { selctx := compdesc.NewResourceSelectionContext(i, rscs) @@ -1333,30 +828,30 @@ func (c *componentVersionAccessView) GetResourcesBySelectors(selectors []compdes return resources, nil } -func (c *componentVersionAccessView) GetSource(id metav1.Identity) (SourceAccess, error) { +func (c *componentVersionAccessView) GetSource(id metav1.Identity) (cpi.SourceAccess, error) { r, err := c.GetDescriptor().GetSourceByIdentity(id) if err != nil { return nil, err } - return NewSourceAccess(c, r.Access, r.SourceMeta), nil + return cpi.NewSourceAccess(c, r.Access, r.SourceMeta), nil } func (c *componentVersionAccessView) GetSourceIndex(id metav1.Identity) int { return c.GetDescriptor().GetSourceIndexByIdentity(id) } -func (c *componentVersionAccessView) GetSourceByIndex(i int) (SourceAccess, error) { +func (c *componentVersionAccessView) GetSourceByIndex(i int) (cpi.SourceAccess, error) { if i < 0 || i >= len(c.GetDescriptor().Sources) { return nil, errors.ErrInvalid("source index", strconv.Itoa(i)) } r := c.GetDescriptor().Sources[i] - return NewSourceAccess(c, r.Access, r.SourceMeta), nil + return cpi.NewSourceAccess(c, r.Access, r.SourceMeta), nil } -func (c *componentVersionAccessView) GetSources() []SourceAccess { - result := []SourceAccess{} +func (c *componentVersionAccessView) GetSources() []cpi.SourceAccess { + result := []cpi.SourceAccess{} for _, r := range c.GetDescriptor().Sources { - result = append(result, NewSourceAccess(c, r.Access, r.SourceMeta)) + result = append(result, cpi.NewSourceAccess(c, r.Access, r.SourceMeta)) } return result } @@ -1365,7 +860,7 @@ func (c *componentVersionAccessView) GetReferences() compdesc.References { return c.GetDescriptor().References } -func (c *componentVersionAccessView) GetReference(id metav1.Identity) (ComponentReference, error) { +func (c *componentVersionAccessView) GetReference(id metav1.Identity) (cpi.ComponentReference, error) { return c.GetDescriptor().GetReferenceByIdentity(id) } @@ -1373,9 +868,9 @@ func (c *componentVersionAccessView) GetReferenceIndex(id metav1.Identity) int { return c.GetDescriptor().GetReferenceIndexByIdentity(id) } -func (c *componentVersionAccessView) GetReferenceByIndex(i int) (ComponentReference, error) { +func (c *componentVersionAccessView) GetReferenceByIndex(i int) (cpi.ComponentReference, error) { if i < 0 || i > len(c.GetDescriptor().References) { - return ComponentReference{}, errors.ErrInvalid("reference index", strconv.Itoa(i)) + return cpi.ComponentReference{}, errors.ErrInvalid("reference index", strconv.Itoa(i)) } return c.GetDescriptor().References[i], nil } @@ -1423,3 +918,56 @@ func (c *componentVersionAccessView) GetReferencesBySelectors(selectors []compde } return references, nil } + +//////////////////////////////////////////////////////////////////////////////// + +func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error), tgtimpl ComponentVersionAccessBase, it compdesc.ArtifactAccessor, sel func(cpi.AccessSpec) bool, forcestore bool, opts *cpi.BlobUploadOptions) (ferr error) { + var finalize finalizer.Finalizer + defer finalize.FinalizeWithErrorPropagation(&ferr) + + for i := 0; i < it.Len(); i++ { + nested := finalize.Nested() + a := it.GetArtifact(i) + spec, err := ctx.AccessSpecForSpec(a.GetAccess()) + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + if sel(spec) { + blob, err := blobAccessForLocalAccessSpec(spec, src, accprov) + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + nested.Close(blob) + + var effspec cpi.AccessSpec + if forcestore { + effspec, err = tgtimpl.AddBlobFor(blob, cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, ctx)) + } else { + effspec, err = addBlob(tgtimpl, a.GetType(), cpi.ReferenceHint(spec, src), blob, cpi.GlobalAccess(spec, ctx)) + } + if err != nil { + return errors.Wrapf(err, "cannot store %s %d", kind, i) + } + a.SetAccess(effspec) + } + err = nested.Finalize() + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + } + return nil +} + +func blobAccessForLocalAccessSpec(spec cpi.AccessSpec, cv cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error)) (blobaccess.BlobAccess, error) { + var m cpi.AccessMethod + var err error + if accprov != nil { + m, err = accprov(spec) + } else { + m, err = spec.AccessMethod(cv) + } + if err != nil { + return nil, err + } + return m.AsBlobAccess(), nil +} diff --git a/pkg/contexts/ocm/cpi/support/compversaccess.go b/pkg/contexts/ocm/cpi/support/compversaccess.go deleted file mode 100644 index 962d6180e5..0000000000 --- a/pkg/contexts/ocm/cpi/support/compversaccess.go +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - -package support - -import ( - "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/refmgmt" -) - -type _ComponentVersionAccessImplBase = cpi.ComponentVersionAccessImplBase - -type ComponentVersionAccessImpl interface { - cpi.ComponentVersionAccessImpl - EnablePersistence() bool -} - -type componentVersionAccessImpl struct { - *_ComponentVersionAccessImplBase - lazy bool - directAccess bool - persistent bool - discardChanges bool - base ComponentVersionContainer -} - -var _ ComponentVersionAccessImpl = (*componentVersionAccessImpl)(nil) - -func GetComponentVersionContainer[T ComponentVersionContainer](cv cpi.ComponentVersionAccess) (T, error) { - var _nil T - - impl, err := cpi.GetComponentVersionAccessImplementation(cv) - if err != nil { - return _nil, err - } - if mine, ok := impl.(*componentVersionAccessImpl); ok { - cont, ok := mine.base.(T) - if ok { - return cont, nil - } - return _nil, errors.Newf("non-matching component version implementation %T", mine.base) - } - return _nil, errors.Newf("non-matching component version implementation %T", impl) -} - -func NewComponentVersionAccessImpl(name, version string, container ComponentVersionContainer, lazy, persistent, direct bool) (cpi.ComponentVersionAccessImpl, error) { - base, err := cpi.NewComponentVersionAccessImplBase(container.GetContext(), name, version, container.GetParentViewManager()) - if err != nil { - return nil, err - } - impl := &componentVersionAccessImpl{ - _ComponentVersionAccessImplBase: base, - lazy: lazy, - persistent: persistent, - directAccess: direct, - base: container, - } - container.SetImplementation(impl) - return impl, nil -} - -func (a *componentVersionAccessImpl) EnablePersistence() bool { - if a.discardChanges { - return false - } - a.persistent = true - a.GetStorageContext() - return true -} - -func (a *componentVersionAccessImpl) IsPersistent() bool { - return a.persistent -} - -func (d *componentVersionAccessImpl) UseDirectAccess() bool { - return d.directAccess -} - -func (a *componentVersionAccessImpl) DiscardChanges() { - a.discardChanges = true -} - -func (a *componentVersionAccessImpl) Close() error { - list := errors.ErrListf("closing component version access %s/%s", a.GetName(), a.GetVersion()) - return list.Add(a.base.Close(), a._ComponentVersionAccessImplBase.Close()).Result() -} - -func (a *componentVersionAccessImpl) Repository() cpi.Repository { - return a.base.Repository() -} - -func (a *componentVersionAccessImpl) IsReadOnly() bool { - return a.base.IsReadOnly() -} - -//////////////////////////////////////////////////////////////////////////////// -// with access to actual view - -func (a *componentVersionAccessImpl) AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) { - return a.base.AccessMethod(acc, cv) -} - -func (a *componentVersionAccessImpl) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { - return a.base.GetInexpensiveContentVersionIdentity(acc, cv) -} - -func (a *componentVersionAccessImpl) GetDescriptor() *compdesc.ComponentDescriptor { - return a.base.GetDescriptor() -} - -func (a *componentVersionAccessImpl) GetStorageContext() cpi.StorageContext { - return a.base.GetStorageContext() -} - -func (a *componentVersionAccessImpl) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { - return a.base.AddBlobFor(blob, refName, global) -} - -func (a *componentVersionAccessImpl) ShouldUpdate(final bool) bool { - if a.discardChanges { - return false - } - if final { - return a.persistent - } - return !a.lazy && a.directAccess && a.persistent -} - -func (a *componentVersionAccessImpl) Update(final bool) error { - if a.ShouldUpdate(final) { - return a.base.Update() - } - return nil -} diff --git a/pkg/contexts/ocm/cpi/support/doc.go b/pkg/contexts/ocm/cpi/support/doc.go deleted file mode 100644 index de614ae9a4..0000000000 --- a/pkg/contexts/ocm/cpi/support/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - -/* -Package support provides a standard implementation for the object type set -required to implement the OCM repository interface. - -This implementation is based on three interfaces that have to implemented: - - - BlobContainer - is used to provide access to blob data - - ComponentVersionContainer - is used to provide access to component version for component. - -The function NewComponentVersionAccessImpl can be used to create an -object implementing the complete ComponentVersionAccess contract. -*/ -package support diff --git a/pkg/contexts/ocm/cpi/support/error.go b/pkg/contexts/ocm/cpi/support/error.go deleted file mode 100644 index 56b12f7e41..0000000000 --- a/pkg/contexts/ocm/cpi/support/error.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - -package support - -import "fmt" - -type UpdateComponentVersionContainerError struct { - Name string - Version string - - Original error -} - -func (e UpdateComponentVersionContainerError) Error() string { - message := fmt.Sprintf( - "unable to update '%s:%s' base component container", - e.Name, - e.Version, - ) - - if e.Original != nil { - message = fmt.Sprintf("%s: %s", message, e.Original.Error()) - } - - return message -} - -func (e UpdateComponentVersionContainerError) Unwrap() error { - return e.Original -} - -type AccessCheckError struct { - Name string - Version string - Type string - - Original error -} - -func (e AccessCheckError) Error() string { - message := fmt.Sprintf( - "failed access spec check on '%s:%s' with type '%s'", - e.Name, - e.Version, - e.Type, - ) - - if e.Original != nil { - message = fmt.Sprintf("%s: %s", message, e.Original.Error()) - } - - return message -} - -func (e AccessCheckError) Unwrap() error { - return e.Original -} diff --git a/pkg/contexts/ocm/interface.go b/pkg/contexts/ocm/interface.go index 4ad64ec641..3548bced5c 100644 --- a/pkg/contexts/ocm/interface.go +++ b/pkg/contexts/ocm/interface.go @@ -12,13 +12,14 @@ import ( metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/runtime" ) // ErrTempVersion indicates an ignored update in the backend because the // current version has not yet been added to the repository. -var ErrTempVersion = cpi.ErrTempVersion +var ErrTempVersion = repocpi.ErrTempVersion const ( KIND_COMPONENTVERSION = internal.KIND_COMPONENTVERSION diff --git a/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go b/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go index f980c949fc..96ec4733b8 100644 --- a/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go +++ b/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go @@ -13,7 +13,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localblob" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/refmgmt" ) @@ -23,14 +23,14 @@ type localFilesystemBlobAccessMethod struct { sync.Mutex closed bool spec *localblob.AccessSpec - base support.ComponentVersionContainer + base repocpi.ComponentVersionAccessImpl err error blobAccess blobaccess.BlobAccess } var _ accspeccpi.AccessMethodImpl = (*localFilesystemBlobAccessMethod)(nil) -func newLocalFilesystemBlobAccessMethod(a *localblob.AccessSpec, base support.ComponentVersionContainer, ref refmgmt.ExtendedAllocatable) (accspeccpi.AccessMethod, error) { +func newLocalFilesystemBlobAccessMethod(a *localblob.AccessSpec, base repocpi.ComponentVersionAccessImpl, ref refmgmt.ExtendedAllocatable) (accspeccpi.AccessMethod, error) { m := &localFilesystemBlobAccessMethod{ spec: a, base: base, diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index b05d267be9..aa12c15d2d 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -6,7 +6,6 @@ package comparch import ( "github.com/mandelsoft/vfs/pkg/vfs" - "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/common/accessobj" @@ -16,7 +15,7 @@ import ( ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/refmgmt" ) @@ -50,7 +49,7 @@ func _Wrap(ctx cpi.ContextProvider, obj *accessobj.AccessObject, spec *Repositor ctx: ctx.OCMContext(), base: accessobj.NewFileSystemBlobAccess(obj), } - impl, err := support.NewComponentVersionAccessImpl(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true) + impl, err := repocpi.NewComponentVersionAccessBase(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true) if err != nil { return nil, err } @@ -59,7 +58,7 @@ func _Wrap(ctx cpi.ContextProvider, obj *accessobj.AccessObject, spec *Repositor spec: spec, container: s, } - arch.ComponentVersionAccess = cpi.NewComponentVersionAccess(impl) + arch.ComponentVersionAccess = repocpi.NewComponentVersionAccess(impl) arch.main, arch.nonref = newRepository(arch) s.repo = arch.nonref return arch, nil @@ -100,19 +99,19 @@ func (c *ComponentArchive) SetVersion(v string) { type componentArchiveContainer struct { ctx cpi.Context - impl support.ComponentVersionAccessImpl + impl repocpi.ComponentVersionAccessBase base *accessobj.FileSystemBlobAccess spec *RepositorySpec repo cpi.Repository } -var _ support.ComponentVersionContainer = (*componentArchiveContainer)(nil) +var _ repocpi.ComponentVersionAccessImpl = (*componentArchiveContainer)(nil) -func (c *componentArchiveContainer) SetImplementation(impl support.ComponentVersionAccessImpl) { +func (c *componentArchiveContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *componentArchiveContainer) GetParentViewManager() cpi.ComponentAccessViewManager { +func (c *componentArchiveContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { return nil } diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 604e3bdeb2..eb00510520 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -19,13 +19,13 @@ import ( ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/utils" ) -type _RepositoryImplBase = cpi.RepositoryImplBase +type _RepositoryImplBase = repocpi.RepositoryImplBase type RepositoryImpl struct { _RepositoryImplBase @@ -33,7 +33,7 @@ type RepositoryImpl struct { arch *ComponentArchive } -var _ cpi.RepositoryImpl = (*RepositoryImpl)(nil) +var _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil) func NewRepository(ctx cpi.Context, s *RepositorySpec) (cpi.Repository, error) { if s.GetPathFileSystem() == nil { @@ -48,12 +48,12 @@ func NewRepository(ctx cpi.Context, s *RepositorySpec) (cpi.Repository, error) { func newRepository(a *ComponentArchive) (main cpi.Repository, nonref cpi.Repository) { // close main cv abstraction on repository close -------v - base := cpi.NewRepositoryImplBase(a.GetContext(), a.ComponentVersionAccess) + base := repocpi.NewRepositoryImplBase(a.GetContext(), a.ComponentVersionAccess) impl := &RepositoryImpl{ _RepositoryImplBase: *base, arch: a, } - return cpi.NewRepository(impl), cpi.NewNoneRefRepositoryView(impl) + return repocpi.NewRepository(impl), repocpi.NewNoneRefRepositoryView(impl) } func (r *RepositoryImpl) ComponentLister() cpi.ComponentLister { @@ -150,17 +150,17 @@ func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, erro //////////////////////////////////////////////////////////////////////////////// -type _ComponentAccessImplBase = cpi.ComponentAccessImplBase +type _ComponentAccessImplBase = repocpi.ComponentAccessImplBase type ComponentAccessImpl struct { _ComponentAccessImplBase repo *RepositoryImpl } -var _ cpi.ComponentAccessImpl = (*ComponentAccessImpl)(nil) +var _ repocpi.ComponentAccessBase = (*ComponentAccessImpl)(nil) func newComponentAccess(r *RepositoryImpl) (cpi.ComponentAccess, error) { - base, err := cpi.NewComponentAccessImplBase(r.GetContext(), r.arch.GetName(), r) + base, err := repocpi.NewComponentAccessImplBase(r.GetContext(), r.arch.GetName(), r) if err != nil { return nil, err } @@ -168,7 +168,7 @@ func newComponentAccess(r *RepositoryImpl) (cpi.ComponentAccess, error) { _ComponentAccessImplBase: *base, repo: r, } - return cpi.NewComponentAccess(impl, "component archive"), nil + return repocpi.NewComponentAccess(impl, "component archive"), nil } func (c *ComponentAccessImpl) IsReadOnly() bool { @@ -191,7 +191,7 @@ func (c *ComponentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio } func (c *ComponentAccessImpl) container(access cpi.ComponentVersionAccess) *componentArchiveContainer { - mine, _ := support.GetComponentVersionContainer[*ComponentVersionContainer](access) + mine, _ := repocpi.GetComponentVersionImpl[*ComponentVersionContainer](access) if mine == nil || mine.comp != c { return nil } @@ -226,26 +226,26 @@ func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (cpi //////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl support.ComponentVersionAccessImpl + impl repocpi.ComponentVersionAccessBase comp *ComponentAccessImpl descriptor *compdesc.ComponentDescriptor } -var _ support.ComponentVersionContainer = (*ComponentVersionContainer)(nil) +var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil) func newComponentVersionAccess(comp *ComponentAccessImpl, version string, persistent bool) (cpi.ComponentVersionAccess, error) { c, err := newComponentVersionContainer(comp) if err != nil { return nil, err } - impl, err := support.NewComponentVersionAccessImpl(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) + impl, err := repocpi.NewComponentVersionAccessBase(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) if err != nil { c.Close() return nil, err } - return cpi.NewComponentVersionAccess(impl), nil + return repocpi.NewComponentVersionAccess(impl), nil } func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionContainer, error) { @@ -255,11 +255,11 @@ func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionC }, nil } -func (c *ComponentVersionContainer) SetImplementation(impl support.ComponentVersionAccessImpl) { +func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentViewManager() cpi.ComponentAccessViewManager { +func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { return c.comp } diff --git a/pkg/contexts/ocm/repositories/ctf/repo_test.go b/pkg/contexts/ocm/repositories/ctf/repo_test.go index ee9651615d..ad6ca828a7 100644 --- a/pkg/contexts/ocm/repositories/ctf/repo_test.go +++ b/pkg/contexts/ocm/repositories/ctf/repo_test.go @@ -9,6 +9,7 @@ import ( . "github.com/onsi/gomega" . "github.com/open-component-model/ocm/pkg/contexts/ocm/testhelper" . "github.com/open-component-model/ocm/pkg/finalizer" + "github.com/open-component-model/ocm/pkg/refmgmt" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/mandelsoft/vfs/pkg/memoryfs" @@ -36,6 +37,64 @@ var _ = Describe("access method", func() { fs = memoryfs.New() }) + It("adds naked component version and later lookup", func() { + final := Finalizer{} + defer Defer(final.Finalize) + + a := Must(ctf.Create(ctx, accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, "ctf", 0o700, accessio.PathFileSystem(fs))) + final.Close(a, "repository") + c := Must(a.LookupComponent(COMPONENT)) + final.Close(c, "component") + + cv := Must(c.NewVersion(VERSION)) + final.Close(cv, "version") + + MustBeSuccessful(c.AddVersion(cv)) + MustBeSuccessful(final.Finalize()) + + refmgmt.AllocLog.Trace("opening ctf") + a = Must(ctf.Open(ctx, accessobj.ACC_READONLY, "ctf", 0o700, accessio.PathFileSystem(fs))) + final.Close(a) + + refmgmt.AllocLog.Trace("lookup component") + c = Must(a.LookupComponent(COMPONENT)) + final.Close(c) + + refmgmt.AllocLog.Trace("lookup version") + cv = Must(c.LookupVersion(VERSION)) + final.Close(cv) + + refmgmt.AllocLog.Trace("closing") + MustBeSuccessful(final.Finalize()) + }) + + It("adds naked component version and later shortcut lookup", func() { + final := Finalizer{} + defer Defer(final.Finalize) + + a := Must(ctf.Create(ctx, accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, "ctf", 0o700, accessio.PathFileSystem(fs))) + final.Close(a, "repository") + c := Must(a.LookupComponent(COMPONENT)) + final.Close(c, "component") + + cv := Must(c.NewVersion(VERSION)) + final.Close(cv, "version") + + MustBeSuccessful(c.AddVersion(cv)) + MustBeSuccessful(final.Finalize()) + + refmgmt.AllocLog.Trace("opening ctf") + a = Must(ctf.Open(ctx, accessobj.ACC_READONLY, "ctf", 0o700, accessio.PathFileSystem(fs))) + final.Close(a) + + refmgmt.AllocLog.Trace("lookup component version") + cv = Must(a.LookupComponentVersion(COMPONENT, VERSION)) + final.Close(cv) + + refmgmt.AllocLog.Trace("closing") + MustBeSuccessful(final.Finalize()) + }) + It("adds component version", func() { final := Finalizer{} defer Defer(final.Finalize) diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index aacadd39f1..d8d066b943 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -9,13 +9,14 @@ import ( "strings" "github.com/Masterminds/semver/v3" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" + "github.com/open-component-model/ocm/pkg/refmgmt" "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" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/utils" ) @@ -24,7 +25,7 @@ const META_SEPARATOR = ".build-" //////////////////////////////////////////////////////////////////////////////// -type _ComponentAccessImplBase = cpi.ComponentAccessImplBase +type _ComponentAccessImplBase = repocpi.ComponentAccessImplBase type componentAccessImpl struct { _ComponentAccessImplBase @@ -39,7 +40,7 @@ func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.Compo return nil, err } - base, err := cpi.NewComponentAccessImplBase(repo.GetContext(), name, repo) + base, err := repocpi.NewComponentAccessImplBase(repo.GetContext(), name, repo) if err != nil { return nil, err } @@ -54,11 +55,14 @@ func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.Compo name: name, namespace: namespace, } - return cpi.NewComponentAccess(impl, "OCM component[OCI]"), nil + return repocpi.NewComponentAccess(impl, "OCM component[OCI]"), nil } func (c *componentAccessImpl) Close() error { - return accessio.Close(c.namespace, c._ComponentAccessImplBase) + refmgmt.AllocLog.Trace("closing component [OCI]", "name", c.name) + err := accessio.Close(c.namespace, c._ComponentAccessImplBase) + refmgmt.AllocLog.Trace("closed component [OCI]", "name", c.name) + return err } //////////////////////////////////////////////////////////////////////////////// @@ -140,7 +144,7 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio } func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess) *ComponentVersionContainer { - mine, _ := support.GetComponentVersionContainer[*ComponentVersionContainer](access) + mine, _ := repocpi.GetComponentVersionImpl[*ComponentVersionContainer](access) if mine == nil || mine.comp != c { return nil } diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 9ea2eb38fa..ffd50c8397 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -9,6 +9,7 @@ import ( "path" "strings" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/opencontainers/go-digest" "github.com/open-component-model/ocm/pkg/common" @@ -27,7 +28,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/generics" "github.com/open-component-model/ocm/pkg/refmgmt" @@ -39,18 +39,18 @@ func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessI if err != nil { return nil, err } - impl, err := support.NewComponentVersionAccessImpl(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) + impl, err := repocpi.NewComponentVersionAccessBase(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) if err != nil { c.Close() return nil, err } - return cpi.NewComponentVersionAccess(impl), nil + return repocpi.NewComponentVersionAccess(impl), nil } // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl support.ComponentVersionAccessImpl + impl repocpi.ComponentVersionAccessBase comp *componentAccessImpl version string @@ -59,7 +59,7 @@ type ComponentVersionContainer struct { state accessobj.State } -var _ support.ComponentVersionContainer = (*ComponentVersionContainer)(nil) +var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil) func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess) (*ComponentVersionContainer, error) { m := access.ManifestAccess() @@ -81,11 +81,11 @@ func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAcce }, nil } -func (c *ComponentVersionContainer) SetImplementation(impl support.ComponentVersionAccessImpl) { +func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentViewManager() cpi.ComponentAccessViewManager { +func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { return c.comp } diff --git a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go index 27224863c5..70975fb68c 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go @@ -11,6 +11,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/mandelsoft/vfs/pkg/osfs" @@ -83,7 +84,7 @@ var _ = Describe("component repository mapping", func() { defer Defer(finalize.Finalize) repo := finalizer.ClosingWith(&finalize, Must(DefaultContext.RepositoryForSpec(spec))) - impl := Must(cpi.GetRepositoryImplementation(repo)) + impl := Must(repocpi.GetRepositoryImplementation(repo)) Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl")) comp := finalizer.ClosingWith(&finalize, Must(repo.LookupComponent(COMPONENT))) @@ -123,7 +124,7 @@ var _ = Describe("component repository mapping", func() { // create repository repo := finalizer.ClosingWith(&finalize, Must(DefaultContext.RepositoryForSpec(spec))) - impl := Must(cpi.GetRepositoryImplementation(repo)) + impl := Must(repocpi.GetRepositoryImplementation(repo)) Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl")) comp := finalizer.ClosingWith(&finalize, Must(repo.LookupComponent(COMPONENT))) @@ -172,7 +173,7 @@ var _ = Describe("component repository mapping", func() { // create repository repo := finalizer.ClosingWith(&finalize, Must(ctx.RepositoryForSpec(spec))) - impl := Must(cpi.GetRepositoryImplementation(repo)) + impl := Must(repocpi.GetRepositoryImplementation(repo)) Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl")) comp := finalizer.ClosingWith(&finalize, Must(repo.LookupComponent(COMPONENT))) @@ -222,7 +223,7 @@ var _ = Describe("component repository mapping", func() { // create repository repo := finalizer.ClosingWith(&finalize, Must(ctx.RepositoryForSpec(spec))) - impl := Must(cpi.GetRepositoryImplementation(repo)) + impl := Must(repocpi.GetRepositoryImplementation(repo)) Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl")) ocirepo := genericocireg.GetOCIRepository(repo) Expect(ocirepo).NotTo(BeNil()) @@ -278,7 +279,7 @@ var _ = Describe("component repository mapping", func() { defer Defer(finalize.Finalize, "finalize open elements") repo := finalizer.ClosingWith(&finalize, Must(DefaultContext.RepositoryForSpec(spec))) - impl := Must(cpi.GetRepositoryImplementation(repo)) + impl := Must(repocpi.GetRepositoryImplementation(repo)) Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl")) nested := finalize.Nested() diff --git a/pkg/contexts/ocm/repositories/genericocireg/repository.go b/pkg/contexts/ocm/repositories/genericocireg/repository.go index 05ec84b968..91d4d0e76a 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repository.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repository.go @@ -15,6 +15,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/oci" ocicpi "github.com/open-component-model/ocm/pkg/contexts/oci/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg/componentmapping" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/refmgmt" @@ -29,7 +30,7 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository { if o, ok := r.(OCIBasedRepository); ok { return o.OCIRepository() } - impl, err := cpi.GetRepositoryImplementation(r) + impl, err := repocpi.GetRepositoryImplementation(r) if err != nil { return nil } @@ -39,7 +40,7 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository { return nil } -type _RepositoryImplBase = cpi.RepositoryImplBase +type _RepositoryImplBase = repocpi.RepositoryImplBase type RepositoryImpl struct { _RepositoryImplBase @@ -49,18 +50,18 @@ type RepositoryImpl struct { } var ( - _ cpi.RepositoryImpl = (*RepositoryImpl)(nil) + _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil) _ credentials.ConsumerIdentityProvider = (*RepositoryImpl)(nil) ) func NewRepository(ctx cpi.Context, meta *ComponentRepositoryMeta, ocirepo oci.Repository) (cpi.Repository, error) { impl := &RepositoryImpl{ - _RepositoryImplBase: *cpi.NewRepositoryImplBase(ctx.OCMContext()), + _RepositoryImplBase: *repocpi.NewRepositoryImplBase(ctx.OCMContext()), meta: *DefaultComponentRepositoryMeta(meta), ocirepo: ocirepo, } - impl.nonref = cpi.NewNoneRefRepositoryView(impl) - r := cpi.NewRepository(impl, "OCM repo[OCI]") + impl.nonref = repocpi.NewNoneRefRepositoryView(impl) + r := repocpi.NewRepository(impl, "OCM repo[OCI]") return r, nil } @@ -179,6 +180,7 @@ func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cp return nil, err } defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed. + refmgmt.AllocLog.Trace("OCM Repo[OCI]: lookup version for temporary component ref", "component", name, "version", version) return c.LookupVersion(version) } diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index 3cd11b627e..e68d9e0a9c 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -8,30 +8,30 @@ import ( "fmt" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/utils" ) -type _ComponentAccessImplBase = cpi.ComponentAccessImplBase +type _componentAccessImplBase = repocpi.ComponentAccessImplBase type componentAccessImpl struct { - _ComponentAccessImplBase + _componentAccessImplBase repo *RepositoryImpl name string } func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.ComponentAccess, error) { - base, err := cpi.NewComponentAccessImplBase(repo.GetContext(), name, repo) + base, err := repocpi.NewComponentAccessImplBase(repo.GetContext(), name, repo) if err != nil { return nil, err } impl := &componentAccessImpl{ - _ComponentAccessImplBase: *base, + _componentAccessImplBase: *base, repo: repo, name: name, } - return cpi.NewComponentAccess(impl, "OCM component[Simple]"), nil + return repocpi.NewComponentAccess(impl, "OCM component[Simple]"), nil } func (c *componentAccessImpl) ListVersions() ([]string, error) { @@ -54,7 +54,7 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio if !ok { return nil, cpi.ErrComponentVersionNotFoundWrap(err, c.name, version) } - v, err := c._ComponentAccessImplBase.View() + v, err := c._componentAccessImplBase.View() if err != nil { return nil, err } @@ -64,7 +64,7 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio } func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess) *ComponentVersionContainer { - mine, _ := support.GetComponentVersionContainer[*ComponentVersionContainer](access) + mine, _ := repocpi.GetComponentVersionImpl[*ComponentVersionContainer](access) if mine == nil || mine.comp != c { return nil } diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index 0e755e0a7f..91101df04f 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -13,7 +13,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/refmgmt" ) @@ -28,25 +28,25 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis if err != nil { return nil, err } - impl, err := support.NewComponentVersionAccessImpl(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) + impl, err := repocpi.NewComponentVersionAccessBase(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) if err != nil { c.Close() return nil, err } - return cpi.NewComponentVersionAccess(impl), nil + return repocpi.NewComponentVersionAccess(impl), nil } // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl support.ComponentVersionAccessImpl + impl repocpi.ComponentVersionAccessBase comp *componentAccessImpl version string access VersionAccess } -var _ support.ComponentVersionContainer = (*ComponentVersionContainer)(nil) +var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil) func newComponentVersionContainer(comp *componentAccessImpl, version string, access VersionAccess) (*ComponentVersionContainer, error) { return &ComponentVersionContainer{ @@ -56,11 +56,11 @@ func newComponentVersionContainer(comp *componentAccessImpl, version string, acc }, nil } -func (c *ComponentVersionContainer) SetImplementation(impl support.ComponentVersionAccessImpl) { +func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentViewManager() cpi.ComponentAccessViewManager { +func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { return c.comp } diff --git a/pkg/contexts/ocm/repositories/virtual/repository.go b/pkg/contexts/ocm/repositories/virtual/repository.go index 69f5465161..d244dfad50 100644 --- a/pkg/contexts/ocm/repositories/virtual/repository.go +++ b/pkg/contexts/ocm/repositories/virtual/repository.go @@ -6,10 +6,11 @@ package virtual import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/refmgmt" ) -type _RepositoryImplBase = cpi.RepositoryImplBase +type _RepositoryImplBase = repocpi.RepositoryImplBase type RepositoryImpl struct { _RepositoryImplBase @@ -17,15 +18,15 @@ type RepositoryImpl struct { nonref cpi.Repository } -var _ cpi.RepositoryImpl = (*RepositoryImpl)(nil) +var _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil) func NewRepository(ctx cpi.Context, acc Access) cpi.Repository { impl := &RepositoryImpl{ - _RepositoryImplBase: *cpi.NewRepositoryImplBase(ctx.OCMContext()), + _RepositoryImplBase: *repocpi.NewRepositoryImplBase(ctx.OCMContext()), access: acc, } - impl.nonref = cpi.NewNoneRefRepositoryView(impl) - r := cpi.NewRepository(impl, "OCM repo[Simple]") + impl.nonref = repocpi.NewNoneRefRepositoryView(impl) + r := repocpi.NewRepository(impl, "OCM repo[Simple]") return r } diff --git a/pkg/refmgmt/refcloser.go b/pkg/refmgmt/refcloser.go index 904c5ad40b..4b1e1fea20 100644 --- a/pkg/refmgmt/refcloser.go +++ b/pkg/refmgmt/refcloser.go @@ -115,6 +115,7 @@ func CloseTemporary(c io.Closer) error { if !Lazy(c) { return errors.ErrNotSupported("lazy mode") } + AllocLog.Trace("close temporary ref") return c.Close() } diff --git a/pkg/refmgmt/refmgmt.go b/pkg/refmgmt/refmgmt.go index 5326965b4d..211d4b1654 100644 --- a/pkg/refmgmt/refmgmt.go +++ b/pkg/refmgmt/refmgmt.go @@ -14,7 +14,7 @@ import ( var ALLOC_REALM = logging.DefineSubRealm("reference counting", "refcnt") -var allocLog = logging.DynamicLogger(ALLOC_REALM) +var AllocLog = logging.DynamicLogger(ALLOC_REALM) type Allocatable interface { Ref() error @@ -83,7 +83,7 @@ func (c *refMgmt) Ref() error { return ErrClosed } c.refcount++ - allocLog.Trace("ref", "name", c.name, "refcnt", c.refcount) + AllocLog.Trace("ref", "name", c.name, "refcnt", c.refcount) return nil } @@ -97,7 +97,7 @@ func (c *refMgmt) Unref() error { var err error c.refcount-- - allocLog.Trace("unref", "name", c.name, "refcnt", c.refcount) + AllocLog.Trace("unref", "name", c.name, "refcnt", c.refcount) if c.refcount <= 0 { for _, f := range c.before { f.Cleanup() @@ -135,13 +135,14 @@ func (c *refMgmt) UnrefLast() error { } if c.refcount > 1 { + AllocLog.Trace("unref last failed", "name", c.name, "pending", c.refcount) return errors.ErrStillInUseWrap(errors.Newf("%d reference(s) pending", c.refcount), c.name) } var err error c.refcount-- - allocLog.Trace("unref last", "name", c.name, "refcnt", c.refcount) + AllocLog.Trace("unref last", "name", c.name, "refcnt", c.refcount) if c.refcount <= 0 { for _, f := range c.before { f.Cleanup() @@ -154,7 +155,7 @@ func (c *refMgmt) UnrefLast() error { } if err != nil { - allocLog.Trace("cleanup last failed", "name", c.name, "error", err.Error()) + AllocLog.Trace("cleanup last failed", "name", c.name, "error", err.Error()) return errors.Wrapf(err, "unable to cleanup %s while unref last", c.name) } From 7f450549ce02daa92eb9fcc18f766b932c3a17e4 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Nov 2023 02:20:18 -0800 Subject: [PATCH 02/18] split view into separate files --- pkg/contexts/ocm/cpi/repocpi/base_r.go | 37 ++++ .../ocm/cpi/repocpi/helperinterfaces.go | 8 + pkg/contexts/ocm/cpi/repocpi/view.go | 167 ------------------ pkg/contexts/ocm/cpi/repocpi/view_r.go | 142 +++++++++++++++ 4 files changed, 187 insertions(+), 167 deletions(-) create mode 100644 pkg/contexts/ocm/cpi/repocpi/base_r.go delete mode 100644 pkg/contexts/ocm/cpi/repocpi/view.go create mode 100644 pkg/contexts/ocm/cpi/repocpi/view_r.go diff --git a/pkg/contexts/ocm/cpi/repocpi/base_r.go b/pkg/contexts/ocm/cpi/repocpi/base_r.go new file mode 100644 index 0000000000..1da391d6c5 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/base_r.go @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "io" + + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" +) + +type RepositoryImpl interface { + resource.ResourceImplementation[cpi.Repository] + internal.RepositoryImpl +} + +type _RepositoryImplBase = resource.ResourceImplBase[cpi.Repository] + +type RepositoryImplBase struct { + *_RepositoryImplBase + ctx cpi.Context +} + +func (b *RepositoryImplBase) GetContext() cpi.Context { + return b.ctx +} + +func NewRepositoryImplBase(ctx cpi.Context, closer ...io.Closer) *RepositoryImplBase { + base, _ := resource.NewResourceImplBase[cpi.Repository, io.Closer](nil, closer...) + return &RepositoryImplBase{ + _RepositoryImplBase: base, + ctx: ctx, + } +} diff --git a/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go index 47cbba36e1..2d3a71812d 100644 --- a/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go +++ b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go @@ -5,7 +5,15 @@ package repocpi import ( + "fmt" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" +) + +var ( + ErrClosed = resource.ErrClosed + ErrTempVersion = fmt.Errorf("temporary component version cannot be updated") ) // BlobContainer is the interface for an element capable to store blobs. diff --git a/pkg/contexts/ocm/cpi/repocpi/view.go b/pkg/contexts/ocm/cpi/repocpi/view.go deleted file mode 100644 index 86931ed7fa..0000000000 --- a/pkg/contexts/ocm/cpi/repocpi/view.go +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. -// -// SPDX-License-Identifier: Apache-2.0 - -package repocpi - -import ( - "fmt" - "io" - - "github.com/open-component-model/ocm/pkg/contexts/credentials" - cpi2 "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" - "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/refmgmt" - "github.com/open-component-model/ocm/pkg/refmgmt/resource" - "github.com/open-component-model/ocm/pkg/utils" -) - -// View objects are the user facing generic implementations of the context interfaces. -// They are responsible to handle the reference counting and use -// shared implementations objects for th concrete type-specific implementations. -// Additionally, they are used to implement interface functionality which is -// common to all implementations and NOT dependent on the backend system technology. - -var ( - ErrClosed = resource.ErrClosed - ErrTempVersion = fmt.Errorf("temporary component version cannot be updated") -) - -//////////////////////////////////////////////////////////////////////////////// - -type _RepositoryView interface { - resource.ResourceViewInt[cpi2.Repository] // here you have to redeclare -} - -type RepositoryViewManager = resource.ViewManager[cpi2.Repository] // here you have to use an alias - -type RepositoryImpl interface { - resource.ResourceImplementation[cpi2.Repository] - internal.RepositoryImpl -} - -type _RepositoryImplBase = resource.ResourceImplBase[cpi2.Repository] - -type RepositoryImplBase struct { - _RepositoryImplBase - ctx cpi2.Context -} - -func (b *RepositoryImplBase) GetContext() cpi2.Context { - return b.ctx -} - -func NewRepositoryImplBase(ctx cpi2.Context, closer ...io.Closer) *RepositoryImplBase { - base, _ := resource.NewResourceImplBase[cpi2.Repository, io.Closer](nil, closer...) - return &RepositoryImplBase{ - _RepositoryImplBase: *base, - ctx: ctx, - } -} - -type repositoryView struct { - _RepositoryView - impl RepositoryImpl -} - -var ( - _ cpi2.Repository = (*repositoryView)(nil) - _ credentials.ConsumerIdentityProvider = (*repositoryView)(nil) - _ utils.Unwrappable = (*repositoryView)(nil) -) - -func GetRepositoryImplementation(n cpi2.Repository) (RepositoryImpl, error) { - if v, ok := n.(*repositoryView); ok { - return v.impl, nil - } - return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) -} - -func repositoryViewCreator(i RepositoryImpl, v resource.CloserView, d RepositoryViewManager) cpi2.Repository { - return &repositoryView{ - _RepositoryView: resource.NewView[cpi2.Repository](v, d), - impl: i, - } -} - -// NewNoneRefRepositoryView provides a repository reflecting the state of the -// view manager without holding an additional reference. -func NewNoneRefRepositoryView(i RepositoryImpl) cpi2.Repository { - return &repositoryView{ - _RepositoryView: resource.NewView[cpi2.Repository](resource.NewNonRefView[cpi2.Repository](i), i), - impl: i, - } -} - -func NewRepository(impl RepositoryImpl, name ...string) cpi2.Repository { - return resource.NewResource[cpi2.Repository](impl, repositoryViewCreator, utils.OptionalDefaulted("OCM repo", name...), true) -} - -func (r *repositoryView) Unwrap() interface{} { - return r.impl -} - -func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { - return credentials.GetProvidedConsumerId(r.impl, uctx...) -} - -func (r *repositoryView) GetIdentityMatcher() string { - return credentials.GetProvidedIdentityMatcher(r.impl) -} - -func (r *repositoryView) GetSpecification() cpi2.RepositorySpec { - return r.impl.GetSpecification() -} - -func (r *repositoryView) GetContext() cpi2.Context { - return r.impl.GetContext() -} - -func (r *repositoryView) ComponentLister() cpi2.ComponentLister { - return r.impl.ComponentLister() -} - -func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) { - err = r.Execute(func() error { - ok, err = r.impl.ExistsComponentVersion(name, version) - return err - }) - return ok, err -} - -func (r *repositoryView) LookupComponentVersion(name string, version string) (acc cpi2.ComponentVersionAccess, err error) { - err = r.Execute(func() error { - acc, err = r.impl.LookupComponentVersion(name, version) - return err - }) - return acc, err -} - -func (r *repositoryView) LookupComponent(name string) (acc cpi2.ComponentAccess, err error) { - err = r.Execute(func() error { - acc, err = r.impl.LookupComponent(name) - return err - }) - return acc, err -} - -func (r *repositoryView) NewComponentVersion(comp, vers string, overrides ...bool) (cpi2.ComponentVersionAccess, error) { - c, err := refmgmt.ToLazy(r.LookupComponent(comp)) - if err != nil { - return nil, err - } - defer c.Close() - - return c.NewVersion(vers, overrides...) -} - -func (r *repositoryView) AddComponentVersion(cv cpi2.ComponentVersionAccess, overrides ...bool) error { - c, err := refmgmt.ToLazy(r.LookupComponent(cv.GetName())) - if err != nil { - return err - } - defer c.Close() - - return c.AddVersion(cv, overrides...) -} diff --git a/pkg/contexts/ocm/cpi/repocpi/view_r.go b/pkg/contexts/ocm/cpi/repocpi/view_r.go new file mode 100644 index 0000000000..573060cac2 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/view_r.go @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "fmt" + + "github.com/open-component-model/ocm/pkg/contexts/credentials" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" + "github.com/open-component-model/ocm/pkg/refmgmt/resource" + "github.com/open-component-model/ocm/pkg/utils" +) + +// View objects are the user facing generic implementations of the context interfaces. +// They are responsible to handle the reference counting and use +// shared implementations objects for th concrete type-specific implementations. +// Additionally, they are used to implement interface functionality which is +// common to all implementations and NOT dependent on the backend system technology. + +//////////////////////////////////////////////////////////////////////////////// + +type _repositoryView interface { + resource.ResourceViewInt[cpi.Repository] // here you have to redeclare +} + +type RepositoryViewManager = resource.ViewManager[cpi.Repository] // here you have to use an alias + +type RepositoryBase interface { + resource.ResourceImplementation[cpi.Repository] + internal.RepositoryImpl +} + +type repositoryView struct { + _repositoryView + base RepositoryImpl +} + +var ( + _ cpi.Repository = (*repositoryView)(nil) + _ credentials.ConsumerIdentityProvider = (*repositoryView)(nil) + _ utils.Unwrappable = (*repositoryView)(nil) +) + +func GetRepositoryImplementation(n cpi.Repository) (RepositoryImpl, error) { + if v, ok := n.(*repositoryView); ok { + return v.base, nil + } + return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) +} + +func repositoryViewCreator(i RepositoryBase, v resource.CloserView, d RepositoryViewManager) cpi.Repository { + return &repositoryView{ + _repositoryView: resource.NewView[cpi.Repository](v, d), + base: i, + } +} + +// NewNoneRefRepositoryView provides a repository reflecting the state of the +// view manager without holding an additional reference. +func NewNoneRefRepositoryView(i RepositoryBase) cpi.Repository { + return &repositoryView{ + _repositoryView: resource.NewView[cpi.Repository](resource.NewNonRefView[cpi.Repository](i), i), + base: i, + } +} + +func NewRepository(impl RepositoryBase, name ...string) cpi.Repository { + return resource.NewResource[cpi.Repository](impl, repositoryViewCreator, utils.OptionalDefaulted("OCM repo", name...), true) +} + +func (r *repositoryView) Unwrap() interface{} { + return r.base +} + +func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { + return credentials.GetProvidedConsumerId(r.base, uctx...) +} + +func (r *repositoryView) GetIdentityMatcher() string { + return credentials.GetProvidedIdentityMatcher(r.base) +} + +func (r *repositoryView) GetSpecification() cpi.RepositorySpec { + return r.base.GetSpecification() +} + +func (r *repositoryView) GetContext() cpi.Context { + return r.base.GetContext() +} + +func (r *repositoryView) ComponentLister() cpi.ComponentLister { + return r.base.ComponentLister() +} + +func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) { + err = r.Execute(func() error { + ok, err = r.base.ExistsComponentVersion(name, version) + return err + }) + return ok, err +} + +func (r *repositoryView) LookupComponentVersion(name string, version string) (acc cpi.ComponentVersionAccess, err error) { + err = r.Execute(func() error { + acc, err = r.base.LookupComponentVersion(name, version) + return err + }) + return acc, err +} + +func (r *repositoryView) LookupComponent(name string) (acc cpi.ComponentAccess, err error) { + err = r.Execute(func() error { + acc, err = r.base.LookupComponent(name) + return err + }) + return acc, err +} + +func (r *repositoryView) NewComponentVersion(comp, vers string, overrides ...bool) (cpi.ComponentVersionAccess, error) { + c, err := refmgmt.ToLazy(r.LookupComponent(comp)) + if err != nil { + return nil, err + } + defer c.Close() + + return c.NewVersion(vers, overrides...) +} + +func (r *repositoryView) AddComponentVersion(cv cpi.ComponentVersionAccess, overrides ...bool) error { + c, err := refmgmt.ToLazy(r.LookupComponent(cv.GetName())) + if err != nil { + return err + } + defer c.Close() + + return c.AddVersion(cv, overrides...) +} From a624cbb7e26d4ba58ba86da14ee6ef055797f5ee Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Nov 2023 02:49:42 -0800 Subject: [PATCH 03/18] align CV creation from implementation --- pkg/contexts/ocm/cpi/repocpi/base_cv.go | 2 +- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 8 ++++++-- .../ocm/repositories/comparch/componentarchive.go | 7 ++++--- pkg/contexts/ocm/repositories/comparch/repository.go | 7 +------ .../ocm/repositories/genericocireg/componentversion.go | 7 +------ pkg/contexts/ocm/repositories/virtual/componentversion.go | 7 +------ 6 files changed, 14 insertions(+), 24 deletions(-) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index a2a0058c6c..7e12165305 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -64,7 +64,7 @@ type componentVersionAccessBase struct { var _ ComponentVersionAccessBase = (*componentVersionAccessBase)(nil) -func NewComponentVersionAccessBase(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBase, error) { +func newComponentVersionAccessBase(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBase, error) { base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess](impl.GetParentViewManager(), closer...) if err != nil { return nil, err diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index 8a023ce1cd..2f74af3988 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -125,8 +125,12 @@ func artifactAccessViewCreator(i ComponentVersionAccessBase, v resource.CloserVi return cv } -func NewComponentVersionAccess(impl ComponentVersionAccessBase) cpi.ComponentVersionAccess { - return resource.NewResource[cpi.ComponentVersionAccess](impl, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", impl.GetName(), impl.GetVersion()), true) +func NewComponentVersionAccess(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (cpi.ComponentVersionAccess, error) { + base, err := newComponentVersionAccessBase(name, version, impl, lazy, persistent, direct, closer...) + if err != nil { + return nil, errors.Join(err, impl.Close()) + } + return resource.NewResource[cpi.ComponentVersionAccess](base, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", name, version), true), nil } func (c *componentVersionAccessView) Unwrap() interface{} { diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index aa12c15d2d..5e99ebb8e9 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -48,17 +48,18 @@ func _Wrap(ctx cpi.ContextProvider, obj *accessobj.AccessObject, spec *Repositor s := &componentArchiveContainer{ ctx: ctx.OCMContext(), base: accessobj.NewFileSystemBlobAccess(obj), + spec: spec, } - impl, err := repocpi.NewComponentVersionAccessBase(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true) + cv, err := repocpi.NewComponentVersionAccess(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true) if err != nil { return nil, err } - s.spec = spec + arch := &ComponentArchive{ spec: spec, container: s, } - arch.ComponentVersionAccess = repocpi.NewComponentVersionAccess(impl) + arch.ComponentVersionAccess = cv arch.main, arch.nonref = newRepository(arch) s.repo = arch.nonref return arch, nil diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index eb00510520..5e2ca39db0 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -240,12 +240,7 @@ func newComponentVersionAccess(comp *ComponentAccessImpl, version string, persis if err != nil { return nil, err } - impl, err := repocpi.NewComponentVersionAccessBase(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) - if err != nil { - c.Close() - return nil, err - } - return repocpi.NewComponentVersionAccess(impl), nil + return repocpi.NewComponentVersionAccess(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) } func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionContainer, error) { diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index ffd50c8397..99d2135a56 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -39,12 +39,7 @@ func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessI if err != nil { return nil, err } - impl, err := repocpi.NewComponentVersionAccessBase(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) - if err != nil { - c.Close() - return nil, err - } - return repocpi.NewComponentVersionAccess(impl), nil + return repocpi.NewComponentVersionAccess(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) } // ////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index 91101df04f..f8718f80e0 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -28,12 +28,7 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis if err != nil { return nil, err } - impl, err := repocpi.NewComponentVersionAccessBase(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) - if err != nil { - c.Close() - return nil, err - } - return repocpi.NewComponentVersionAccess(impl), nil + return repocpi.NewComponentVersionAccess(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) } // ////////////////////////////////////////////////////////////////////////////// From 47d48a8413c7c867794604db777c99dbdcdcaef8 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Nov 2023 07:49:47 -0800 Subject: [PATCH 04/18] work --- pkg/contexts/ocm/cpi/repocpi/base_c.go | 90 +++++++++++++++---- pkg/contexts/ocm/cpi/repocpi/view_c.go | 13 ++- .../ocm/repositories/comparch/repository.go | 41 +++++---- .../repositories/genericocireg/component.go | 44 ++++----- .../genericocireg/componentversion.go | 2 +- .../ocm/repositories/virtual/component.go | 50 +++++++---- .../repositories/virtual/componentversion.go | 8 +- pkg/refmgmt/resource/resource.go | 4 + 8 files changed, 172 insertions(+), 80 deletions(-) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go index 54fe929977..c3be2d6e2d 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -9,47 +9,103 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" ) // ComponentAccessImpl is the provider implementation // interface for component versions. type ComponentAccessImpl interface { - resource.ResourceImplementation[cpi.ComponentAccess] - internal.ComponentAccessImpl + SetImplementation(base ComponentAccessBase) + GetParentViewManager() RepositoryViewManager - IsReadOnly() bool + GetContext() cpi.Context GetName() string + IsReadOnly() bool - IsOwned(access cpi.ComponentVersionAccess) bool - + ListVersions() ([]string, error) + LookupVersion(version string) (cpi.ComponentVersionAccess, error) + HasVersion(vers string) (bool, error) + NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) AddVersion(cv cpi.ComponentVersionAccess) error + + io.Closer } -type _ComponentAccessImplBase = resource.ResourceImplBase[cpi.ComponentAccess] +type _componentAccessImplBase = resource.ResourceImplBase[cpi.ComponentAccess] -type ComponentAccessImplBase struct { - *_ComponentAccessImplBase +type componentAccessBase struct { + *_componentAccessImplBase ctx cpi.Context name string + impl ComponentAccessImpl } -func NewComponentAccessImplBase(ctx cpi.Context, name string, repo RepositoryViewManager, closer ...io.Closer) (*ComponentAccessImplBase, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentAccess](repo, closer...) +func newComponentAccessImplBase(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessBase, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentAccess](impl.GetParentViewManager(), closer...) if err != nil { return nil, err } - return &ComponentAccessImplBase{ - _ComponentAccessImplBase: base, - ctx: ctx, - name: name, - }, nil + b := &componentAccessBase{ + _componentAccessImplBase: base, + ctx: impl.GetContext(), + name: impl.GetName(), + impl: impl, + } + impl.SetImplementation(b) + return b, nil +} + +func (b *componentAccessBase) Close() error { + list := errors.ErrListf("closing component %s", b.name) + refmgmt.AllocLog.Trace("closing component base", "name", b.name) + list.Add(b.impl.Close()) + list.Add(b._componentAccessImplBase.Close()) + refmgmt.AllocLog.Trace("closed component base", "name", b.name) + return list.Result() } -func (b *ComponentAccessImplBase) GetContext() cpi.Context { +func (b *componentAccessBase) GetContext() cpi.Context { return b.ctx } -func (b *ComponentAccessImplBase) GetName() string { +func (b *componentAccessBase) GetName() string { return b.name } + +func (c *componentAccessBase) IsOwned(cv cpi.ComponentVersionAccess) bool { + base, err := GetComponentVersionAccessBase(cv) + if err != nil { + return false + } + + impl := base.(*componentVersionAccessBase).impl + cvcompmgr := impl.GetParentViewManager() + mymgr := c._componentAccessImplBase + return mymgr == cvcompmgr +} + +func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess) error { + return c.impl.AddVersion(cv) +} + +func (b *componentAccessBase) ListVersions() ([]string, error) { + return b.impl.ListVersions() +} + +func (b *componentAccessBase) LookupVersion(version string) (internal.ComponentVersionAccess, error) { + return b.impl.LookupVersion(version) +} + +func (b *componentAccessBase) HasVersion(vers string) (bool, error) { + return b.impl.HasVersion(vers) +} + +func (b *componentAccessBase) NewVersion(version string, overrides ...bool) (internal.ComponentVersionAccess, error) { + return b.impl.NewVersion(version, overrides...) +} + +func (b *componentAccessBase) IsReadOnly() bool { + return b.impl.IsReadOnly() +} diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index 1ea93335c1..1170b42ca3 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -6,6 +6,7 @@ package repocpi import ( "fmt" + "io" "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" @@ -60,8 +61,16 @@ func componentAccessViewCreator(i ComponentAccessBase, v resource.CloserView, d } } -func NewComponentAccess(impl ComponentAccessBase, kind ...string) cpi.ComponentAccess { - return resource.NewResource[cpi.ComponentAccess](impl, componentAccessViewCreator, fmt.Sprintf("%s %s", utils.OptionalDefaulted("component", kind...), impl.GetName()), true) +func NewComponentAccess(impl ComponentAccessImpl, kind string, closer ...io.Closer) (cpi.ComponentAccess, error) { + base, err := newComponentAccessImplBase(impl, closer...) + if err != nil { + return nil, errors.Join(err, impl.Close()) + } + if kind == "" { + kind = "component" + } + cv := resource.NewResource[cpi.ComponentAccess](base, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), true) + return cv, nil } func (c *componentAccessView) Unwrap() interface{} { diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 5e2ca39db0..a713363978 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -150,25 +150,38 @@ func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, erro //////////////////////////////////////////////////////////////////////////////// -type _ComponentAccessImplBase = repocpi.ComponentAccessImplBase - type ComponentAccessImpl struct { - _ComponentAccessImplBase + base repocpi.ComponentAccessBase repo *RepositoryImpl } -var _ repocpi.ComponentAccessBase = (*ComponentAccessImpl)(nil) +var _ repocpi.ComponentAccessImpl = (*ComponentAccessImpl)(nil) func newComponentAccess(r *RepositoryImpl) (cpi.ComponentAccess, error) { - base, err := repocpi.NewComponentAccessImplBase(r.GetContext(), r.arch.GetName(), r) - if err != nil { - return nil, err - } impl := &ComponentAccessImpl{ - _ComponentAccessImplBase: *base, - repo: r, + repo: r, } - return repocpi.NewComponentAccess(impl, "component archive"), nil + return repocpi.NewComponentAccess(impl, "component archive") +} + +func (c *ComponentAccessImpl) Close() error { + return nil +} + +func (c *ComponentAccessImpl) SetImplementation(base repocpi.ComponentAccessBase) { + c.base = base +} + +func (c *ComponentAccessImpl) GetParentViewManager() repocpi.RepositoryViewManager { + return c.repo +} + +func (c *ComponentAccessImpl) GetContext() cpi.Context { + return c.repo.GetContext() +} + +func (c *ComponentAccessImpl) GetName() string { + return c.repo.arch.GetName() } func (c *ComponentAccessImpl) IsReadOnly() bool { @@ -198,10 +211,6 @@ func (c *ComponentAccessImpl) container(access cpi.ComponentVersionAccess) *comp return mine.comp.repo.arch.container } -func (c *ComponentAccessImpl) IsOwned(access cpi.ComponentVersionAccess) bool { - return c.container(access) == c.repo.arch.container -} - func (c *ComponentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error { if access.GetName() != c.GetName() { return errors.ErrInvalid("component name", access.GetName()) @@ -255,7 +264,7 @@ func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVers } func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { - return c.comp + return c.comp.base } func (c *ComponentVersionContainer) Close() error { diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index d8d066b943..ced15b85ed 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -12,7 +12,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/refmgmt" - "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" @@ -25,10 +24,8 @@ const META_SEPARATOR = ".build-" //////////////////////////////////////////////////////////////////////////////// -type _ComponentAccessImplBase = repocpi.ComponentAccessImplBase - type componentAccessImpl struct { - _ComponentAccessImplBase + base repocpi.ComponentAccessBase repo *RepositoryImpl name string namespace oci.NamespaceAccess @@ -39,32 +36,41 @@ func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.Compo if err != nil { return nil, err } - - base, err := repocpi.NewComponentAccessImplBase(repo.GetContext(), name, repo) - if err != nil { - return nil, err - } namespace, err := repo.ocirepo.LookupNamespace(mapped) if err != nil { - base.Close() return nil, err } impl := &componentAccessImpl{ - _ComponentAccessImplBase: *base, - repo: repo, - name: name, - namespace: namespace, + repo: repo, + name: name, + namespace: namespace, } - return repocpi.NewComponentAccess(impl, "OCM component[OCI]"), nil + return repocpi.NewComponentAccess(impl, "OCM component[OCI]") } func (c *componentAccessImpl) Close() error { refmgmt.AllocLog.Trace("closing component [OCI]", "name", c.name) - err := accessio.Close(c.namespace, c._ComponentAccessImplBase) + err := c.namespace.Close() refmgmt.AllocLog.Trace("closed component [OCI]", "name", c.name) return err } +func (c *componentAccessImpl) SetImplementation(base repocpi.ComponentAccessBase) { + c.base = base +} + +func (c *componentAccessImpl) GetParentViewManager() repocpi.RepositoryViewManager { + return c.repo +} + +func (c *componentAccessImpl) GetContext() cpi.Context { + return c.repo.GetContext() +} + +func (c *componentAccessImpl) GetName() string { + return c.name +} + //////////////////////////////////////////////////////////////////////////////// func toTag(v string) string { @@ -151,10 +157,6 @@ func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess return mine } -func (c *componentAccessImpl) IsOwned(access cpi.ComponentVersionAccess) bool { - return c.versionContainer(access) != nil -} - func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error { if access.GetName() != c.GetName() { return errors.ErrInvalid("component name", access.GetName()) @@ -172,7 +174,7 @@ func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) erro } func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { - v, err := c.View(false) + v, err := c.base.View(false) if err != nil { return nil, err } diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 99d2135a56..87f1ff45f4 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -81,7 +81,7 @@ func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVers } func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { - return c.comp + return c.comp.base } func (c *ComponentVersionContainer) Close() error { diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index e68d9e0a9c..0d275c729b 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -13,25 +13,41 @@ import ( "github.com/open-component-model/ocm/pkg/utils" ) -type _componentAccessImplBase = repocpi.ComponentAccessImplBase - type componentAccessImpl struct { - _componentAccessImplBase + base repocpi.ComponentAccessBase + repo *RepositoryImpl name string } +var _ repocpi.ComponentAccessImpl = (*componentAccessImpl)(nil) + func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.ComponentAccess, error) { - base, err := repocpi.NewComponentAccessImplBase(repo.GetContext(), name, repo) - if err != nil { - return nil, err - } impl := &componentAccessImpl{ - _componentAccessImplBase: *base, - repo: repo, - name: name, + repo: repo, + name: name, } - return repocpi.NewComponentAccess(impl, "OCM component[Simple]"), nil + return repocpi.NewComponentAccess(impl, "OCM component[Simple]") +} + +func (c *componentAccessImpl) Close() error { + return nil +} + +func (c *componentAccessImpl) SetImplementation(base repocpi.ComponentAccessBase) { + c.base = base +} + +func (c *componentAccessImpl) GetParentViewManager() repocpi.RepositoryViewManager { + return c.repo +} + +func (c *componentAccessImpl) GetContext() cpi.Context { + return c.repo.GetContext() +} + +func (c *componentAccessImpl) GetName() string { + return c.name } func (c *componentAccessImpl) ListVersions() ([]string, error) { @@ -54,7 +70,7 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio if !ok { return nil, cpi.ErrComponentVersionNotFoundWrap(err, c.name, version) } - v, err := c._componentAccessImplBase.View() + v, err := c.base.View() if err != nil { return nil, err } @@ -71,10 +87,6 @@ func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess return mine } -func (c *componentAccessImpl) IsOwned(access cpi.ComponentVersionAccess) bool { - return c.versionContainer(access) != nil -} - func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error { if access.GetName() != c.GetName() { return errors.ErrInvalid("component name", access.GetName()) @@ -83,14 +95,14 @@ func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) erro if mine == nil { return fmt.Errorf("cannot add component version: component version access %s not created for target", access.GetName()+":"+access.GetVersion()) } - mine.impl.EnablePersistence() + mine.base.EnablePersistence() // delayed update in close is not done for composition mode - return mine.impl.Update(!mine.impl.UseDirectAccess()) + return mine.base.Update(!mine.base.UseDirectAccess()) } func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { - v, err := c.View(false) + v, err := c.base.View(false) if err != nil { return nil, err } diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index f8718f80e0..6ef0704949 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -34,7 +34,7 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl repocpi.ComponentVersionAccessBase + base repocpi.ComponentVersionAccessBase comp *componentAccessImpl version string @@ -51,12 +51,12 @@ func newComponentVersionContainer(comp *componentAccessImpl, version string, acc }, nil } -func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { - c.impl = impl +func (c *ComponentVersionContainer) SetImplementation(base repocpi.ComponentVersionAccessBase) { + c.base = base } func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { - return c.comp + return c.comp.base } func (c *ComponentVersionContainer) Close() error { diff --git a/pkg/refmgmt/resource/resource.go b/pkg/refmgmt/resource/resource.go index 9b4f53c6fd..792ad2ea18 100644 --- a/pkg/refmgmt/resource/resource.go +++ b/pkg/refmgmt/resource/resource.go @@ -256,6 +256,10 @@ func (b *ResourceImplBase[T]) Allocatable() refmgmt.ExtendedAllocatable { return b.refs.Allocatable() } +func (b *ResourceImplBase[T]) ViewManager() ViewManager[T] { + return b.refs +} + func (b *ResourceImplBase[T]) View(main ...bool) (T, error) { return b.refs.View(main...) } From bf9a6b32f4416a216fa3a57a46f187a1f263eb7c Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Nov 2023 08:46:33 -0800 Subject: [PATCH 05/18] component implementation support --- pkg/contexts/ocm/cpi/repocpi/base_c.go | 13 +++--- pkg/contexts/ocm/cpi/repocpi/base_cv.go | 8 ++-- pkg/contexts/ocm/cpi/repocpi/doc.go | 45 +++++++++++++++++++ pkg/contexts/ocm/cpi/repocpi/view_cv.go | 2 +- .../repositories/comparch/componentarchive.go | 5 ++- .../ocm/repositories/comparch/repository.go | 8 ++-- .../ocm/repositories/ctf/repo_test.go | 2 +- .../repositories/genericocireg/component.go | 8 ++-- .../genericocireg/componentversion.go | 6 +-- .../repositories/genericocireg/repo_test.go | 2 +- .../ocm/repositories/virtual/component.go | 4 +- .../repositories/virtual/componentversion.go | 4 +- 12 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 pkg/contexts/ocm/cpi/repocpi/doc.go diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go index c3be2d6e2d..8cc3bbb757 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -17,8 +17,8 @@ import ( // ComponentAccessImpl is the provider implementation // interface for component versions. type ComponentAccessImpl interface { - SetImplementation(base ComponentAccessBase) - GetParentViewManager() RepositoryViewManager + SetBase(base ComponentAccessBase) + GetParentBase() RepositoryViewManager GetContext() cpi.Context GetName() string @@ -43,7 +43,7 @@ type componentAccessBase struct { } func newComponentAccessImplBase(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessBase, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentAccess](impl.GetParentViewManager(), closer...) + base, err := resource.NewResourceImplBase[cpi.ComponentAccess, cpi.Repository](impl.GetParentBase(), closer...) if err != nil { return nil, err } @@ -53,7 +53,7 @@ func newComponentAccessImplBase(impl ComponentAccessImpl, closer ...io.Closer) ( name: impl.GetName(), impl: impl, } - impl.SetImplementation(b) + impl.SetBase(b) return b, nil } @@ -81,9 +81,8 @@ func (c *componentAccessBase) IsOwned(cv cpi.ComponentVersionAccess) bool { } impl := base.(*componentVersionAccessBase).impl - cvcompmgr := impl.GetParentViewManager() - mymgr := c._componentAccessImplBase - return mymgr == cvcompmgr + cvcompmgr := impl.GetParentBase() + return c == cvcompmgr } func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess) error { diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index 7e12165305..ce9cef8676 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -22,8 +22,8 @@ import ( // interface for component versions. type ComponentVersionAccessImpl interface { GetContext() cpi.Context - SetImplementation(base ComponentVersionAccessBase) - GetParentViewManager() ComponentAccessViewManager + SetBase(base ComponentVersionAccessBase) + GetParentBase() ComponentAccessBase Repository() cpi.Repository @@ -65,7 +65,7 @@ type componentVersionAccessBase struct { var _ ComponentVersionAccessBase = (*componentVersionAccessBase)(nil) func newComponentVersionAccessBase(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBase, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess](impl.GetParentViewManager(), closer...) + base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess, cpi.ComponentAccess](impl.GetParentBase(), closer...) if err != nil { return nil, err } @@ -80,7 +80,7 @@ func newComponentVersionAccessBase(name, version string, impl ComponentVersionAc directAccess: direct, impl: impl, } - impl.SetImplementation(b) + impl.SetBase(b) return b, nil } diff --git a/pkg/contexts/ocm/cpi/repocpi/doc.go b/pkg/contexts/ocm/cpi/repocpi/doc.go new file mode 100644 index 0000000000..13dce2c962 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/doc.go @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +// Package repocpi contains the implementation support +// for repository backends. It offers three methods +// to create component version, component and repository +// objects based on three simple implementation interfaces. +// +// The basic provisioning model is layered: +// +// - on layer 1 there is the user facing API defined +// in package [github.com/open-component-model/ocm/pkg/contexts/ocm]. +// +// - on layer 2 (this package) there is a backend agnostic +// implementation od standard functionality based on layer 3. +// This is divided into two parts +// +// a) the view objects provided by the Dup() calls of the layer 1 API. +// All dups are internally based on a single base object. +// +// b) the base object for all dup views is used to implement some +// common functionality like the view management. The base object +// is closed, when the last view disappears. +// This base object the uses forwards calls to the final +// storage backend implementation interface. +// +// - the storage backend implementations based on the implementation +// interfaces provided by layer 2. +// +// The implementation interfaces and the functions to create API objects are: +// +// - interface [ComponentVersionAccessImpl] is used to create an ocm.ComponentVersionAccess object +// using the function [NewComponentVersionAccess]. +// - interface [ComponentAccessImpl] is used to create an ocm.ComponentAccess object +// using the function [NewComponentAccess]. +// - interface [RepositoryImpl] is used to create an ocm.ComponentAccess object +// using the function [NewRepository]. +// +// Component version implementations provide basic access to component versions +// and their descriptors. The keep a reference to component implementations, which are +// again based of repository implementations. The task of repository implementations is +// to provide component objects. Their implementations are the responsible to provide +// component version objects, +package repocpi diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index 2f74af3988..ff548fb702 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -10,7 +10,6 @@ import ( "io" "strconv" - "github.com/open-component-model/ocm/pkg/finalizer" "github.com/opencontainers/go-digest" "github.com/open-component-model/ocm/pkg/blobaccess" @@ -26,6 +25,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/finalizer" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" "github.com/open-component-model/ocm/pkg/utils" diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index 5e99ebb8e9..086ae25ceb 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -6,6 +6,7 @@ package comparch import ( "github.com/mandelsoft/vfs/pkg/vfs" + "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/common/accessobj" @@ -108,11 +109,11 @@ type componentArchiveContainer struct { var _ repocpi.ComponentVersionAccessImpl = (*componentArchiveContainer)(nil) -func (c *componentArchiveContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { +func (c *componentArchiveContainer) SetBase(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *componentArchiveContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { +func (c *componentArchiveContainer) GetParentBase() repocpi.ComponentAccessBase { return nil } diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index a713363978..89c46f7961 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -168,11 +168,11 @@ func (c *ComponentAccessImpl) Close() error { return nil } -func (c *ComponentAccessImpl) SetImplementation(base repocpi.ComponentAccessBase) { +func (c *ComponentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { c.base = base } -func (c *ComponentAccessImpl) GetParentViewManager() repocpi.RepositoryViewManager { +func (c *ComponentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { return c.repo } @@ -259,11 +259,11 @@ func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionC }, nil } -func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { +func (c *ComponentVersionContainer) SetBase(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { +func (c *ComponentVersionContainer) GetParentBase() repocpi.ComponentAccessBase { return c.comp.base } diff --git a/pkg/contexts/ocm/repositories/ctf/repo_test.go b/pkg/contexts/ocm/repositories/ctf/repo_test.go index ad6ca828a7..6553239d65 100644 --- a/pkg/contexts/ocm/repositories/ctf/repo_test.go +++ b/pkg/contexts/ocm/repositories/ctf/repo_test.go @@ -9,7 +9,6 @@ import ( . "github.com/onsi/gomega" . "github.com/open-component-model/ocm/pkg/contexts/ocm/testhelper" . "github.com/open-component-model/ocm/pkg/finalizer" - "github.com/open-component-model/ocm/pkg/refmgmt" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/mandelsoft/vfs/pkg/memoryfs" @@ -24,6 +23,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ctf" "github.com/open-component-model/ocm/pkg/contexts/ocm/resourcetypes" "github.com/open-component-model/ocm/pkg/mime" + "github.com/open-component-model/ocm/pkg/refmgmt" ) const COMPONENT = "github.com/mandelsoft/ocm" diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index ced15b85ed..950065240b 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -9,14 +9,14 @@ import ( "strings" "github.com/Masterminds/semver/v3" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" - "github.com/open-component-model/ocm/pkg/refmgmt" "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" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/utils" ) @@ -55,11 +55,11 @@ func (c *componentAccessImpl) Close() error { return err } -func (c *componentAccessImpl) SetImplementation(base repocpi.ComponentAccessBase) { +func (c *componentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { c.base = base } -func (c *componentAccessImpl) GetParentViewManager() repocpi.RepositoryViewManager { +func (c *componentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { return c.repo } diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 87f1ff45f4..3fd95adaa2 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -9,7 +9,6 @@ import ( "path" "strings" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/opencontainers/go-digest" "github.com/open-component-model/ocm/pkg/common" @@ -28,6 +27,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/generics" "github.com/open-component-model/ocm/pkg/refmgmt" @@ -76,11 +76,11 @@ func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAcce }, nil } -func (c *ComponentVersionContainer) SetImplementation(impl repocpi.ComponentVersionAccessBase) { +func (c *ComponentVersionContainer) SetBase(impl repocpi.ComponentVersionAccessBase) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { +func (c *ComponentVersionContainer) GetParentBase() repocpi.ComponentAccessBase { return c.comp.base } diff --git a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go index 70975fb68c..7d96fc9b32 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go @@ -11,7 +11,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" . "github.com/open-component-model/ocm/pkg/testutils" "github.com/mandelsoft/vfs/pkg/osfs" @@ -40,6 +39,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/digester/digesters/artifact" "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg" "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg/componentmapping" diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index 0d275c729b..0ca2c308d9 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -34,11 +34,11 @@ func (c *componentAccessImpl) Close() error { return nil } -func (c *componentAccessImpl) SetImplementation(base repocpi.ComponentAccessBase) { +func (c *componentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { c.base = base } -func (c *componentAccessImpl) GetParentViewManager() repocpi.RepositoryViewManager { +func (c *componentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { return c.repo } diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index 6ef0704949..3b9d9673d4 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -51,11 +51,11 @@ func newComponentVersionContainer(comp *componentAccessImpl, version string, acc }, nil } -func (c *ComponentVersionContainer) SetImplementation(base repocpi.ComponentVersionAccessBase) { +func (c *ComponentVersionContainer) SetBase(base repocpi.ComponentVersionAccessBase) { c.base = base } -func (c *ComponentVersionContainer) GetParentViewManager() repocpi.ComponentAccessViewManager { +func (c *ComponentVersionContainer) GetParentBase() repocpi.ComponentAccessBase { return c.comp.base } From 6c688c5864b7a74d3c6957074d4c1a780410122e Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Nov 2023 09:41:11 -0800 Subject: [PATCH 06/18] repository implementation support --- pkg/contexts/ocm/cpi/repocpi/base_cv.go | 8 ++ pkg/contexts/ocm/cpi/repocpi/base_r.go | 73 +++++++++++++++---- pkg/contexts/ocm/cpi/repocpi/view_c.go | 10 +++ pkg/contexts/ocm/cpi/repocpi/view_cv.go | 10 +++ pkg/contexts/ocm/cpi/repocpi/view_r.go | 36 +++++++-- .../repositories/comparch/componentarchive.go | 6 +- .../ocm/repositories/comparch/repository.go | 33 ++++++--- pkg/contexts/ocm/repositories/ctf/format.go | 4 +- .../repositories/genericocireg/component.go | 4 +- .../repositories/genericocireg/repository.go | 34 +++++---- .../ocm/repositories/genericocireg/type.go | 2 +- .../ocm/repositories/virtual/component.go | 2 +- .../ocm/repositories/virtual/repository.go | 28 +++---- pkg/refmgmt/resource/resource.go | 6 ++ 14 files changed, 187 insertions(+), 69 deletions(-) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index ce9cef8676..1fcd56ef34 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -40,6 +40,14 @@ type ComponentVersionAccessImpl interface { io.Closer } +type ComponentVersionAccessImplSupport struct { + Base ComponentVersionAccessBase +} + +func (b *ComponentVersionAccessImplSupport) SetBase(base ComponentVersionAccessBase) { + b.Base = base +} + type _componentVersionAccessImplBase = resource.ResourceImplBase[cpi.ComponentVersionAccess] // componentVersionAccessBase is the counterpart to views, all views diff --git a/pkg/contexts/ocm/cpi/repocpi/base_r.go b/pkg/contexts/ocm/cpi/repocpi/base_r.go index 1da391d6c5..a22d76cd89 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_r.go @@ -8,30 +8,75 @@ import ( "io" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" ) type RepositoryImpl interface { - resource.ResourceImplementation[cpi.Repository] - internal.RepositoryImpl + SetBase(base RepositoryBase) + + GetContext() cpi.Context + + GetSpecification() cpi.RepositorySpec + ComponentLister() cpi.ComponentLister + + ExistsComponentVersion(name string, version string) (bool, error) + LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) + LookupComponent(name string) (cpi.ComponentAccess, error) + + io.Closer } -type _RepositoryImplBase = resource.ResourceImplBase[cpi.Repository] +type _repositoryImplBase = resource.ResourceImplBase[cpi.Repository] + +type repositoryBase struct { + *_repositoryImplBase + ctx cpi.Context + kind string + impl RepositoryImpl +} + +func newRepositoryImplBase(impl RepositoryImpl, kind string, closer ...io.Closer) RepositoryBase { + base := resource.NewSimpleResourceImplBase[cpi.Repository](closer...) + b := &repositoryBase{ + _repositoryImplBase: base, + ctx: impl.GetContext(), + impl: impl, + } + impl.SetBase(b) + return b +} -type RepositoryImplBase struct { - *_RepositoryImplBase - ctx cpi.Context +func (b *repositoryBase) Close() error { + list := errors.ErrListf("closing %s", b.kind) + refmgmt.AllocLog.Trace("closing repository base", "kind", b.kind) + list.Add(b.impl.Close()) + list.Add(b._repositoryImplBase.Close()) + refmgmt.AllocLog.Trace("closed repository base", "kind", b.kind) + return list.Result() } -func (b *RepositoryImplBase) GetContext() cpi.Context { +func (b *repositoryBase) GetContext() cpi.Context { return b.ctx } -func NewRepositoryImplBase(ctx cpi.Context, closer ...io.Closer) *RepositoryImplBase { - base, _ := resource.NewResourceImplBase[cpi.Repository, io.Closer](nil, closer...) - return &RepositoryImplBase{ - _RepositoryImplBase: base, - ctx: ctx, - } +func (b *repositoryBase) GetSpecification() cpi.RepositorySpec { + return b.impl.GetSpecification() +} + +func (b *repositoryBase) ComponentLister() cpi.ComponentLister { + return b.impl.ComponentLister() +} + +func (b *repositoryBase) ExistsComponentVersion(name string, version string) (bool, error) { + return b.impl.ExistsComponentVersion(name, version) +} + +func (b *repositoryBase) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) { + return b.impl.LookupComponentVersion(name, version) +} + +func (b *repositoryBase) LookupComponent(name string) (cpi.ComponentAccess, error) { + return b.impl.LookupComponent(name) } diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index 1170b42ca3..1bf897ba9e 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -54,6 +54,16 @@ func GetComponentAccessBase(n cpi.ComponentAccess) (ComponentAccessBase, error) return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", n)) } +func GetComponentAccessImplementation(n cpi.ComponentAccess) (ComponentAccessImpl, error) { + if v, ok := n.(*componentAccessView); ok { + if b, ok := v.base.(*componentAccessBase); ok { + return b.impl, nil + } + return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", v.base)) + } + return nil, errors.ErrNotSupported("component implementation type", fmt.Sprintf("%T", n)) +} + func componentAccessViewCreator(i ComponentAccessBase, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess { return &componentAccessView{ _componentAccessView: resource.NewView[cpi.ComponentAccess](v, d), diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index ff548fb702..d556f5f5a4 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -116,6 +116,16 @@ func GetComponentVersionAccessBase(n cpi.ComponentVersionAccess) (ComponentVersi return nil, errors.ErrNotSupported("component version base type", fmt.Sprintf("%T", n)) } +func GetComponentVersionAccessImplementation(n cpi.ComponentVersionAccess) (ComponentVersionAccessImpl, error) { + if v, ok := n.(*componentVersionAccessView); ok { + if b, ok := v.base.(*componentVersionAccessBase); ok { + return b.impl, nil + } + return nil, errors.ErrNotSupported("component version base type", fmt.Sprintf("%T", v.base)) + } + return nil, errors.ErrNotSupported("component version implementation type", fmt.Sprintf("%T", n)) +} + func artifactAccessViewCreator(i ComponentVersionAccessBase, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess { cv := &componentVersionAccessView{ _componentVersionAccessView: resource.NewView[cpi.ComponentVersionAccess](v, d), diff --git a/pkg/contexts/ocm/cpi/repocpi/view_r.go b/pkg/contexts/ocm/cpi/repocpi/view_r.go index 573060cac2..440fde63a5 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_r.go @@ -6,10 +6,10 @@ package repocpi import ( "fmt" + "io" "github.com/open-component-model/ocm/pkg/contexts/credentials" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" @@ -32,12 +32,22 @@ type RepositoryViewManager = resource.ViewManager[cpi.Repository] // here you ha type RepositoryBase interface { resource.ResourceImplementation[cpi.Repository] - internal.RepositoryImpl + + GetContext() cpi.Context + + GetSpecification() cpi.RepositorySpec + ComponentLister() cpi.ComponentLister + + ExistsComponentVersion(name string, version string) (bool, error) + LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) + LookupComponent(name string) (cpi.ComponentAccess, error) + + io.Closer } type repositoryView struct { _repositoryView - base RepositoryImpl + base RepositoryBase } var ( @@ -46,13 +56,23 @@ var ( _ utils.Unwrappable = (*repositoryView)(nil) ) -func GetRepositoryImplementation(n cpi.Repository) (RepositoryImpl, error) { +func GetRepositoryBase(n cpi.Repository) (RepositoryBase, error) { if v, ok := n.(*repositoryView); ok { return v.base, nil } return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) } +func GetRepositoryImplementation(n cpi.Repository) (RepositoryImpl, error) { + if v, ok := n.(*repositoryView); ok { + if b, ok := v.base.(*repositoryBase); ok { + return b.impl, nil + } + return nil, errors.ErrNotSupported("repository base type", fmt.Sprintf("%T", v.base)) + } + return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) +} + func repositoryViewCreator(i RepositoryBase, v resource.CloserView, d RepositoryViewManager) cpi.Repository { return &repositoryView{ _repositoryView: resource.NewView[cpi.Repository](v, d), @@ -69,8 +89,12 @@ func NewNoneRefRepositoryView(i RepositoryBase) cpi.Repository { } } -func NewRepository(impl RepositoryBase, name ...string) cpi.Repository { - return resource.NewResource[cpi.Repository](impl, repositoryViewCreator, utils.OptionalDefaulted("OCM repo", name...), true) +func NewRepository(impl RepositoryImpl, kind string, closer ...io.Closer) cpi.Repository { + base := newRepositoryImplBase(impl, kind, closer...) + if kind == "" { + kind = "OCM repository" + } + return resource.NewResource[cpi.Repository](base, repositoryViewCreator, kind, true) } func (r *repositoryView) Unwrap() interface{} { diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index 086ae25ceb..91a31d7663 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -23,13 +23,15 @@ import ( //////////////////////////////////////////////////////////////////////////////// +type _componentVersionAccess = cpi.ComponentVersionAccess + // ComponentArchive is the go representation for a component artifact. type ComponentArchive struct { + _componentVersionAccess spec *RepositorySpec container *componentArchiveContainer main cpi.Repository nonref cpi.Repository - cpi.ComponentVersionAccess } // New returns a new representation based element. @@ -60,7 +62,7 @@ func _Wrap(ctx cpi.ContextProvider, obj *accessobj.AccessObject, spec *Repositor spec: spec, container: s, } - arch.ComponentVersionAccess = cv + arch._componentVersionAccess = cv arch.main, arch.nonref = newRepository(arch) s.repo = arch.nonref return arch, nil diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 89c46f7961..996b8e3c9b 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -25,12 +25,11 @@ import ( "github.com/open-component-model/ocm/pkg/utils" ) -type _RepositoryImplBase = repocpi.RepositoryImplBase - type RepositoryImpl struct { - _RepositoryImplBase - lock sync.RWMutex - arch *ComponentArchive + lock sync.RWMutex + base repocpi.RepositoryBase + arch *ComponentArchive + nonref cpi.Repository } var _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil) @@ -46,14 +45,26 @@ func NewRepository(ctx cpi.Context, s *RepositorySpec) (cpi.Repository, error) { return a.AsRepository(), nil } -func newRepository(a *ComponentArchive) (main cpi.Repository, nonref cpi.Repository) { +func newRepository(a *ComponentArchive) (main, nonref cpi.Repository) { // close main cv abstraction on repository close -------v - base := repocpi.NewRepositoryImplBase(a.GetContext(), a.ComponentVersionAccess) impl := &RepositoryImpl{ - _RepositoryImplBase: *base, - arch: a, + arch: a, } - return repocpi.NewRepository(impl), repocpi.NewNoneRefRepositoryView(impl) + r := repocpi.NewRepository(impl, "comparch") + return r, impl.nonref +} + +func (r *RepositoryImpl) Close() error { + return r.arch.container.Close() +} + +func (r *RepositoryImpl) SetBase(base repocpi.RepositoryBase) { + r.base = base + r.nonref = repocpi.NewNoneRefRepositoryView(base) +} + +func (r *RepositoryImpl) GetContext() cpi.Context { + return r.arch.GetContext() } func (r *RepositoryImpl) ComponentLister() cpi.ComponentLister { @@ -173,7 +184,7 @@ func (c *ComponentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { } func (c *ComponentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { - return c.repo + return c.repo.base } func (c *ComponentAccessImpl) GetContext() cpi.Context { diff --git a/pkg/contexts/ocm/repositories/ctf/format.go b/pkg/contexts/ocm/repositories/ctf/format.go index 5b1bc27e76..f2bc385214 100644 --- a/pkg/contexts/ocm/repositories/ctf/format.go +++ b/pkg/contexts/ocm/repositories/ctf/format.go @@ -47,7 +47,7 @@ func Open(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode v if err != nil { return nil, err } - return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r) + return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r), nil } func Create(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (cpi.Repository, error) { @@ -55,7 +55,7 @@ func Create(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode if err != nil { return nil, err } - return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r) + return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r), nil } //////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index 950065240b..eb0d65871b 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -60,7 +60,7 @@ func (c *componentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { } func (c *componentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { - return c.repo + return c.repo.base } func (c *componentAccessImpl) GetContext() cpi.Context { @@ -133,7 +133,7 @@ func (c *componentAccessImpl) HasVersion(vers string) (bool, error) { } func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { - v, err := c.repo.View() + v, err := c.repo.base.View() if err != nil { return nil, err } diff --git a/pkg/contexts/ocm/repositories/genericocireg/repository.go b/pkg/contexts/ocm/repositories/genericocireg/repository.go index 91d4d0e76a..338d369402 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repository.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repository.go @@ -40,10 +40,9 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository { return nil } -type _RepositoryImplBase = repocpi.RepositoryImplBase - type RepositoryImpl struct { - _RepositoryImplBase + base repocpi.RepositoryBase + ctx cpi.Context meta ComponentRepositoryMeta nonref cpi.Repository ocirepo oci.Repository @@ -54,15 +53,26 @@ var ( _ credentials.ConsumerIdentityProvider = (*RepositoryImpl)(nil) ) -func NewRepository(ctx cpi.Context, meta *ComponentRepositoryMeta, ocirepo oci.Repository) (cpi.Repository, error) { +func NewRepository(ctx cpi.Context, meta *ComponentRepositoryMeta, ocirepo oci.Repository) cpi.Repository { impl := &RepositoryImpl{ - _RepositoryImplBase: *repocpi.NewRepositoryImplBase(ctx.OCMContext()), - meta: *DefaultComponentRepositoryMeta(meta), - ocirepo: ocirepo, + ctx: ctx, + meta: *DefaultComponentRepositoryMeta(meta), + ocirepo: ocirepo, } - impl.nonref = repocpi.NewNoneRefRepositoryView(impl) - r := repocpi.NewRepository(impl, "OCM repo[OCI]") - return r, nil + return repocpi.NewRepository(impl, "OCM repo[OCI]") +} + +func (r *RepositoryImpl) Close() error { + return r.ocirepo.Close() +} + +func (r *RepositoryImpl) SetBase(base repocpi.RepositoryBase) { + r.base = base + r.nonref = repocpi.NewNoneRefRepositoryView(base) +} + +func (r *RepositoryImpl) GetContext() cpi.Context { + return r.ctx } func (r *RepositoryImpl) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { @@ -83,10 +93,6 @@ func (r *RepositoryImpl) GetIdentityMatcher() string { return "" } -func (r *RepositoryImpl) Close() error { - return r.ocirepo.Close() -} - func (r *RepositoryImpl) OCIRepository() ocicpi.Repository { return r.ocirepo } diff --git a/pkg/contexts/ocm/repositories/genericocireg/type.go b/pkg/contexts/ocm/repositories/genericocireg/type.go index b90282880d..016cee5c51 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/type.go +++ b/pkg/contexts/ocm/repositories/genericocireg/type.go @@ -162,7 +162,7 @@ func (s *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentia if err != nil { return nil, err } - return NewRepository(ctx, &s.ComponentRepositoryMeta, r) + return NewRepository(ctx, &s.ComponentRepositoryMeta, r), nil } func DefaultComponentRepositoryMeta(meta *ComponentRepositoryMeta) *ComponentRepositoryMeta { diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index 0ca2c308d9..a0832eb8b0 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -39,7 +39,7 @@ func (c *componentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { } func (c *componentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { - return c.repo + return c.repo.base } func (c *componentAccessImpl) GetContext() cpi.Context { diff --git a/pkg/contexts/ocm/repositories/virtual/repository.go b/pkg/contexts/ocm/repositories/virtual/repository.go index d244dfad50..7394767160 100644 --- a/pkg/contexts/ocm/repositories/virtual/repository.go +++ b/pkg/contexts/ocm/repositories/virtual/repository.go @@ -10,10 +10,9 @@ import ( "github.com/open-component-model/ocm/pkg/refmgmt" ) -type _RepositoryImplBase = repocpi.RepositoryImplBase - type RepositoryImpl struct { - _RepositoryImplBase + base repocpi.RepositoryBase + ctx cpi.Context access Access nonref cpi.Repository } @@ -22,26 +21,23 @@ var _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil) func NewRepository(ctx cpi.Context, acc Access) cpi.Repository { impl := &RepositoryImpl{ - _RepositoryImplBase: *repocpi.NewRepositoryImplBase(ctx.OCMContext()), - access: acc, + ctx: ctx, + access: acc, } - impl.nonref = repocpi.NewNoneRefRepositoryView(impl) - r := repocpi.NewRepository(impl, "OCM repo[Simple]") - return r + return repocpi.NewRepository(impl, "OCM repo[Simple]") } -/* -func (r *RepositoryImpl) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { - return nil +func (r *RepositoryImpl) Close() error { + return r.access.Close() } -func (r *RepositoryImpl) GetIdentityMatcher() string { - return "" +func (r *RepositoryImpl) SetBase(base repocpi.RepositoryBase) { + r.base = base + r.nonref = repocpi.NewNoneRefRepositoryView(base) } -*/ -func (r *RepositoryImpl) Close() error { - return r.access.Close() +func (r *RepositoryImpl) GetContext() cpi.Context { + return r.ctx } func (r *RepositoryImpl) GetSpecification() cpi.RepositorySpec { diff --git a/pkg/refmgmt/resource/resource.go b/pkg/refmgmt/resource/resource.go index 792ad2ea18..80626b44b7 100644 --- a/pkg/refmgmt/resource/resource.go +++ b/pkg/refmgmt/resource/resource.go @@ -244,6 +244,12 @@ func NewResourceImplBase[T any, M io.Closer](m ViewManager[M], closer ...io.Clos }, nil } +func NewSimpleResourceImplBase[T any](closer ...io.Closer) *ResourceImplBase[T] { + return &ResourceImplBase[T]{ + closer: closer, + } +} + func (b *ResourceImplBase[T]) SetViewManager(m ViewManager[T]) { b.refs = m } From 21b32641b04ce904168e67246835b3bb13a63e65 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Thu, 9 Nov 2023 05:05:24 -0800 Subject: [PATCH 07/18] work on impl api cleanup --- pkg/contexts/ocm/cpi/repocpi/base_c.go | 5 ----- pkg/contexts/ocm/cpi/repocpi/view_c.go | 13 ++++++------- .../ocm/repositories/comparch/repository.go | 11 ----------- .../ocm/repositories/genericocireg/component.go | 17 ----------------- .../ocm/repositories/virtual/component.go | 16 ---------------- 5 files changed, 6 insertions(+), 56 deletions(-) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go index 8cc3bbb757..732ad4fdbc 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -28,7 +28,6 @@ type ComponentAccessImpl interface { LookupVersion(version string) (cpi.ComponentVersionAccess, error) HasVersion(vers string) (bool, error) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) - AddVersion(cv cpi.ComponentVersionAccess) error io.Closer } @@ -85,10 +84,6 @@ func (c *componentAccessBase) IsOwned(cv cpi.ComponentVersionAccess) bool { return c == cvcompmgr } -func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess) error { - return c.impl.AddVersion(cv) -} - func (b *componentAccessBase) ListVersions() ([]string, error) { return b.impl.ListVersions() } diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index 1bf897ba9e..2af1a36bb9 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -33,8 +33,6 @@ type ComponentAccessBase interface { GetName() string IsOwned(access cpi.ComponentVersionAccess) bool - - AddVersion(cv cpi.ComponentVersionAccess) error } type componentAccessView struct { @@ -126,7 +124,7 @@ func (c *componentAccessView) addVersion(acc cpi.ComponentVersionAccess, overrid ctx := acc.GetContext() - impl, err := GetComponentVersionAccessBase(acc) + cvbase, err := GetComponentVersionAccessBase(acc) if err != nil { return err } @@ -151,7 +149,7 @@ func (c *componentAccessView) addVersion(acc cpi.ComponentVersionAccess, overrid finalize.With(func() error { return eff.Close() }) - impl, err = GetComponentVersionAccessBase(eff) + cvbase, err = GetComponentVersionAccessBase(eff) if err != nil { return err } @@ -167,15 +165,16 @@ func (c *componentAccessView) addVersion(acc cpi.ComponentVersionAccess, overrid eff = acc } - err = setupLocalBlobs(ctx, "resource", acc, nil, impl, d.Resources, sel, forcestore, opts) + err = setupLocalBlobs(ctx, "resource", acc, nil, cvbase, d.Resources, sel, forcestore, opts) if err == nil { - err = setupLocalBlobs(ctx, "source", acc, nil, impl, d.Sources, sel, forcestore, opts) + err = setupLocalBlobs(ctx, "source", acc, nil, cvbase, d.Sources, sel, forcestore, opts) } if err != nil { return err } - return c.base.AddVersion(eff) + cvbase.EnablePersistence() + return cvbase.Update(!cvbase.UseDirectAccess()) } func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc cpi.ComponentVersionAccess, err error) { diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 996b8e3c9b..7aa90c5710 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -222,17 +222,6 @@ func (c *ComponentAccessImpl) container(access cpi.ComponentVersionAccess) *comp return mine.comp.repo.arch.container } -func (c *ComponentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error { - if access.GetName() != c.GetName() { - return errors.ErrInvalid("component name", access.GetName()) - } - mine := c.container(access) - if mine == nil { - return errors.Newf("component version not owned by component archive") - } - return nil -} - func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { if version != c.repo.arch.GetVersion() { return nil, errors.ErrNotSupported(cpi.KIND_COMPONENTVERSION, version, fmt.Sprintf("component archive %s:%s", c.GetName(), c.repo.arch.GetVersion())) diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index eb0d65871b..9a967b80d4 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -5,7 +5,6 @@ package genericocireg import ( - "fmt" "strings" "github.com/Masterminds/semver/v3" @@ -157,22 +156,6 @@ func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess return mine } -func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error { - if access.GetName() != c.GetName() { - return errors.ErrInvalid("component name", access.GetName()) - } - mine := c.versionContainer(access) - if mine == nil { - return fmt.Errorf("cannot add component version: component version access %s not created for target", access.GetName()+":"+access.GetVersion()) - } - ok := mine.impl.EnablePersistence() - if !ok { - return fmt.Errorf("version has been discarded") - } - // delayed update in close is not done for composition mode - return mine.impl.Update(!mine.impl.UseDirectAccess()) -} - func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { v, err := c.base.View(false) if err != nil { diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index a0832eb8b0..41fc20c6a0 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -5,8 +5,6 @@ package virtual import ( - "fmt" - "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/errors" @@ -87,20 +85,6 @@ func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess return mine } -func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error { - if access.GetName() != c.GetName() { - return errors.ErrInvalid("component name", access.GetName()) - } - mine := c.versionContainer(access) - if mine == nil { - return fmt.Errorf("cannot add component version: component version access %s not created for target", access.GetName()+":"+access.GetVersion()) - } - mine.base.EnablePersistence() - - // delayed update in close is not done for composition mode - return mine.base.Update(!mine.base.UseDirectAccess()) -} - func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { v, err := c.base.View(false) if err != nil { From e065aa0723f90f5a35f42c8e147b9fa028fbb257 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Thu, 9 Nov 2023 07:02:39 -0800 Subject: [PATCH 08/18] cleanup cv impl API --- pkg/contexts/ocm/cpi/repocpi/base_cv.go | 36 ++++++++++--- .../ocm/cpi/repocpi/helperinterfaces.go | 6 +-- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 2 + .../comparch/accessmethod_localfs.go | 2 +- .../repositories/comparch/componentarchive.go | 51 +++++++++++-------- .../ocm/repositories/comparch/format.go | 2 +- .../ocm/repositories/comparch/repository.go | 23 ++++----- .../repositories/genericocireg/component.go | 8 --- .../genericocireg/componentversion.go | 10 +++- .../ocm/repositories/virtual/component.go | 8 --- .../repositories/virtual/componentversion.go | 10 +++- 11 files changed, 92 insertions(+), 66 deletions(-) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index 1fcd56ef34..81b86500ec 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -6,6 +6,7 @@ package repocpi import ( "io" + "sync" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" @@ -30,12 +31,11 @@ type ComponentVersionAccessImpl interface { IsReadOnly() bool GetDescriptor() *compdesc.ComponentDescriptor + SetDescriptor(*compdesc.ComponentDescriptor) error AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string - Update() error - BlobContainer io.Closer } @@ -55,12 +55,15 @@ type _componentVersionAccessImplBase = resource.ResourceImplBase[cpi.ComponentVe // Besides some functionality covered by view objects these base objects // implement provider-agnostic parts of the ComponentVersionAccess API. type componentVersionAccessBase struct { + lock sync.Mutex + *_componentVersionAccessImplBase ctx cpi.Context name string version string - blobcache BlobCache + descriptor *compdesc.ComponentDescriptor + blobcache BlobCache lazy bool directAccess bool @@ -131,6 +134,10 @@ func (b *componentVersionAccessBase) GetVersion() string { return b.version } +func (b *componentVersionAccessBase) GetImplementation() ComponentVersionAccessImpl { + return b.impl +} + func (b *componentVersionAccessBase) GetBlobCache() BlobCache { return b.blobcache } @@ -176,7 +183,13 @@ func (b *componentVersionAccessBase) GetInexpensiveContentVersionIdentity(acc cp } func (b *componentVersionAccessBase) GetDescriptor() *compdesc.ComponentDescriptor { - return b.impl.GetDescriptor() + b.lock.Lock() + defer b.lock.Unlock() + + if b.descriptor == nil { + b.descriptor = b.impl.GetDescriptor() + } + return b.descriptor } func (b *componentVersionAccessBase) GetStorageContext() cpi.StorageContext { @@ -184,10 +197,16 @@ func (b *componentVersionAccessBase) GetStorageContext() cpi.StorageContext { } func (b *componentVersionAccessBase) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { - return b.impl.AddBlobFor(blob, refName, global) + return b.impl.AddBlob(blob, refName, global) } func (b *componentVersionAccessBase) ShouldUpdate(final bool) bool { + b.lock.Lock() + defer b.lock.Unlock() + return b.shouldUpdate(final) +} + +func (b *componentVersionAccessBase) shouldUpdate(final bool) bool { if b.discardChanges { return false } @@ -198,8 +217,11 @@ func (b *componentVersionAccessBase) ShouldUpdate(final bool) bool { } func (b *componentVersionAccessBase) Update(final bool) error { - if b.ShouldUpdate(final) { - return b.impl.Update() + b.lock.Lock() + defer b.lock.Unlock() + + if b.shouldUpdate(final) { + return b.impl.SetDescriptor(b.descriptor.Copy()) } return nil } diff --git a/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go index 2d3a71812d..aec5b364f0 100644 --- a/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go +++ b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go @@ -18,7 +18,7 @@ var ( // BlobContainer is the interface for an element capable to store blobs. type BlobContainer interface { - GetBlobData(name string) (cpi.DataAccess, error) + GetBlob(name string) (cpi.DataAccess, error) // GetStorageContext creates a storage context for blobs // that is used to feed blob handlers for specific blob storage methods. @@ -26,11 +26,11 @@ type BlobContainer interface { // be used to store the blob GetStorageContext() cpi.StorageContext - // AddBlobFor stores a local blob together with the component and + // AddBlob stores a local blob together with the component and // potentially provides a global reference according to the OCI distribution spec // if the blob described an oci artifact. // The resulting access information (global and local) is provided as // an access method specification usable in a component descriptor. // This is the direct technical storage, without caring about any handler. - AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) + AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) } diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index d556f5f5a4..7205df2ce2 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -55,6 +55,8 @@ type ComponentVersionAccessBase interface { GetContext() cpi.Context Repository() cpi.Repository + GetImplementation() ComponentVersionAccessImpl + EnablePersistence() bool DiscardChanges() IsPersistent() bool diff --git a/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go b/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go index 96ec4733b8..db622c078d 100644 --- a/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go +++ b/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go @@ -86,7 +86,7 @@ func (m *localFilesystemBlobAccessMethod) Reader() (io.ReadCloser, error) { func (m *localFilesystemBlobAccessMethod) getBlob() (blobaccess.BlobAccess, error) { if m.blobAccess == nil { - data, err := m.base.GetBlobData(m.spec.LocalReference) + data, err := m.base.GetBlob(m.spec.LocalReference) if err != nil { return nil, err } diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index 91a31d7663..e2bdad11e6 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -49,9 +49,9 @@ func _Wrap(ctx cpi.ContextProvider, obj *accessobj.AccessObject, spec *Repositor return nil, err } s := &componentArchiveContainer{ - ctx: ctx.OCMContext(), - base: accessobj.NewFileSystemBlobAccess(obj), - spec: spec, + ctx: ctx.OCMContext(), + fsacc: accessobj.NewFileSystemBlobAccess(obj), + spec: spec, } cv, err := repocpi.NewComponentVersionAccess(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true) if err != nil { @@ -102,17 +102,17 @@ func (c *ComponentArchive) SetVersion(v string) { //////////////////////////////////////////////////////////////////////////////// type componentArchiveContainer struct { - ctx cpi.Context - impl repocpi.ComponentVersionAccessBase - base *accessobj.FileSystemBlobAccess - spec *RepositorySpec - repo cpi.Repository + ctx cpi.Context + base repocpi.ComponentVersionAccessBase + fsacc *accessobj.FileSystemBlobAccess + spec *RepositorySpec + repo cpi.Repository } var _ repocpi.ComponentVersionAccessImpl = (*componentArchiveContainer)(nil) -func (c *componentArchiveContainer) SetBase(impl repocpi.ComponentVersionAccessBase) { - c.impl = impl +func (c *componentArchiveContainer) SetBase(base repocpi.ComponentVersionAccessBase) { + c.base = base } func (c *componentArchiveContainer) GetParentBase() repocpi.ComponentAccessBase { @@ -121,7 +121,7 @@ func (c *componentArchiveContainer) GetParentBase() repocpi.ComponentAccessBase func (c *componentArchiveContainer) Close() error { var list errors.ErrorList - return list.Add(c.Update(), c.base.Close()).Result() + return list.Add(c.Update(), c.fsacc.Close()).Result() } func (c *componentArchiveContainer) GetContext() cpi.Context { @@ -133,26 +133,35 @@ func (c *componentArchiveContainer) Repository() cpi.Repository { } func (c *componentArchiveContainer) IsReadOnly() bool { - return c.base.IsReadOnly() + return c.fsacc.IsReadOnly() } func (c *componentArchiveContainer) Update() error { - return c.base.Update() + return c.fsacc.Update() +} + +func (c *componentArchiveContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error { + if c.fsacc.IsReadOnly() { + return accessobj.ErrReadOnly + } + cur := c.fsacc.GetState().GetState().(*compdesc.ComponentDescriptor) + *cur = *cd.Copy() + return c.fsacc.Update() } func (c *componentArchiveContainer) GetDescriptor() *compdesc.ComponentDescriptor { - if c.base.IsReadOnly() { - return c.base.GetState().GetOriginalState().(*compdesc.ComponentDescriptor) + if c.fsacc.IsReadOnly() { + return c.fsacc.GetState().GetOriginalState().(*compdesc.ComponentDescriptor) } - return c.base.GetState().GetState().(*compdesc.ComponentDescriptor) + return c.fsacc.GetState().GetState().(*compdesc.ComponentDescriptor) } -func (c *componentArchiveContainer) GetBlobData(name string) (cpi.DataAccess, error) { - return c.base.GetBlobDataByName(name) +func (c *componentArchiveContainer) GetBlob(name string) (cpi.DataAccess, error) { + return c.fsacc.GetBlobDataByName(name) } func (c *componentArchiveContainer) GetStorageContext() cpi.StorageContext { - return ocmhdlr.New(c.Repository(), c.impl.GetName(), &BlobSink{c.base}, Type) + return ocmhdlr.New(c.Repository(), c.base.GetName(), &BlobSink{c.fsacc}, Type) } type BlobSink struct { @@ -167,11 +176,11 @@ func (s *BlobSink) AddBlob(blob blobaccess.BlobAccess) (string, error) { return blob.Digest().String(), nil } -func (c *componentArchiveContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { +func (c *componentArchiveContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } - err := c.base.AddBlob(blob) + err := c.fsacc.AddBlob(blob) if err != nil { return nil, err } diff --git a/pkg/contexts/ocm/repositories/comparch/format.go b/pkg/contexts/ocm/repositories/comparch/format.go index 373d161f31..f4e7bb653b 100644 --- a/pkg/contexts/ocm/repositories/comparch/format.go +++ b/pkg/contexts/ocm/repositories/comparch/format.go @@ -135,5 +135,5 @@ func (h *formatHandler) Create(ctx cpi.ContextProvider, path string, opts access // WriteToFilesystem writes the current object to a filesystem. func (h *formatHandler) Write(obj *Object, path string, opts accessio.Options, mode vfs.FileMode) error { - return h.FormatHandler.Write(obj.container.base.Access(), path, opts, mode) + return h.FormatHandler.Write(obj.container.fsacc.Access(), path, opts, mode) } diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 7aa90c5710..504d8bb04d 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -214,14 +214,6 @@ func (c *ComponentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio return newComponentVersionAccess(c, version, false) } -func (c *ComponentAccessImpl) container(access cpi.ComponentVersionAccess) *componentArchiveContainer { - mine, _ := repocpi.GetComponentVersionImpl[*ComponentVersionContainer](access) - if mine == nil || mine.comp != c { - return nil - } - return mine.comp.repo.arch.container -} - func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { if version != c.repo.arch.GetVersion() { return nil, errors.ErrNotSupported(cpi.KIND_COMPONENTVERSION, version, fmt.Sprintf("component archive %s:%s", c.GetName(), c.repo.arch.GetVersion())) @@ -289,23 +281,28 @@ func (c *ComponentVersionContainer) Update() error { return c.comp.repo.arch.container.Update() } +func (c *ComponentVersionContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error { + *c.descriptor = *cd + return c.Update() +} + func (c *ComponentVersionContainer) GetDescriptor() *compdesc.ComponentDescriptor { return c.descriptor } -func (c *ComponentVersionContainer) GetBlobData(name string) (cpi.DataAccess, error) { - return c.comp.repo.arch.container.GetBlobData(name) +func (c *ComponentVersionContainer) GetBlob(name string) (cpi.DataAccess, error) { + return c.comp.repo.arch.container.GetBlob(name) } func (c *ComponentVersionContainer) GetStorageContext() cpi.StorageContext { - return ocmhdlr.New(c.Repository(), c.comp.GetName(), &BlobSink{c.comp.repo.arch.container.base}, Type) + return ocmhdlr.New(c.Repository(), c.comp.GetName(), &BlobSink{c.comp.repo.arch.container.fsacc}, Type) } -func (c *ComponentVersionContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { +func (c *ComponentVersionContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } - err := c.comp.repo.arch.container.base.AddBlob(blob) + err := c.comp.repo.arch.container.fsacc.AddBlob(blob) if err != nil { return nil, err } diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index 9a967b80d4..2349d8b852 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -148,14 +148,6 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio return newComponentVersionAccess(accessobj.ACC_WRITABLE, c, version, acc, true) } -func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess) *ComponentVersionContainer { - mine, _ := repocpi.GetComponentVersionImpl[*ComponentVersionContainer](access) - if mine == nil || mine.comp != c { - return nil - } - return mine -} - func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { v, err := c.base.View(false) if err != nil { diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 3fd95adaa2..166f8535f5 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -160,6 +160,12 @@ func (c *ComponentVersionContainer) GetInexpensiveContentVersionIdentity(a cpi.A return "" } +func (c *ComponentVersionContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error { + cur := c.GetDescriptor() + *cur = *cd + return c.Update() +} + func (c *ComponentVersionContainer) Update() error { logger := Logger(c.GetContext()).WithValues("cv", common.NewNameVersion(c.comp.name, c.version)) err := c.Check() @@ -259,7 +265,7 @@ func (c *ComponentVersionContainer) GetDescriptor() *compdesc.ComponentDescripto return c.state.GetState().(*compdesc.ComponentDescriptor) } -func (c *ComponentVersionContainer) GetBlobData(name string) (cpi.DataAccess, error) { +func (c *ComponentVersionContainer) GetBlob(name string) (cpi.DataAccess, error) { return c.manifest.GetBlob(digest.Digest(name)) } @@ -267,7 +273,7 @@ func (c *ComponentVersionContainer) GetStorageContext() cpi.StorageContext { return ocihdlr.New(c.comp.GetName(), c.Repository(), c.comp.repo.ocirepo.GetSpecification().GetKind(), c.comp.repo.ocirepo, c.comp.namespace, c.manifest) } -func (c *ComponentVersionContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { +func (c *ComponentVersionContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index 41fc20c6a0..4513fb91ea 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -77,14 +77,6 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio return newComponentVersionAccess(c, version, true) } -func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess) *ComponentVersionContainer { - mine, _ := repocpi.GetComponentVersionImpl[*ComponentVersionContainer](access) - if mine == nil || mine.comp != c { - return nil - } - return mine -} - func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { v, err := c.base.View(false) if err != nil { diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index 3b9d9673d4..fbc8facda5 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -135,11 +135,17 @@ func (c *ComponentVersionContainer) Update() error { return c.access.Update() } +func (c *ComponentVersionContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error { + cur := c.access.GetDescriptor() + *cur = *cd + return c.access.Update() +} + func (c *ComponentVersionContainer) GetDescriptor() *compdesc.ComponentDescriptor { return c.access.GetDescriptor() } -func (c *ComponentVersionContainer) GetBlobData(name string) (cpi.DataAccess, error) { +func (c *ComponentVersionContainer) GetBlob(name string) (cpi.DataAccess, error) { return c.access.GetBlob(name) } @@ -147,7 +153,7 @@ func (c *ComponentVersionContainer) GetStorageContext() cpi.StorageContext { return ocmhdlr.New(c.Repository(), c.comp.GetName(), c.access, Type, c.access) } -func (c *ComponentVersionContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { +func (c *ComponentVersionContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { if c.IsReadOnly() { return nil, accessio.ErrReadOnly } From f744071aed485a3468dd1a650cf83e86ff62434d Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Thu, 9 Nov 2023 09:13:36 -0800 Subject: [PATCH 09/18] decouple impl layer --- .../ocmcmds/components/sign/cmd_test.go | 4 +-- pkg/contexts/ocm/cpi/repocpi/base_c.go | 34 +++++++++++++++---- pkg/contexts/ocm/cpi/repocpi/base_r.go | 25 +++++++++++--- pkg/contexts/ocm/cpi/repocpi/view_c.go | 4 +-- .../ocm/repositories/comparch/repository.go | 33 ++++-------------- .../repositories/genericocireg/component.go | 20 +++-------- .../genericocireg/componentversion.go | 7 ++-- .../repositories/genericocireg/repository.go | 13 +------ .../ocm/repositories/virtual/component.go | 20 +++-------- .../repositories/virtual/componentversion.go | 5 ++- .../ocm/repositories/virtual/repository.go | 12 +------ 11 files changed, 73 insertions(+), 104 deletions(-) diff --git a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go index 4200633632..099b9a0451 100644 --- a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go @@ -238,7 +238,7 @@ Error: signing: github.com/mandelsoft/ref:v1: failed resolving component referen buf := bytes.NewBuffer(nil) Expect(env.CatchErrorOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION)).To(HaveOccurred()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` -Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: ocm reference "github.com/mandelsoft/test:v1" not found +Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: component "github.com/mandelsoft/test" not found in ComponentArchive `)) }) @@ -246,7 +246,7 @@ Error: signing: github.com/mandelsoft/ref:v1: failed resolving component referen buf := bytes.NewBuffer(nil) Expect(env.CatchErrorOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, ARCH)).To(HaveOccurred()) Expect(buf.String()).To(StringEqualTrimmedWithContext(` -Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: ocm reference "github.com/mandelsoft/test:v1" not found +Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: component "github.com/mandelsoft/test" not found in ComponentArchive `)) }) }) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go index 732ad4fdbc..d777c56cc8 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -7,13 +7,19 @@ package repocpi import ( "io" + "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" ) +type ComponentVersionAccessInfo struct { + Impl ComponentVersionAccessImpl + Lazy bool + Persistent bool +} + // ComponentAccessImpl is the provider implementation // interface for component versions. type ComponentAccessImpl interface { @@ -25,9 +31,9 @@ type ComponentAccessImpl interface { IsReadOnly() bool ListVersions() ([]string, error) - LookupVersion(version string) (cpi.ComponentVersionAccess, error) HasVersion(vers string) (bool, error) - NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) + LookupVersion(version string) (*ComponentVersionAccessInfo, error) + NewVersion(version string, overrides ...bool) (*ComponentVersionAccessInfo, error) io.Closer } @@ -88,16 +94,30 @@ func (b *componentAccessBase) ListVersions() ([]string, error) { return b.impl.ListVersions() } -func (b *componentAccessBase) LookupVersion(version string) (internal.ComponentVersionAccess, error) { - return b.impl.LookupVersion(version) +func (b *componentAccessBase) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { + i, err := b.impl.LookupVersion(version) + if err != nil { + return nil, err + } + if i == nil || i.Impl == nil { + return nil, errors.ErrInvalid("component implementation behaviour", "LookupVersion") + } + return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, i.Persistent, !compositionmodeattr.Get(b.GetContext())) } func (b *componentAccessBase) HasVersion(vers string) (bool, error) { return b.impl.HasVersion(vers) } -func (b *componentAccessBase) NewVersion(version string, overrides ...bool) (internal.ComponentVersionAccess, error) { - return b.impl.NewVersion(version, overrides...) +func (b *componentAccessBase) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { + i, err := b.impl.NewVersion(version, overrides...) + if err != nil { + return nil, err + } + if i == nil || i.Impl == nil { + return nil, errors.ErrInvalid("component implementation behaviour", "NewVersion") + } + return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, false, !compositionmodeattr.Get(b.GetContext())) } func (b *componentAccessBase) IsReadOnly() bool { diff --git a/pkg/contexts/ocm/cpi/repocpi/base_r.go b/pkg/contexts/ocm/cpi/repocpi/base_r.go index a22d76cd89..e2eb5687d4 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_r.go @@ -13,6 +13,12 @@ import ( "github.com/open-component-model/ocm/pkg/refmgmt/resource" ) +type ComponentAccessInfo struct { + Impl ComponentAccessImpl + Kind string + Main bool +} + type RepositoryImpl interface { SetBase(base RepositoryBase) @@ -22,8 +28,7 @@ type RepositoryImpl interface { ComponentLister() cpi.ComponentLister ExistsComponentVersion(name string, version string) (bool, error) - LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) - LookupComponent(name string) (cpi.ComponentAccess, error) + LookupComponent(name string) (*ComponentAccessInfo, error) io.Closer } @@ -73,10 +78,20 @@ func (b *repositoryBase) ExistsComponentVersion(name string, version string) (bo return b.impl.ExistsComponentVersion(name, version) } -func (b *repositoryBase) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) { - return b.impl.LookupComponentVersion(name, version) +func (b *repositoryBase) LookupComponentVersion(name string, version string) (cv cpi.ComponentVersionAccess, rerr error) { + c, err := b.LookupComponent(name) + if err != nil { + return nil, err + } + defer refmgmt.PropagateCloseTemporary(&rerr, c) // temporary component object not exposed. + refmgmt.AllocLog.Trace("lookup version for temporary component ref", "component", name, "version", version) + return c.LookupVersion(version) } func (b *repositoryBase) LookupComponent(name string) (cpi.ComponentAccess, error) { - return b.impl.LookupComponent(name) + i, err := b.impl.LookupComponent(name) + if err != nil { + return nil, err + } + return NewComponentAccess(i.Impl, i.Kind, i.Main) } diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index 2af1a36bb9..45e39341c7 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -69,7 +69,7 @@ func componentAccessViewCreator(i ComponentAccessBase, v resource.CloserView, d } } -func NewComponentAccess(impl ComponentAccessImpl, kind string, closer ...io.Closer) (cpi.ComponentAccess, error) { +func NewComponentAccess(impl ComponentAccessImpl, kind string, main bool, closer ...io.Closer) (cpi.ComponentAccess, error) { base, err := newComponentAccessImplBase(impl, closer...) if err != nil { return nil, errors.Join(err, impl.Close()) @@ -77,7 +77,7 @@ func NewComponentAccess(impl ComponentAccessImpl, kind string, closer ...io.Clos if kind == "" { kind = "component" } - cv := resource.NewResource[cpi.ComponentAccess](base, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), true) + cv := resource.NewResource[cpi.ComponentAccess](base, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), main) return cv, nil } diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 504d8bb04d..7240aefbde 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -15,7 +15,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localblob" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localfsblob" - "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -129,25 +128,7 @@ func (r *RepositoryImpl) ExistsComponentVersion(name string, ref string) (bool, return r.arch.GetName() == name && r.arch.GetVersion() == ref, nil } -func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) { - r.lock.RLock() - defer r.lock.RUnlock() - ok, err := r.ExistsComponentVersion(name, version) - if !ok { - if err == nil { - err = errors.ErrNotFound(cpi.KIND_COMPONENTVERSION, common.NewNameVersion(name, version).String(), Type) - } - return nil, err - } - c, err := newComponentAccess(r) - if err != nil { - return nil, err - } - defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed. - return c.LookupVersion(version) -} - -func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, error) { +func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessInfo, error) { r.lock.RLock() defer r.lock.RUnlock() if r.arch == nil { @@ -168,11 +149,11 @@ type ComponentAccessImpl struct { var _ repocpi.ComponentAccessImpl = (*ComponentAccessImpl)(nil) -func newComponentAccess(r *RepositoryImpl) (cpi.ComponentAccess, error) { +func newComponentAccess(r *RepositoryImpl) (*repocpi.ComponentAccessInfo, error) { impl := &ComponentAccessImpl{ repo: r, } - return repocpi.NewComponentAccess(impl, "component archive") + return &repocpi.ComponentAccessInfo{impl, "component archive", true}, nil } func (c *ComponentAccessImpl) Close() error { @@ -207,14 +188,14 @@ func (c *ComponentAccessImpl) HasVersion(vers string) (bool, error) { return vers == c.repo.arch.GetVersion(), nil } -func (c *ComponentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { +func (c *ComponentAccessImpl) LookupVersion(version string) (*repocpi.ComponentVersionAccessInfo, error) { if version != c.repo.arch.GetVersion() { return nil, errors.ErrNotFound(cpi.KIND_COMPONENTVERSION, fmt.Sprintf("%s:%s", c.GetName(), c.repo.arch.GetVersion())) } return newComponentVersionAccess(c, version, false) } -func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { +func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (*repocpi.ComponentVersionAccessInfo, error) { if version != c.repo.arch.GetVersion() { return nil, errors.ErrNotSupported(cpi.KIND_COMPONENTVERSION, version, fmt.Sprintf("component archive %s:%s", c.GetName(), c.repo.arch.GetVersion())) } @@ -236,12 +217,12 @@ type ComponentVersionContainer struct { var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil) -func newComponentVersionAccess(comp *ComponentAccessImpl, version string, persistent bool) (cpi.ComponentVersionAccess, error) { +func newComponentVersionAccess(comp *ComponentAccessImpl, version string, persistent bool) (*repocpi.ComponentVersionAccessInfo, error) { c, err := newComponentVersionContainer(comp) if err != nil { return nil, err } - return repocpi.NewComponentVersionAccess(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) + return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil } func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionContainer, error) { diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index 2349d8b852..22a57457da 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -30,7 +30,7 @@ type componentAccessImpl struct { namespace oci.NamespaceAccess } -func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.ComponentAccess, error) { +func newComponentAccess(repo *RepositoryImpl, name string, main bool) (*repocpi.ComponentAccessInfo, error) { mapped, err := repo.MapComponentNameToNamespace(name) if err != nil { return nil, err @@ -44,7 +44,7 @@ func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.Compo name: name, namespace: namespace, } - return repocpi.NewComponentAccess(impl, "OCM component[OCI]") + return &repocpi.ComponentAccessInfo{impl, "OCM component[OCI]", main}, nil } func (c *componentAccessImpl) Close() error { @@ -131,13 +131,7 @@ func (c *componentAccessImpl) HasVersion(vers string) (bool, error) { return false, err } -func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { - v, err := c.repo.base.View() - if err != nil { - return nil, err - } - defer v.Close() - +func (c *componentAccessImpl) LookupVersion(version string) (*repocpi.ComponentVersionAccessInfo, error) { acc, err := c.namespace.GetArtifact(toTag(version)) if err != nil { if errors.IsErrNotFound(err) { @@ -148,13 +142,7 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio return newComponentVersionAccess(accessobj.ACC_WRITABLE, c, version, acc, true) } -func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { - v, err := c.base.View(false) - if err != nil { - return nil, err - } - defer v.Close() - +func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (*repocpi.ComponentVersionAccessInfo, error) { override := utils.Optional(overrides...) acc, err := c.namespace.GetArtifact(toTag(version)) if err == nil { diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 166f8535f5..725a1173e7 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -22,7 +22,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartifact" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/relativeociref" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compatattr" - "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" ocihdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/oci" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -33,13 +32,13 @@ import ( "github.com/open-component-model/ocm/pkg/refmgmt" ) -// newComponentVersionAccess creates an component access for the artifact access, if this fails the artifact acess is closed. -func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess, persistent bool) (cpi.ComponentVersionAccess, error) { +// newComponentVersionAccess creates a component access for the artifact access, if this fails the artifact acess is closed. +func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess, persistent bool) (*repocpi.ComponentVersionAccessInfo, error) { c, err := newComponentVersionContainer(mode, comp, version, access) if err != nil { return nil, err } - return repocpi.NewComponentVersionAccess(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) + return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil } // ////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/repositories/genericocireg/repository.go b/pkg/contexts/ocm/repositories/genericocireg/repository.go index 338d369402..641ccb857b 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repository.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repository.go @@ -18,7 +18,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg/componentmapping" "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/utils" ) @@ -176,20 +175,10 @@ func (r *RepositoryImpl) ExistsComponentVersion(name string, version string) (bo return false, nil } -func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, error) { +func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessInfo, error) { return newComponentAccess(r, name, true) } -func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) { - c, err := newComponentAccess(r, name, false) - if err != nil { - return nil, err - } - defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed. - refmgmt.AllocLog.Trace("OCM Repo[OCI]: lookup version for temporary component ref", "component", name, "version", version) - return c.LookupVersion(version) -} - func (r *RepositoryImpl) MapComponentNameToNamespace(name string) (string, error) { switch r.meta.ComponentNameMapping { case OCIRegistryURLPathMapping, "": diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index 4513fb91ea..5c434667a9 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -20,12 +20,12 @@ type componentAccessImpl struct { var _ repocpi.ComponentAccessImpl = (*componentAccessImpl)(nil) -func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.ComponentAccess, error) { +func newComponentAccess(repo *RepositoryImpl, name string, main bool) (*repocpi.ComponentAccessInfo, error) { impl := &componentAccessImpl{ repo: repo, name: name, } - return repocpi.NewComponentAccess(impl, "OCM component[Simple]") + return &repocpi.ComponentAccessInfo{impl, "OCM component[Simple]", main}, nil } func (c *componentAccessImpl) Close() error { @@ -60,7 +60,7 @@ func (c *componentAccessImpl) IsReadOnly() bool { return c.repo.access.IsReadOnly() } -func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { +func (c *componentAccessImpl) LookupVersion(version string) (*repocpi.ComponentVersionAccessInfo, error) { ok, err := c.HasVersion(version) if err != nil { return nil, err @@ -68,22 +68,10 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio if !ok { return nil, cpi.ErrComponentVersionNotFoundWrap(err, c.name, version) } - v, err := c.base.View() - if err != nil { - return nil, err - } - defer v.Close() - return newComponentVersionAccess(c, version, true) } -func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { - v, err := c.base.View(false) - if err != nil { - return nil, err - } - defer v.Close() - +func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (*repocpi.ComponentVersionAccessInfo, error) { override := utils.Optional(overrides...) ok, err := c.HasVersion(version) if err == nil && ok { diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index fbc8facda5..f4ce58f882 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -8,7 +8,6 @@ import ( "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localblob" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localfsblob" - "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -19,7 +18,7 @@ import ( ) // newComponentVersionAccess creates a component access for the artifact access, if this fails the artifact acess is closed. -func newComponentVersionAccess(comp *componentAccessImpl, version string, persistent bool) (cpi.ComponentVersionAccess, error) { +func newComponentVersionAccess(comp *componentAccessImpl, version string, persistent bool) (*repocpi.ComponentVersionAccessInfo, error) { access, err := comp.repo.access.GetComponentVersion(comp.GetName(), version) if err != nil { return nil, err @@ -28,7 +27,7 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis if err != nil { return nil, err } - return repocpi.NewComponentVersionAccess(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext())) + return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil } // ////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/repositories/virtual/repository.go b/pkg/contexts/ocm/repositories/virtual/repository.go index 7394767160..b9a79174ac 100644 --- a/pkg/contexts/ocm/repositories/virtual/repository.go +++ b/pkg/contexts/ocm/repositories/virtual/repository.go @@ -7,7 +7,6 @@ package virtual import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi" - "github.com/open-component-model/ocm/pkg/refmgmt" ) type RepositoryImpl struct { @@ -55,15 +54,6 @@ func (r *RepositoryImpl) ExistsComponentVersion(name string, version string) (bo return r.access.ExistsComponentVersion(name, version) } -func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, error) { +func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessInfo, error) { return newComponentAccess(r, name, true) } - -func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) { - c, err := newComponentAccess(r, name, false) - if err != nil { - return nil, err - } - defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed. - return c.LookupVersion(version) -} From abebc2683088cc6ebafb8d51bf4b333778fe7c19 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Sat, 11 Nov 2023 09:31:15 -0800 Subject: [PATCH 10/18] move down cv view to base (store blob) --- pkg/contexts/ocm/cpi/modopts.go | 14 +++ pkg/contexts/ocm/cpi/repocpi/base_cv.go | 98 ++++++++++++++++++++- pkg/contexts/ocm/cpi/repocpi/view_c.go | 3 +- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 111 ++++++------------------ pkg/contexts/ocm/internal/modopts.go | 84 +++++++++++++++--- pkg/contexts/ocm/modopts.go | 14 +++ pkg/optionutils/pointer.go | 6 ++ 7 files changed, 227 insertions(+), 103 deletions(-) diff --git a/pkg/contexts/ocm/cpi/modopts.go b/pkg/contexts/ocm/cpi/modopts.go index cc4141f9c4..5b5773f4df 100644 --- a/pkg/contexts/ocm/cpi/modopts.go +++ b/pkg/contexts/ocm/cpi/modopts.go @@ -20,10 +20,24 @@ type ( BlobUploadOption = internal.BlobUploadOption BlobUploadOptions = internal.BlobUploadOptions + + AddVersionOption = internal.AddVersionOption + AddVersionOptions = internal.AddVersionOptions ) //////////////////////////////////////////////////////////////////////////////// +func NewAddVersionOptions(list ...AddVersionOption) *AddVersionOptions { + return internal.NewAddVersionOptions(list...) +} + +// Overwrite enabled the overwrite mode for adding a component version. +func Overwrite(flag ...bool) AddVersionOption { + return internal.Overwrite(flag...) +} + +//////////////////////////////////////////////////////////////////////////////// + func NewBlobModificationOptions(list ...BlobModificationOption) *BlobModificationOptions { return internal.NewBlobModificationOptions(list...) } diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index 81b86500ec..720dff645d 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -5,15 +5,22 @@ package repocpi import ( + "encoding/json" "io" "sync" "github.com/open-component-model/ocm/pkg/common" + "github.com/open-component-model/ocm/pkg/common/accessio" + "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" + "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" + "github.com/open-component-model/ocm/pkg/utils" ) // here, we define the common implementation agnostic parts @@ -196,10 +203,6 @@ func (b *componentVersionAccessBase) GetStorageContext() cpi.StorageContext { return b.impl.GetStorageContext() } -func (b *componentVersionAccessBase) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { - return b.impl.AddBlob(blob, refName, global) -} - func (b *componentVersionAccessBase) ShouldUpdate(final bool) bool { b.lock.Lock() defer b.lock.Unlock() @@ -225,3 +228,90 @@ func (b *componentVersionAccessBase) Update(final bool) error { } return nil } + +func (c *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { + if blob == nil { + return nil, errors.New("a resource has to be defined") + } + if c.IsReadOnly() { + return nil, accessio.ErrReadOnly + } + blob, err := blob.Dup() + if err != nil { + return nil, errors.Wrapf(err, "invalid blob access") + } + defer blob.Close() + err = utils.ValidateObject(blob) + if err != nil { + return nil, errors.Wrapf(err, "invalid blob access") + } + + storagectx := c.GetStorageContext() + ctx := c.GetContext() + + // handle foreign blob upload + var prov cpi.BlobHandlerProvider + if opts.BlobHandlerProvider != nil { + prov = opts.BlobHandlerProvider + } else { + if !optionutils.AsValue(opts.UseNoDefaultIfNotSet) { + prov = internal.BlobHandlerProviderForRegistry(ctx.BlobHandlers()) + } else { + //nolint: staticcheck // yes + // use no blob uploader + } + } + if prov != nil { + h := prov.LookupHandler(storagectx, artType, blob.MimeType()) + if h != nil { + acc, err := h.StoreBlob(blob, artType, refName, nil, storagectx) + if err != nil { + return nil, err + } + if acc != nil { + if !keepblobattr.Get(ctx) || acc.IsLocal(ctx) { + return acc, nil + } + global = acc + } + } + } + + var acc cpi.AccessSpec + + if final || c.UseDirectAccess() { + acc, err = c.impl.AddBlob(blob, refName, global) + if err != nil { + return nil, err + } + } else { + // use local composition access to be added to the repository with AddVersion. + acc = compose.New(refName, blob.MimeType(), global) + } + return c.cacheLocalBlob(acc, blob) +} + +func (c *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { + key, err := json.Marshal(acc) + if err != nil { + return nil, errors.Wrapf(err, "cannot marshal access spec") + } + // local blobs might not be accessible from the underlying + // repository implementation if the component version is not + // finally added (for example ghcr.io as OCI repository). + // Therefore, we keep a copy of the blob access for further usage. + + // if a local blob is uploader and the access method is replaced + // we have to handle the case that the technical upload repo + // is the same as the storage backend of the OCM repository, which + // might have been configured with local credentials, which were + // reused by the uploader. + // The access spec is independent of the actual repo, so it does + // not have access to those credentials. Therefore, we have to + // keep the original blob for further usage, also. + err = c.GetBlobCache().AddBlobFor(string(key), blob) + if err != nil { + return nil, err + } + return acc, nil +} diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index 45e39341c7..f6cd3a707c 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -15,6 +15,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/finalizer" + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt/resource" "github.com/open-component-model/ocm/pkg/utils" ) @@ -158,7 +159,7 @@ func (c *componentAccessView) addVersion(acc cpi.ComponentVersionAccess, overrid *d = *acc.GetDescriptor().Copy() } else { // transfer composition blobs into local blobs - opts.UseNoDefaultIfNotSet = true + opts.UseNoDefaultIfNotSet = optionutils.PointerTo(true) opts.BlobHandlerProvider = nil sel = compose.Is d = acc.GetDescriptor() diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index 7205df2ce2..ae78f887eb 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -17,7 +17,6 @@ import ( "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" - "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -26,6 +25,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/finalizer" + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" "github.com/open-component-model/ocm/pkg/utils" @@ -72,12 +72,12 @@ type ComponentVersionAccessBase interface { // be used to store the blob GetStorageContext() cpi.StorageContext - // AddBlobFor stores a local blob together with the component and + // AddBlob stores a local blob together with the component and // potentially provides a global reference. // The resulting access information (global and local) is provided as // an access method specification usable in a component descriptor. // This is the direct technical storage, without caring about any handler. - AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) + AddBlob(blob cpi.BlobAccess, arttype, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) IsReadOnly() bool @@ -234,6 +234,14 @@ func (c *componentVersionAccessView) accessMethod(spec cpi.AccessSpec) (meth cpi return meth, err } +func (c *componentVersionAccessView) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { + key, err := json.Marshal(acc) + if err != nil { + return nil + } + return c.base.GetBlobCache().GetBlobFor(string(key)) +} + func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec cpi.AccessSpec) string { var err error @@ -282,10 +290,13 @@ func (c *componentVersionAccessView) update(final bool) error { if err != nil { return err } + opts := &cpi.BlobUploadOptions{ + UseNoDefaultIfNotSet: optionutils.PointerTo(true), + } // TODO: exceute for separately lockable view - err = setupLocalBlobs(ctx, "resource", c, c.accessMethod, impl, d.Resources, compose.Is, true, nil) + err = setupLocalBlobs(ctx, "resource", c, c.accessMethod, impl, d.Resources, compose.Is, true, opts) if err == nil { - err = setupLocalBlobs(ctx, "source", c, c.accessMethod, impl, d.Sources, compose.Is, true, nil) + err = setupLocalBlobs(ctx, "source", c, c.accessMethod, impl, d.Sources, compose.Is, true, opts) } if err != nil { return err @@ -299,80 +310,15 @@ func (c *componentVersionAccessView) update(final bool) error { } func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, opts ...internal.BlobUploadOption) (cpi.AccessSpec, error) { - if blob == nil { - return nil, errors.New("a resource has to be defined") - } - if c.base.IsReadOnly() { - return nil, accessio.ErrReadOnly - } - blob, err := blob.Dup() - if err != nil { - return nil, errors.Wrapf(err, "invalid blob access") - } - defer blob.Close() - err = utils.ValidateObject(blob) - if err != nil { - return nil, errors.Wrapf(err, "invalid blob access") - } - - return addBlob(c.base, artType, refName, blob, global) -} - -func addBlob(impl ComponentVersionAccessBase, artType, refName string, blob cpi.BlobAccess, global cpi.AccessSpec) (cpi.AccessSpec, error) { - storagectx := impl.GetStorageContext() - ctx := impl.GetContext() - h := ctx.BlobHandlers().LookupHandler(storagectx.GetImplementationRepositoryType(), artType, blob.MimeType()) - if h != nil { - acc, err := h.StoreBlob(blob, artType, refName, nil, storagectx) - if err != nil { - return nil, err - } - if acc != nil { - if !keepblobattr.Get(ctx) || acc.IsLocal(ctx) { - return acc, nil - } - global = acc - } - } - if impl.UseDirectAccess() { - return impl.AddBlobFor(blob, refName, global) - } - // use local composition access to be added to the repository with AddVersion. - acc := compose.New(refName, blob.MimeType(), global) - return cacheLocalBlob(impl, acc, blob) -} - -func (c *componentVersionAccessView) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { - key, err := json.Marshal(acc) - if err != nil { - return nil - } - return c.base.GetBlobCache().GetBlobFor(string(key)) -} + var spec cpi.AccessSpec + eff := cpi.NewBlobUploadOptions(opts...) + err := c.Execute(func() error { + var err error + spec, err = c.base.AddBlob(blob, artType, refName, global, false, eff) + return err + }) -func cacheLocalBlob(impl ComponentVersionAccessBase, acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { - key, err := json.Marshal(acc) - if err != nil { - return nil, errors.Wrapf(err, "cannot marshal access spec") - } - // local blobs might not be accessible from the underlying - // repository implementation if the component version is not - // finally added (for example ghcr.io as OCI repository). - // Therefore, we keep a copy of the blob access for further usage. - - // if a local blob is uploader and the access method is replaced - // we have to handle the case that the technical upload repo - // is the same as the storage backend of the OCM repository, which - // might have been configured with local credentials, which were - // reused by the uploader. - // The access spec is independent of the actual repo, so it does - // not have access to those credentials. Therefore, we have to - // keep the original blob for further usage, also. - err = impl.GetBlobCache().AddBlobFor(string(key), blob) - if err != nil { - return nil, err - } - return acc, nil + return spec, err } func (c *componentVersionAccessView) AdjustResourceAccess(meta *cpi.ResourceMeta, acc compdesc.AccessSpec, opts ...internal.ModificationOption) error { @@ -937,7 +883,7 @@ func (c *componentVersionAccessView) GetReferencesBySelectors(selectors []compde //////////////////////////////////////////////////////////////////////////////// -func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error), tgtimpl ComponentVersionAccessBase, it compdesc.ArtifactAccessor, sel func(cpi.AccessSpec) bool, forcestore bool, opts *cpi.BlobUploadOptions) (ferr error) { +func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error), tgtbase ComponentVersionAccessBase, it compdesc.ArtifactAccessor, sel func(cpi.AccessSpec) bool, final bool, opts *cpi.BlobUploadOptions) (ferr error) { var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) @@ -955,12 +901,7 @@ func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAcces } nested.Close(blob) - var effspec cpi.AccessSpec - if forcestore { - effspec, err = tgtimpl.AddBlobFor(blob, cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, ctx)) - } else { - effspec, err = addBlob(tgtimpl, a.GetType(), cpi.ReferenceHint(spec, src), blob, cpi.GlobalAccess(spec, ctx)) - } + effspec, err := tgtbase.AddBlob(blob, a.GetType(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, ctx), final, opts) if err != nil { return errors.Wrapf(err, "cannot store %s %d", kind, i) } diff --git a/pkg/contexts/ocm/internal/modopts.go b/pkg/contexts/ocm/internal/modopts.go index 4c691a9360..c509cbba1c 100644 --- a/pkg/contexts/ocm/internal/modopts.go +++ b/pkg/contexts/ocm/internal/modopts.go @@ -5,6 +5,7 @@ package internal import ( + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/utils" ) @@ -18,7 +19,7 @@ type BlobOptionImpl interface { } type BlobUploadOptions struct { - UseNoDefaultIfNotSet bool `json:"noDefaultUpload,omitempty"` + UseNoDefaultIfNotSet *bool `json:"noDefaultUpload,omitempty"` BlobHandlerProvider BlobHandlerProvider `json:"-"` } @@ -43,13 +44,31 @@ func (o *BlobUploadOptions) ApplyBlobModificationOption(opts *BlobModificationOp } func (o *BlobUploadOptions) ApplyBlobUploadOption(opts *BlobUploadOptions) { + optionutils.ApplyOption(o.UseNoDefaultIfNotSet, &opts.UseNoDefaultIfNotSet) if o.BlobHandlerProvider != nil { opts.BlobHandlerProvider = o.BlobHandlerProvider - } else { - opts.UseNoDefaultIfNotSet = true + opts.UseNoDefaultIfNotSet = utils.BoolP(true) } } +//////////////////////////////////////////////////////////////////////////////// + +type nodefaulthandler bool + +func (o nodefaulthandler) ApplyBlobModificationOption(opts *BlobModificationOptions) { + o.ApplyBlobUploadOption(&opts.BlobUploadOptions) +} + +func (o nodefaulthandler) ApplyBlobUploadOption(opts *BlobUploadOptions) { + opts.UseNoDefaultIfNotSet = optionutils.PointerTo(bool(o)) +} + +func UseNoDefaultBlobHandlers(b ...bool) BlobOptionImpl { + return nodefaulthandler(utils.OptionalDefaultedBool(true, b...)) +} + +//////////////////////////////////////////////////////////////////////////////// + type handler struct { blobHandlerProvider BlobHandlerProvider } @@ -131,10 +150,10 @@ func (m *ModificationOptions) ApplyBlobModificationOption(opts *BlobModification } func (m *ModificationOptions) ApplyModificationOption(opts *ModificationOptions) { - applyBool(m.ModifyResource, &opts.ModifyResource) - applyBool(m.AcceptExistentDigests, &opts.AcceptExistentDigests) - applyBool(m.SkipDigest, &opts.SkipDigest) - applyBool(m.SkipVerify, &opts.SkipVerify) + optionutils.ApplyOption(m.ModifyResource, &opts.ModifyResource) + optionutils.ApplyOption(m.AcceptExistentDigests, &opts.AcceptExistentDigests) + optionutils.ApplyOption(m.SkipDigest, &opts.SkipDigest) + optionutils.ApplyOption(m.SkipVerify, &opts.SkipVerify) if m.HasherProvider != nil { opts.HasherProvider = m.HasherProvider } @@ -143,12 +162,6 @@ func (m *ModificationOptions) ApplyModificationOption(opts *ModificationOptions) } } -func applyBool(m *bool, t **bool) { - if m != nil { - *t = utils.BoolP(*m) - } -} - func (m *ModificationOptions) GetHasher(algo ...string) Hasher { return m.HasherProvider.GetHasher(utils.OptionalDefaulted(m.DefaultHashAlgorithm, algo...)) } @@ -299,3 +312,48 @@ func (o *BlobModificationOptions) ApplyBlobUploadOption(opts *BlobUploadOptions) func (o *BlobModificationOptions) ApplyModificationOption(opts *ModificationOptions) { o.ModificationOptions.ApplyModificationOption(opts) } + +/////////////////////////////////////////////////////////////////////////////// + +// BlobModificationOption is used for option list allowing both, +// blob upload and modification options. +type AddVersionOption interface { + ApplyAddVersionOption(*AddVersionOptions) +} + +type AddVersionOptions struct { + Overwrite *bool + BlobUploadOptions +} + +func NewAddVersionOptions(list ...AddVersionOption) *AddVersionOptions { + var m AddVersionOptions + m.ApplyAddVersionOptions(list...) + return &m +} + +func (m *AddVersionOptions) ApplyAddVersionOptions(list ...AddVersionOption) { + for _, o := range list { + if o != nil { + o.ApplyAddVersionOption(m) + } + } +} + +func (o *AddVersionOptions) ApplyAddVersionOption(opts *AddVersionOptions) { + optionutils.ApplyOption(o.Overwrite, &opts.Overwrite) + o.BlobUploadOptions.ApplyBlobUploadOption(&opts.BlobUploadOptions) +} + +//////////////////////////////////////////////////////////////////////////////// + +type overwrite bool + +func (m overwrite) ApplyAddVersionOption(opts *AddVersionOptions) { + opts.Overwrite = utils.BoolP(m) +} + +// Overwrite enabled the overwrite mode for adding a component version. +func Overwrite(flag ...bool) AddVersionOption { + return overwrite(utils.OptionalDefaultedBool(true, flag...)) +} diff --git a/pkg/contexts/ocm/modopts.go b/pkg/contexts/ocm/modopts.go index b65f0f06dd..a03eba5cc6 100644 --- a/pkg/contexts/ocm/modopts.go +++ b/pkg/contexts/ocm/modopts.go @@ -20,10 +20,24 @@ type ( BlobUploadOption = internal.BlobUploadOption BlobUploadOptions = internal.BlobUploadOptions + + AddVersionOption = internal.AddVersionOption + AddVersionOptions = internal.AddVersionOptions ) //////////////////////////////////////////////////////////////////////////////// +func NewAddVersionOptions(list ...AddVersionOption) *AddVersionOptions { + return internal.NewAddVersionOptions(list...) +} + +// Overwrite enabled the overwrite mode for adding a component version. +func Overwrite(flag ...bool) AddVersionOption { + return internal.Overwrite(flag...) +} + +//////////////////////////////////////////////////////////////////////////////// + func NewBlobModificationOptions(list ...BlobModificationOption) *BlobModificationOptions { return internal.NewBlobModificationOptions(list...) } diff --git a/pkg/optionutils/pointer.go b/pkg/optionutils/pointer.go index a982b1c6b6..ae5298b76d 100644 --- a/pkg/optionutils/pointer.go +++ b/pkg/optionutils/pointer.go @@ -16,3 +16,9 @@ func AsValue[T any](p *T) T { } return r } + +func ApplyOption[T any](opt *T, tgt **T) { + if opt != nil { + *tgt = opt + } +} From fefcc085d53ae4cb8dbc619ebeffcd626f6630cf Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Sat, 11 Nov 2023 09:58:08 -0800 Subject: [PATCH 11/18] move down c view to base (add version) --- pkg/contexts/ocm/cpi/repocpi/base_c.go | 64 ++++++++++++++++++- pkg/contexts/ocm/cpi/repocpi/base_cv.go | 50 +++++++++++++++ pkg/contexts/ocm/cpi/repocpi/view_c.go | 82 ++++++------------------- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 49 --------------- pkg/contexts/ocm/internal/repository.go | 15 ++--- 5 files changed, 136 insertions(+), 124 deletions(-) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go index d777c56cc8..f7424042bb 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -7,9 +7,13 @@ package repocpi import ( "io" + "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" + "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/finalizer" + "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" ) @@ -79,6 +83,10 @@ func (b *componentAccessBase) GetName() string { return b.name } +func (b *componentAccessBase) IsReadOnly() bool { + return b.impl.IsReadOnly() +} + func (c *componentAccessBase) IsOwned(cv cpi.ComponentVersionAccess) bool { base, err := GetComponentVersionAccessBase(cv) if err != nil { @@ -120,6 +128,58 @@ func (b *componentAccessBase) NewVersion(version string, overrides ...bool) (cpi return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, false, !compositionmodeattr.Get(b.GetContext())) } -func (b *componentAccessBase) IsReadOnly() bool { - return b.impl.IsReadOnly() +func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) { + var finalize finalizer.Finalizer + defer finalize.FinalizeWithErrorPropagation(&ferr) + + ctx := c.GetContext() + cvbase, err := GetComponentVersionAccessBase(cv) + if err != nil { + return err + } + + var ( + d *compdesc.ComponentDescriptor + sel func(cpi.AccessSpec) bool + eff cpi.ComponentVersionAccess + ) + + forcestore := c.IsOwned(cv) + if !forcestore { + // transfer all local blobs into a new owned version. + sel = func(spec cpi.AccessSpec) bool { return spec.IsLocal(ctx) } + + eff, err = c.NewVersion(cv.GetVersion(), optionutils.AsValue(opts.Overwrite)) + if err != nil { + return err + } + finalize.With(func() error { + return eff.Close() + }) + cvbase, err = GetComponentVersionAccessBase(eff) + if err != nil { + return err + } + + d = eff.GetDescriptor() + *d = *cv.GetDescriptor().Copy() + } else { + // transfer composition blobs into local blobs + opts.UseNoDefaultIfNotSet = optionutils.PointerTo(true) + opts.BlobHandlerProvider = nil + sel = compose.Is + d = cv.GetDescriptor() + eff = cv + } + + err = setupLocalBlobs(ctx, "resource", cv, nil, cvbase, d.Resources, sel, forcestore, &opts.BlobUploadOptions) + if err == nil { + err = setupLocalBlobs(ctx, "source", cv, nil, cvbase, d.Sources, sel, forcestore, &opts.BlobUploadOptions) + } + if err != nil { + return err + } + + cvbase.EnablePersistence() + return cvbase.Update(!cvbase.UseDirectAccess()) } diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index 720dff645d..60c021b261 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -9,6 +9,7 @@ import ( "io" "sync" + "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" @@ -17,6 +18,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/finalizer" "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" @@ -315,3 +317,51 @@ func (c *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi } return acc, nil } + +//////////////////////////////////////////////////////////////////////////////// + +func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error), tgtbase ComponentVersionAccessBase, it compdesc.ArtifactAccessor, sel func(cpi.AccessSpec) bool, final bool, opts *cpi.BlobUploadOptions) (ferr error) { + var finalize finalizer.Finalizer + defer finalize.FinalizeWithErrorPropagation(&ferr) + + for i := 0; i < it.Len(); i++ { + nested := finalize.Nested() + a := it.GetArtifact(i) + spec, err := ctx.AccessSpecForSpec(a.GetAccess()) + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + if sel(spec) { + blob, err := blobAccessForLocalAccessSpec(spec, src, accprov) + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + nested.Close(blob) + + effspec, err := tgtbase.AddBlob(blob, a.GetType(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, ctx), final, opts) + if err != nil { + return errors.Wrapf(err, "cannot store %s %d", kind, i) + } + a.SetAccess(effspec) + } + err = nested.Finalize() + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + } + return nil +} + +func blobAccessForLocalAccessSpec(spec cpi.AccessSpec, cv cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error)) (blobaccess.BlobAccess, error) { + var m cpi.AccessMethod + var err error + if accprov != nil { + m, err = accprov(spec) + } else { + m, err = spec.AccessMethod(cv) + } + if err != nil { + return nil, err + } + return m.AsBlobAccess(), nil +} diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index f6cd3a707c..883b7fc748 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -9,13 +9,8 @@ import ( "io" "github.com/open-component-model/ocm/pkg/common/accessio" - "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" - "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/finalizer" - "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt/resource" "github.com/open-component-model/ocm/pkg/utils" ) @@ -28,12 +23,20 @@ type ComponentAccessViewManager = resource.ViewManager[cpi.ComponentAccess] // h type ComponentAccessBase interface { resource.ResourceImplementation[cpi.ComponentAccess] - internal.ComponentAccessImpl + GetContext() cpi.Context IsReadOnly() bool GetName() string IsOwned(access cpi.ComponentVersionAccess) bool + + ListVersions() ([]string, error) + LookupVersion(version string) (cpi.ComponentVersionAccess, error) + HasVersion(vers string) (bool, error) + NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) + + Close() error + AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) } type componentAccessView struct { @@ -110,72 +113,23 @@ func (c *componentAccessView) LookupVersion(version string) (acc cpi.ComponentVe return acc, err } -func (c *componentAccessView) AddVersion(acc cpi.ComponentVersionAccess, overrides ...bool) error { +func (c *componentAccessView) AddVersion(acc cpi.ComponentVersionAccess, overwrite ...bool) error { if acc.GetName() != c.GetName() { return errors.ErrInvalid("component name", acc.GetName()) } + return c.Execute(func() error { - return c.addVersion(acc, overrides...) + return c.base.AddVersion(acc, cpi.NewAddVersionOptions(cpi.Overwrite(utils.Optional(overwrite...)))) }) } -func (c *componentAccessView) addVersion(acc cpi.ComponentVersionAccess, overrides ...bool) (ferr error) { - var finalize finalizer.Finalizer - defer finalize.FinalizeWithErrorPropagation(&ferr) - - ctx := acc.GetContext() - - cvbase, err := GetComponentVersionAccessBase(acc) - if err != nil { - return err - } - - var ( - d *compdesc.ComponentDescriptor - sel func(cpi.AccessSpec) bool - eff cpi.ComponentVersionAccess - ) - - opts := cpi.NewBlobUploadOptions() - - forcestore := c.base.IsOwned(acc) - if !forcestore { - // transfer all local blobs into a new owned version. - sel = func(spec cpi.AccessSpec) bool { return spec.IsLocal(ctx) } - - eff, err = c.base.NewVersion(acc.GetVersion(), overrides...) - if err != nil { - return err - } - finalize.With(func() error { - return eff.Close() - }) - cvbase, err = GetComponentVersionAccessBase(eff) - if err != nil { - return err - } - - d = eff.GetDescriptor() - *d = *acc.GetDescriptor().Copy() - } else { - // transfer composition blobs into local blobs - opts.UseNoDefaultIfNotSet = optionutils.PointerTo(true) - opts.BlobHandlerProvider = nil - sel = compose.Is - d = acc.GetDescriptor() - eff = acc - } - - err = setupLocalBlobs(ctx, "resource", acc, nil, cvbase, d.Resources, sel, forcestore, opts) - if err == nil { - err = setupLocalBlobs(ctx, "source", acc, nil, cvbase, d.Sources, sel, forcestore, opts) - } - if err != nil { - return err +func (c *componentAccessView) AddVersionOpt(acc cpi.ComponentVersionAccess, opts ...cpi.AddVersionOption) error { + if acc.GetName() != c.GetName() { + return errors.ErrInvalid("component name", acc.GetName()) } - - cvbase.EnablePersistence() - return cvbase.Update(!cvbase.UseDirectAccess()) + return c.Execute(func() error { + return c.base.AddVersion(acc, cpi.NewAddVersionOptions(opts...)) + }) } func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc cpi.ComponentVersionAccess, err error) { diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index ae78f887eb..c3ef28d2bd 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -24,7 +24,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor" "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/finalizer" "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" @@ -880,51 +879,3 @@ func (c *componentVersionAccessView) GetReferencesBySelectors(selectors []compde } return references, nil } - -//////////////////////////////////////////////////////////////////////////////// - -func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error), tgtbase ComponentVersionAccessBase, it compdesc.ArtifactAccessor, sel func(cpi.AccessSpec) bool, final bool, opts *cpi.BlobUploadOptions) (ferr error) { - var finalize finalizer.Finalizer - defer finalize.FinalizeWithErrorPropagation(&ferr) - - for i := 0; i < it.Len(); i++ { - nested := finalize.Nested() - a := it.GetArtifact(i) - spec, err := ctx.AccessSpecForSpec(a.GetAccess()) - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } - if sel(spec) { - blob, err := blobAccessForLocalAccessSpec(spec, src, accprov) - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } - nested.Close(blob) - - effspec, err := tgtbase.AddBlob(blob, a.GetType(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, ctx), final, opts) - if err != nil { - return errors.Wrapf(err, "cannot store %s %d", kind, i) - } - a.SetAccess(effspec) - } - err = nested.Finalize() - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } - } - return nil -} - -func blobAccessForLocalAccessSpec(spec cpi.AccessSpec, cv cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error)) (blobaccess.BlobAccess, error) { - var m cpi.AccessMethod - var err error - if accprov != nil { - m, err = accprov(spec) - } else { - m, err = spec.AccessMethod(cv) - } - if err != nil { - return nil, err - } - return m.AsBlobAccess(), nil -} diff --git a/pkg/contexts/ocm/internal/repository.go b/pkg/contexts/ocm/internal/repository.go index 656fe981d8..7a4246c1af 100644 --- a/pkg/contexts/ocm/internal/repository.go +++ b/pkg/contexts/ocm/internal/repository.go @@ -51,7 +51,9 @@ type ( MimeType = blobaccess.MimeType ) -type ComponentAccessImpl interface { +type ComponentAccess interface { + resource.ResourceView[ComponentAccess] + GetContext() Context GetName() string @@ -59,15 +61,10 @@ type ComponentAccessImpl interface { LookupVersion(version string) (ComponentVersionAccess, error) HasVersion(vers string) (bool, error) NewVersion(version string, overrides ...bool) (ComponentVersionAccess, error) - - Close() error -} - -type ComponentAccess interface { - resource.ResourceView[ComponentAccess] - - ComponentAccessImpl AddVersion(cv ComponentVersionAccess, overrides ...bool) error + AddVersionOpt(cv ComponentVersionAccess, opts ...AddVersionOption) error + + io.Closer } // AccessProvider assembled methods provided From 67b94b4a8a7c7b1f2c8fb4d195f186988134578c Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Sat, 11 Nov 2023 16:51:41 -0800 Subject: [PATCH 12/18] move down vc view to base (update/finish) --- pkg/blobaccess/standard.go | 4 + pkg/contexts/ocm/cpi/repocpi/base_c.go | 56 ++++---- pkg/contexts/ocm/cpi/repocpi/base_cv.go | 146 +++++++++++++++------ pkg/contexts/ocm/cpi/repocpi/view_cv.go | 100 ++------------ pkg/contexts/ocm/internal/accessspecref.go | 10 ++ 5 files changed, 158 insertions(+), 158 deletions(-) diff --git a/pkg/blobaccess/standard.go b/pkg/blobaccess/standard.go index 857d6e5606..1283933458 100644 --- a/pkg/blobaccess/standard.go +++ b/pkg/blobaccess/standard.go @@ -9,6 +9,7 @@ import ( "github.com/open-component-model/ocm/pkg/blobaccess/bpi" "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/finalizer" "github.com/open-component-model/ocm/pkg/mime" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/utils" @@ -217,6 +218,7 @@ type AnnotatedBlobAccess[T DataAccess] interface { type annotatedBlobAccessView[T DataAccess] struct { _blobAccess + id finalizer.ObjectIdentity annotation T } @@ -230,6 +232,7 @@ func (a *annotatedBlobAccessView[T]) Dup() (BlobAccess, error) { return nil, err } return &annotatedBlobAccessView[T]{ + id: finalizer.NewObjectIdentity(a.id.String()), _blobAccess: b, annotation: a.annotation, }, nil @@ -247,6 +250,7 @@ func ForDataAccess[T DataAccess](digest digest.Digest, size int64, mimeType stri a := bpi.BaseAccessForDataAccessAndMeta(mimeType, access, digest, size) return &annotatedBlobAccessView[T]{ + id: finalizer.NewObjectIdentity("annotatedBlobAccess"), _blobAccess: bpi.NewBlobAccessForBase(a), annotation: access, } diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/base_c.go index f7424042bb..2ae2f964c4 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_c.go @@ -7,7 +7,7 @@ package repocpi import ( "io" - "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" + "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -132,24 +132,14 @@ func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess, opts *cp var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) - ctx := c.GetContext() cvbase, err := GetComponentVersionAccessBase(cv) if err != nil { return err } - var ( - d *compdesc.ComponentDescriptor - sel func(cpi.AccessSpec) bool - eff cpi.ComponentVersionAccess - ) - forcestore := c.IsOwned(cv) if !forcestore { - // transfer all local blobs into a new owned version. - sel = func(spec cpi.AccessSpec) bool { return spec.IsLocal(ctx) } - - eff, err = c.NewVersion(cv.GetVersion(), optionutils.AsValue(opts.Overwrite)) + eff, err := c.NewVersion(cv.GetVersion(), optionutils.AsValue(opts.Overwrite)) if err != nil { return err } @@ -161,25 +151,35 @@ func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess, opts *cp return err } - d = eff.GetDescriptor() + d := eff.GetDescriptor() *d = *cv.GetDescriptor().Copy() - } else { - // transfer composition blobs into local blobs - opts.UseNoDefaultIfNotSet = optionutils.PointerTo(true) - opts.BlobHandlerProvider = nil - sel = compose.Is - d = cv.GetDescriptor() - eff = cv - } - err = setupLocalBlobs(ctx, "resource", cv, nil, cvbase, d.Resources, sel, forcestore, &opts.BlobUploadOptions) - if err == nil { - err = setupLocalBlobs(ctx, "source", cv, nil, cvbase, d.Sources, sel, forcestore, &opts.BlobUploadOptions) + err = c.setupLocalBlobs("resource", cv, cvbase, d.Resources, &opts.BlobUploadOptions) + if err == nil { + err = c.setupLocalBlobs("source", cv, cvbase, d.Sources, &opts.BlobUploadOptions) + } + if err != nil { + return err + } } - if err != nil { - return err + cvbase.EnablePersistence() + err = cvbase.Update(!cvbase.UseDirectAccess()) + return err +} + +func (c *componentAccessBase) setupLocalBlobs(kind string, src cpi.ComponentVersionAccess, tgtbase ComponentVersionAccessBase, it compdesc.ArtifactAccessor, opts *cpi.BlobUploadOptions) (ferr error) { + ctx := src.GetContext() + // transfer all local blobs + prov := func(spec cpi.AccessSpec) (blob blobaccess.BlobAccess, ref string, global cpi.AccessSpec, err error) { + if spec.IsLocal(ctx) { + m, err := spec.AccessMethod(src) + if err != nil { + return nil, "", nil, err + } + return m.AsBlobAccess(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, tgtbase.GetContext()), nil + } + return nil, "", nil, nil } - cvbase.EnablePersistence() - return cvbase.Update(!cvbase.UseDirectAccess()) + return tgtbase.(*componentVersionAccessBase).setupLocalBlobs(kind, prov, it, false, opts) } diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index 60c021b261..9f67fcecf9 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -6,6 +6,7 @@ package repocpi import ( "encoding/json" + "fmt" "io" "sync" @@ -13,6 +14,7 @@ import ( "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" + "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -65,6 +67,7 @@ type _componentVersionAccessImplBase = resource.ResourceImplBase[cpi.ComponentVe // implement provider-agnostic parts of the ComponentVersionAccess API. type componentVersionAccessBase struct { lock sync.Mutex + id finalizer.ObjectIdentity *_componentVersionAccessImplBase ctx cpi.Context @@ -91,6 +94,7 @@ func newComponentVersionAccessBase(name, version string, impl ComponentVersionAc } b := &componentVersionAccessBase{ _componentVersionAccessImplBase: base, + id: finalizer.NewObjectIdentity(fmt.Sprintf("%s:%s", name, version)), ctx: impl.GetContext(), name: name, version: version, @@ -124,6 +128,11 @@ func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersi func (b *componentVersionAccessBase) Close() error { list := errors.ErrListf("closing component version %s", common.VersionedElementKey(b)) refmgmt.AllocLog.Trace("closing component version base", "name", common.VersionedElementKey(b)) + // prepare artifact access for final close in + // direct access mode. + if !compositionmodeattr.Get(b.ctx) { + list.Add(b.update(true)) + } list.Add(b.impl.Close()) list.Add(b._componentVersionAccessImplBase.Close()) list.Add(b.blobcache.Clear()) @@ -183,8 +192,27 @@ func (b *componentVersionAccessBase) IsReadOnly() bool { //////////////////////////////////////////////////////////////////////////////// // with access to actual view -func (b *componentVersionAccessBase) AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) { - return b.impl.AccessMethod(acc, cv) +func (b *componentVersionAccessBase) AccessMethod(spec cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (meth cpi.AccessMethod, err error) { + switch { + case compose.Is(spec): + cspec, ok := spec.(*compose.AccessSpec) + if !ok { + return nil, fmt.Errorf("invalid implementation (%T) for access method compose", spec) + } + blob := b.getLocalBlob(cspec) + if blob == nil { + return nil, errors.ErrUnknown(blobaccess.KIND_BLOB, cspec.Id, common.VersionedElementKey(b).String()) + } + meth, err = compose.NewMethod(cspec, blob) + case spec.IsLocal(b.ctx): + meth, err = b.impl.AccessMethod(spec, cv) + if err == nil { + if blob := b.getLocalBlob(spec); blob != nil { + meth, err = newFakeMethod(meth, blob) + } + } + } + return meth, err } func (b *componentVersionAccessBase) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { @@ -195,6 +223,10 @@ func (b *componentVersionAccessBase) GetDescriptor() *compdesc.ComponentDescript b.lock.Lock() defer b.lock.Unlock() + return b.getDescriptor() +} + +func (b *componentVersionAccessBase) getDescriptor() *compdesc.ComponentDescriptor { if b.descriptor == nil { b.descriptor = b.impl.GetDescriptor() } @@ -208,6 +240,7 @@ func (b *componentVersionAccessBase) GetStorageContext() cpi.StorageContext { func (b *componentVersionAccessBase) ShouldUpdate(final bool) bool { b.lock.Lock() defer b.lock.Unlock() + return b.shouldUpdate(final) } @@ -225,17 +258,48 @@ func (b *componentVersionAccessBase) Update(final bool) error { b.lock.Lock() defer b.lock.Unlock() - if b.shouldUpdate(final) { - return b.impl.SetDescriptor(b.descriptor.Copy()) + return b.update(final) +} + +func (b *componentVersionAccessBase) update(final bool) error { + if !b.shouldUpdate(final) { + return nil } - return nil + + d := b.getDescriptor() + + opts := &cpi.BlobUploadOptions{ + UseNoDefaultIfNotSet: optionutils.PointerTo(true), + } + err := b.setupLocalBlobs("resource", b.composeAccess, d.Resources, true, opts) + if err == nil { + err = b.setupLocalBlobs("source", b.composeAccess, d.Sources, true, opts) + } + if err != nil { + return err + } + + err = b.impl.SetDescriptor(b.descriptor.Copy()) + if err != nil { + return err + } + err = b.blobcache.Clear() + return err +} + +func (b *componentVersionAccessBase) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { + key, err := json.Marshal(acc) + if err != nil { + return nil + } + return b.blobcache.GetBlobFor(string(key)) } -func (c *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { +func (b *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } - if c.IsReadOnly() { + if b.IsReadOnly() { return nil, accessio.ErrReadOnly } blob, err := blob.Dup() @@ -248,8 +312,7 @@ func (c *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refNa return nil, errors.Wrapf(err, "invalid blob access") } - storagectx := c.GetStorageContext() - ctx := c.GetContext() + ctx := b.GetContext() // handle foreign blob upload var prov cpi.BlobHandlerProvider @@ -258,12 +321,12 @@ func (c *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refNa } else { if !optionutils.AsValue(opts.UseNoDefaultIfNotSet) { prov = internal.BlobHandlerProviderForRegistry(ctx.BlobHandlers()) - } else { - //nolint: staticcheck // yes + } else { //nolint: staticcheck // yes // use no blob uploader } } if prov != nil { + storagectx := b.GetStorageContext() h := prov.LookupHandler(storagectx, artType, blob.MimeType()) if h != nil { acc, err := h.StoreBlob(blob, artType, refName, nil, storagectx) @@ -281,8 +344,8 @@ func (c *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refNa var acc cpi.AccessSpec - if final || c.UseDirectAccess() { - acc, err = c.impl.AddBlob(blob, refName, global) + if final || b.UseDirectAccess() { + acc, err = b.impl.AddBlob(blob, refName, global) if err != nil { return nil, err } @@ -290,10 +353,10 @@ func (c *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refNa // use local composition access to be added to the repository with AddVersion. acc = compose.New(refName, blob.MimeType(), global) } - return c.cacheLocalBlob(acc, blob) + return b.cacheLocalBlob(acc, blob) } -func (c *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { +func (b *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { key, err := json.Marshal(acc) if err != nil { return nil, errors.Wrapf(err, "cannot marshal access spec") @@ -311,7 +374,8 @@ func (c *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi // The access spec is independent of the actual repo, so it does // not have access to those credentials. Therefore, we have to // keep the original blob for further usage, also. - err = c.GetBlobCache().AddBlobFor(string(key), blob) + k := BlobCacheKey(string(key)) + err = b.blobcache.AddBlobFor(k, blob) if err != nil { return nil, err } @@ -320,25 +384,45 @@ func (c *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi //////////////////////////////////////////////////////////////////////////////// -func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error), tgtbase ComponentVersionAccessBase, it compdesc.ArtifactAccessor, sel func(cpi.AccessSpec) bool, final bool, opts *cpi.BlobUploadOptions) (ferr error) { +func (b *componentVersionAccessBase) composeAccess(spec cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error) { + if !compose.Is(spec) { + return nil, "", nil, nil + } + cspec, ok := spec.(*compose.AccessSpec) + if !ok { + return nil, "", nil, fmt.Errorf("invalid implementation (%T) for access method compose", spec) + } + blob := b.getLocalBlob(cspec) + if blob == nil { + return nil, "", nil, errors.ErrUnknown(blobaccess.KIND_BLOB, cspec.Id, common.VersionedElementKey(b).String()) + } + blob, err := blob.Dup() + if err != nil { + return nil, "", nil, errors.Wrapf(err, "cached blob") + } + + return blob, cspec.ReferenceName, cspec.GlobalAccess.Get(), nil +} + +func (b *componentVersionAccessBase) setupLocalBlobs(kind string, accprov func(cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error), it compdesc.ArtifactAccessor, final bool, opts *cpi.BlobUploadOptions) (ferr error) { var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) for i := 0; i < it.Len(); i++ { nested := finalize.Nested() a := it.GetArtifact(i) - spec, err := ctx.AccessSpecForSpec(a.GetAccess()) + spec, err := b.ctx.AccessSpecForSpec(a.GetAccess()) if err != nil { return errors.Wrapf(err, "%s %d", kind, i) } - if sel(spec) { - blob, err := blobAccessForLocalAccessSpec(spec, src, accprov) - if err != nil { - return errors.Wrapf(err, "%s %d", kind, i) - } + blob, ref, global, err := accprov(spec) + if err != nil { + return errors.Wrapf(err, "%s %d", kind, i) + } + if blob != nil { nested.Close(blob) - effspec, err := tgtbase.AddBlob(blob, a.GetType(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, ctx), final, opts) + effspec, err := b.AddBlob(blob, a.GetType(), ref, global, final, opts) if err != nil { return errors.Wrapf(err, "cannot store %s %d", kind, i) } @@ -351,17 +435,3 @@ func setupLocalBlobs(ctx cpi.Context, kind string, src cpi.ComponentVersionAcces } return nil } - -func blobAccessForLocalAccessSpec(spec cpi.AccessSpec, cv cpi.ComponentVersionAccess, accprov func(cpi.AccessSpec) (cpi.AccessMethod, error)) (blobaccess.BlobAccess, error) { - var m cpi.AccessMethod - var err error - if accprov != nil { - m, err = accprov(spec) - } else { - m, err = spec.AccessMethod(cv) - } - if err != nil { - return nil, err - } - return m.AsBlobAccess(), nil -} diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index c3ef28d2bd..81df69e5ae 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -5,18 +5,14 @@ package repocpi import ( - "encoding/json" "fmt" "io" "strconv" - "github.com/opencontainers/go-digest" - "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/common/accessio" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose" - "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" @@ -24,7 +20,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor" "github.com/open-component-model/ocm/pkg/errors" - "github.com/open-component-model/ocm/pkg/optionutils" "github.com/open-component-model/ocm/pkg/refmgmt" "github.com/open-component-model/ocm/pkg/refmgmt/resource" "github.com/open-component-model/ocm/pkg/utils" @@ -67,7 +62,7 @@ type ComponentVersionAccessBase interface { // GetStorageContext creates a storage context for blobs // that is used to feed blob handlers for specific blob storage methods. - // If no handler accepts the blob, the AddBlobFor method will + // If no handler accepts the blob, the AddBlob method will // be used to store the blob GetStorageContext() cpi.StorageContext @@ -132,7 +127,6 @@ func artifactAccessViewCreator(i ComponentVersionAccessBase, v resource.CloserVi _componentVersionAccessView: resource.NewView[cpi.ComponentVersionAccess](v, d), base: i, } - v.Allocatable().BeforeCleanup(refmgmt.CleanupHandlerFunc(cv.finish)) return cv } @@ -154,16 +148,6 @@ func (c *componentVersionAccessView) Close() error { return list.Add(c.err, err).Result() } -func (c *componentVersionAccessView) finish() { - if !c.IsClosed() { - // prepare artifact access for final close in - // direct access mode. - if !compositionmodeattr.Get(c.GetContext()) { - c.err = c.update(true) - } - } -} - func (c *componentVersionAccessView) Repository() cpi.Repository { return c.base.Repository() } @@ -210,35 +194,11 @@ func (c *componentVersionAccessView) AccessMethod(spec cpi.AccessSpec) (meth cpi func (c *componentVersionAccessView) accessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) { switch { - case compose.Is(spec): - cspec, ok := spec.(*compose.AccessSpec) - if !ok { - return nil, fmt.Errorf("invalid implementation (%T) for access method compose", spec) - } - blob := c.getLocalBlob(cspec) - if blob == nil { - return nil, errors.ErrUnknown(blobaccess.KIND_BLOB, cspec.Id, common.VersionedElementKey(c).String()) - } - meth, err = compose.NewMethod(cspec, blob) - case !spec.IsLocal(c.GetContext()): - meth, err = spec.AccessMethod(c) + case spec.IsLocal(c.GetContext()): + return c.base.AccessMethod(spec, c.Allocatable()) default: - meth, err = c.base.AccessMethod(spec, c.Allocatable()) - if err == nil { - if blob := c.getLocalBlob(spec); blob != nil { - meth, err = newFakeMethod(meth, blob) - } - } - } - return meth, err -} - -func (c *componentVersionAccessView) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { - key, err := json.Marshal(acc) - if err != nil { - return nil + return spec.AccessMethod(c) } - return c.base.GetBlobCache().GetBlobFor(string(key)) } func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec cpi.AccessSpec) string { @@ -274,40 +234,10 @@ func (c *componentVersionAccessView) Update() error { if !c.base.IsPersistent() { return ErrTempVersion } - return c.update(true) + return c.base.Update(true) }) } -func (c *componentVersionAccessView) update(final bool) error { - if !c.base.ShouldUpdate(final) { - return nil - } - - ctx := c.GetContext() - d := c.GetDescriptor() - impl, err := GetComponentVersionAccessBase(c) - if err != nil { - return err - } - opts := &cpi.BlobUploadOptions{ - UseNoDefaultIfNotSet: optionutils.PointerTo(true), - } - // TODO: exceute for separately lockable view - err = setupLocalBlobs(ctx, "resource", c, c.accessMethod, impl, d.Resources, compose.Is, true, opts) - if err == nil { - err = setupLocalBlobs(ctx, "source", c, c.accessMethod, impl, d.Sources, compose.Is, true, opts) - } - if err != nil { - return err - } - - err = c.base.Update(true) - if err != nil { - return err - } - return c.base.GetBlobCache().Clear() -} - func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, opts ...internal.BlobUploadOption) (cpi.AccessSpec, error) { var spec cpi.AccessSpec eff := cpi.NewBlobUploadOptions(opts...) @@ -518,20 +448,6 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac if err != nil { return err } - if blob := c.getLocalBlob(spec); blob != nil { - var dig digest.Digest - if s, ok := meth.(blobaccess.DigestSource); ok { - dig = s.Digest() - } - err = meth.Close() - if err != nil { - return errors.Wrapf(err, "clsoing shadowed method") - } - meth, err = accspeccpi.NewDefaultMethodForBlobAccess(c, spec, dig, blob, spec.IsLocal(c.GetContext())) - if err != nil { - return err - } - } defer meth.Close() return c.Execute(func() error { @@ -609,7 +525,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } else { cd.Resources[idx] = *res } - return c.update(false) + return c.base.Update(false) }) } @@ -673,7 +589,7 @@ func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdes } else { cd.Sources[idx] = *res } - return c.update(false) + return c.base.Update(false) }) } @@ -685,7 +601,7 @@ func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference) e } else { cd.References[idx] = *ref } - return c.update(false) + return c.base.Update(false) }) } diff --git a/pkg/contexts/ocm/internal/accessspecref.go b/pkg/contexts/ocm/internal/accessspecref.go index 5f30c43b39..c61665b96d 100644 --- a/pkg/contexts/ocm/internal/accessspecref.go +++ b/pkg/contexts/ocm/internal/accessspecref.go @@ -45,6 +45,16 @@ func NewRawAccessSpecRef(data []byte, unmarshaler runtime.Unmarshaler) (*AccessS return &AccessSpecRef{generic: &spec}, nil } +func (a *AccessSpecRef) Get() AccessSpec { + if a == nil { + return nil + } + if a.evaluated != nil { + return a.evaluated + } + return a.generic +} + func (a *AccessSpecRef) Set(spec AccessSpec) { if g, ok := spec.(*GenericAccessSpec); ok { a.evaluated = nil From c80c7bca35c35d9ebe5c41b749d6162497f51db2 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Sun, 12 Nov 2023 05:41:04 -0800 Subject: [PATCH 13/18] generic storage backend implementation interface --- pkg/contexts/ocm/cpi/repocpi/backend.go | 283 ++++++++++++++++++++++++ pkg/contexts/ocm/cpi/repocpi/base_cv.go | 59 +++++ pkg/contexts/ocm/cpi/repocpi/doc.go | 11 +- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 56 ----- 4 files changed, 352 insertions(+), 57 deletions(-) create mode 100644 pkg/contexts/ocm/cpi/repocpi/backend.go diff --git a/pkg/contexts/ocm/cpi/repocpi/backend.go b/pkg/contexts/ocm/cpi/repocpi/backend.go new file mode 100644 index 0000000000..f342e9adce --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/backend.go @@ -0,0 +1,283 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package repocpi + +import ( + "io" + "sync/atomic" + + "github.com/open-component-model/ocm/pkg/common" + "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/refmgmt" + "github.com/open-component-model/ocm/pkg/utils" +) + +// StorageBackendImpl is an interface which can be implemented +// to provide a complete repository view with repository, component +// and component version objects, which are generically implemented +// based on the methods of this interface. +// +// A repositotry interface based on this implementation interface can be +// created using the function NewStorageBackend. +type StorageBackendImpl interface { + // repository related methods. + + io.Closer + GetContext() cpi.Context + GetSpecification() cpi.RepositorySpec + IsReadOnly() bool + + ComponentLister() cpi.ComponentLister + HasComponent(name string) (bool, error) + HasComponentVersion(key common.NameVersion) (bool, error) + + // component related methods. + + ListVersions(comp string) ([]string, error) + HasVersion(vers string) (bool, error) + + // version related methods. + + GetDescriptor(key common.NameVersion) (*compdesc.ComponentDescriptor, error) + SetDescriptor(key common.NameVersion, descriptor *compdesc.ComponentDescriptor) error + AccessMethod(key common.NameVersion, acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) + GetInexpensiveContentVersionIdentity(key common.NameVersion, acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string + GetStorageContext(key common.NameVersion) cpi.StorageContext + GetBlob(key common.NameVersion, name string) (cpi.DataAccess, error) + AddBlob(key common.NameVersion, blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) +} + +type storageBackendRepository struct { + base RepositoryBase + closed atomic.Bool + noref cpi.Repository + kind string + impl StorageBackendImpl +} + +var _ RepositoryImpl = (*storageBackendRepository)(nil) + +// NewStorageBackend provides a complete repository view +// with repository, component and component version objects +// based on the implementation of the sole interface StorageBackendImpl. +// No further implementations are required besides a dedicated +// specification object, the dependent object +// types are generically provided based on the methods of this +// interface. +// The kind parameter is used to denote the kind of repository +// in ids and log messages. +func NewStorageBackend(kind string, impl StorageBackendImpl) cpi.Repository { + backend := &storageBackendRepository{ + impl: impl, + kind: kind, + } + return NewRepository(backend, kind) +} + +func (s *storageBackendRepository) SetBase(base RepositoryBase) { + s.base = base + s.noref = NewNoneRefRepositoryView(base) +} + +func (s *storageBackendRepository) Close() error { + if s.closed.Swap(true) { + return ErrClosed + } + return s.impl.Close() +} + +func (s *storageBackendRepository) GetContext() cpi.Context { + return s.impl.GetContext() +} + +func (s *storageBackendRepository) GetSpecification() cpi.RepositorySpec { + return s.impl.GetSpecification() +} + +func (s *storageBackendRepository) ComponentLister() cpi.ComponentLister { + return s.impl.ComponentLister() +} + +func (s *storageBackendRepository) ExistsComponentVersion(name string, version string) (bool, error) { + return s.impl.HasComponentVersion(common.NewNameVersion(name, version)) +} + +func (s *storageBackendRepository) LookupComponent(name string) (*ComponentAccessInfo, error) { + if ok, err := s.impl.HasComponent(name); !ok || err != nil { + return nil, err + } + impl := &storageBackendComponent{ + repo: s, + name: name, + } + return &ComponentAccessInfo{ + Impl: impl, + Kind: s.kind + " component", + Main: true, + }, nil +} + +//////////////////////////////////////////////////////////////////////////////// + +type storageBackendComponent struct { + base ComponentAccessBase + repo *storageBackendRepository + name string +} + +var _ ComponentAccessImpl = (*storageBackendComponent)(nil) + +func (s *storageBackendComponent) SetBase(base ComponentAccessBase) { + s.base = base +} + +func (s *storageBackendComponent) GetParentBase() RepositoryViewManager { + return s.repo.base +} + +func (s *storageBackendComponent) Close() error { + return nil +} + +func (s *storageBackendComponent) GetContext() cpi.Context { + return s.repo.impl.GetContext() +} + +func (s *storageBackendComponent) GetName() string { + return s.name +} + +func (s *storageBackendComponent) IsReadOnly() bool { + return s.repo.impl.IsReadOnly() +} + +func (s *storageBackendComponent) ListVersions() ([]string, error) { + return s.repo.impl.ListVersions(s.name) +} + +func (s *storageBackendComponent) HasVersion(vers string) (bool, error) { + return s.repo.impl.HasVersion(s.name) +} + +func (s *storageBackendComponent) LookupVersion(version string) (*ComponentVersionAccessInfo, error) { + if ok, err := s.repo.impl.HasComponentVersion(common.NewNameVersion(s.name, version)); !ok || err != nil { + return nil, err + } + + name := common.NewNameVersion(s.name, version) + d, err := s.repo.impl.GetDescriptor(name) + if err != nil { + return nil, err + } + + impl := &storageBackendComponentVersion{ + comp: s, + name: name, + descriptor: d, + } + return &ComponentVersionAccessInfo{ + Impl: impl, + Lazy: true, + Persistent: true, + }, nil +} + +func (s *storageBackendComponent) NewVersion(version string, overwrite ...bool) (*ComponentVersionAccessInfo, error) { + ok, err := s.repo.impl.HasComponentVersion(common.NewNameVersion(s.name, version)) + if err != nil { + return nil, err + } + if ok && !utils.Optional(overwrite...) { + return nil, errors.ErrAlreadyExists(cpi.KIND_COMPONENTVERSION, s.name+"/"+version) + } + + name := common.NewNameVersion(s.name, version) + d := compdesc.New(s.name, version) + + impl := &storageBackendComponentVersion{ + comp: s, + name: name, + descriptor: d, + } + return &ComponentVersionAccessInfo{ + Impl: impl, + Lazy: true, + Persistent: false, + }, nil +} + +//////////////////////////////////////////////////////////////////////////////// + +type storageBackendComponentVersion struct { + base ComponentVersionAccessBase + comp *storageBackendComponent + name common.NameVersion + descriptor *compdesc.ComponentDescriptor +} + +var _ ComponentVersionAccessImpl = (*storageBackendComponentVersion)(nil) + +func (s *storageBackendComponentVersion) Close() error { + return nil +} + +func (s *storageBackendComponentVersion) GetContext() cpi.Context { + return s.comp.repo.impl.GetContext() +} + +func (s *storageBackendComponentVersion) SetBase(base ComponentVersionAccessBase) { + s.base = base +} + +func (s *storageBackendComponentVersion) GetParentBase() ComponentAccessBase { + return s.comp.base +} + +func (s *storageBackendComponentVersion) Repository() cpi.Repository { + return s.comp.repo.noref +} + +func (s *storageBackendComponentVersion) IsReadOnly() bool { + return s.comp.repo.impl.IsReadOnly() +} + +func (s *storageBackendComponentVersion) GetDescriptor() *compdesc.ComponentDescriptor { + d, err := s.comp.repo.impl.GetDescriptor(s.name) + if err != nil { + return nil // TODO: handler error + } + return d +} + +func (s *storageBackendComponentVersion) SetDescriptor(descriptor *compdesc.ComponentDescriptor) error { + err := s.comp.repo.impl.SetDescriptor(s.name, descriptor) + if err != nil { + return err + } + s.descriptor = descriptor + return nil +} + +func (s *storageBackendComponentVersion) AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) { + return s.comp.repo.impl.AccessMethod(s.name, acc, cv) +} + +func (s *storageBackendComponentVersion) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { + return s.comp.repo.impl.GetInexpensiveContentVersionIdentity(s.name, acc, cv) +} + +func (s storageBackendComponentVersion) GetStorageContext() cpi.StorageContext { + return s.comp.repo.impl.GetStorageContext(s.name) +} + +func (s storageBackendComponentVersion) GetBlob(name string) (cpi.DataAccess, error) { + return s.comp.repo.impl.GetBlob(s.name, name) +} + +func (s storageBackendComponentVersion) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) { + return s.comp.repo.impl.AddBlob(s.name, blob, refName, global) +} diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/base_cv.go index 9f67fcecf9..0064d5f2bb 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/base_cv.go @@ -18,6 +18,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" + "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi" "github.com/open-component-model/ocm/pkg/contexts/ocm/internal" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/finalizer" @@ -435,3 +436,61 @@ func (b *componentVersionAccessBase) setupLocalBlobs(kind string, accprov func(c } return nil } + +//////////////////////////////////////////////////////////////////////////////// + +type fakeMethod struct { + spec cpi.AccessSpec + local bool + mime string + blob blobaccess.BlobAccess +} + +var _ accspeccpi.AccessMethodImpl = (*fakeMethod)(nil) + +func newFakeMethod(m cpi.AccessMethod, blob cpi.BlobAccess) (cpi.AccessMethod, error) { + b, err := blob.Dup() + if err != nil { + return nil, errors.Wrapf(err, "cannot remember blob for access method") + } + f := &fakeMethod{ + spec: m.AccessSpec(), + local: m.IsLocal(), + mime: m.MimeType(), + blob: b, + } + err = m.Close() + if err != nil { + _ = b.Close() + return nil, errors.Wrapf(err, "closing access method") + } + return accspeccpi.AccessMethodForImplementation(f, nil) +} + +func (f *fakeMethod) MimeType() string { + return f.mime +} + +func (f *fakeMethod) IsLocal() bool { + return f.local +} + +func (f *fakeMethod) GetKind() string { + return f.spec.GetKind() +} + +func (f *fakeMethod) AccessSpec() internal.AccessSpec { + return f.spec +} + +func (f *fakeMethod) Close() error { + return f.blob.Close() +} + +func (f *fakeMethod) Reader() (io.ReadCloser, error) { + return f.blob.Reader() +} + +func (f *fakeMethod) Get() ([]byte, error) { + return f.blob.Get() +} diff --git a/pkg/contexts/ocm/cpi/repocpi/doc.go b/pkg/contexts/ocm/cpi/repocpi/doc.go index 13dce2c962..e72f369079 100644 --- a/pkg/contexts/ocm/cpi/repocpi/doc.go +++ b/pkg/contexts/ocm/cpi/repocpi/doc.go @@ -41,5 +41,14 @@ // and their descriptors. The keep a reference to component implementations, which are // again based of repository implementations. The task of repository implementations is // to provide component objects. Their implementations are the responsible to provide -// component version objects, +// component version objects. +// +// Besides this basic implementation interface with separated object for a +// repository, component and component version, there is support for a simplified +// implementation interface (StorageBackendImpl). This is a single interface +// bundling all required functionality to implement the object for the three +// concerned elements. With NewStorageBackend it is possible to instantiate +// a new kind of repository based on this single interface. The required +// objects for components and component versions are generically provided +// based on the methods provided by this interface. package repocpi diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index 81df69e5ae..6f4df0e845 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -302,62 +302,6 @@ func (c *componentVersionAccessView) SetSourceBlob(meta *cpi.SourceMeta, blob cp return nil } -type fakeMethod struct { - spec cpi.AccessSpec - local bool - mime string - blob blobaccess.BlobAccess -} - -var _ accspeccpi.AccessMethodImpl = (*fakeMethod)(nil) - -func newFakeMethod(m cpi.AccessMethod, blob cpi.BlobAccess) (cpi.AccessMethod, error) { - b, err := blob.Dup() - if err != nil { - return nil, errors.Wrapf(err, "cannot remember blob for access method") - } - f := &fakeMethod{ - spec: m.AccessSpec(), - local: m.IsLocal(), - mime: m.MimeType(), - blob: b, - } - err = m.Close() - if err != nil { - _ = b.Close() - return nil, errors.Wrapf(err, "closing access method") - } - return accspeccpi.AccessMethodForImplementation(f, nil) -} - -func (f *fakeMethod) MimeType() string { - return f.mime -} - -func (f *fakeMethod) IsLocal() bool { - return f.local -} - -func (f *fakeMethod) GetKind() string { - return f.spec.GetKind() -} - -func (f *fakeMethod) AccessSpec() internal.AccessSpec { - return f.spec -} - -func (f *fakeMethod) Close() error { - return f.blob.Close() -} - -func (f *fakeMethod) Reader() (io.ReadCloser, error) { - return f.blob.Reader() -} - -func (f *fakeMethod) Get() ([]byte, error) { - return f.blob.Get() -} - func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessView, kind string, art A, set func(*T, compdesc.AccessSpec) error, setblob func(*T, cpi.BlobAccess, string, cpi.AccessSpec) error, From 50c02e49cdf70dcf995fd1e746f5bb770add1a75 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Mon, 13 Nov 2023 02:07:04 -0800 Subject: [PATCH 14/18] tour04-config --- examples/lib/helper/helper.go | 16 +- .../lib/tour/01-getting-started/README.md | 13 ++ ... => 01-basic-componentversion-creation.go} | 1 + ...example-b.go => 02-composition-version.go} | 0 .../README.md | 10 + .../{example-a.go => 01-using-credentials.go} | 0 ...b.go => 02-basic-credential-management.go} | 19 +- .../03-credential-repositories.go | 88 +++++++++ .../03-working-with-credentials/README.md | 21 +++ .../03-working-with-credentials/common.go | 14 ++ .../tour/03-working-with-credentials/main.go | 2 + .../01-basic-config-management.go | 116 ++++++++++++ .../02-handle-arbitrary-config.go | 91 +++++++++ .../03-using-ocm-config.go | 111 +++++++++++ .../04-write-config-type.go | 173 ++++++++++++++++++ .../05-write-config-consumer.go | 125 +++++++++++++ .../lib/tour/04-working-with-config/README.md | 23 +++ .../lib/tour/04-working-with-config/common.go | 26 +++ .../lib/tour/04-working-with-config/main.go | 68 +++++++ .../resources/ocmconfig | 8 + .../README.md | 35 ++++ .../{example-a.go => 01-basic-signing.go} | 0 ...mple-b.go => 02-using-context-settings.go} | 2 +- .../06-signing-component-versions/README.md | 19 ++ .../06-signing-component-versions/main.go | 2 +- examples/lib/tour/README.md | 15 ++ pkg/contexts/config/internal/config.go | 15 ++ .../repositories/dockerconfig/type.go | 3 + 28 files changed, 992 insertions(+), 24 deletions(-) create mode 100644 examples/lib/tour/01-getting-started/README.md rename examples/lib/tour/02-composing-a-component-version/{example-a.go => 01-basic-componentversion-creation.go} (99%) rename examples/lib/tour/02-composing-a-component-version/{example-b.go => 02-composition-version.go} (100%) create mode 100644 examples/lib/tour/02-composing-a-component-version/README.md rename examples/lib/tour/03-working-with-credentials/{example-a.go => 01-using-credentials.go} (100%) rename examples/lib/tour/03-working-with-credentials/{example-b.go => 02-basic-credential-management.go} (95%) create mode 100644 examples/lib/tour/03-working-with-credentials/03-credential-repositories.go create mode 100644 examples/lib/tour/03-working-with-credentials/README.md create mode 100644 examples/lib/tour/04-working-with-config/01-basic-config-management.go create mode 100644 examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go create mode 100644 examples/lib/tour/04-working-with-config/03-using-ocm-config.go create mode 100644 examples/lib/tour/04-working-with-config/04-write-config-type.go create mode 100644 examples/lib/tour/04-working-with-config/05-write-config-consumer.go create mode 100644 examples/lib/tour/04-working-with-config/README.md create mode 100644 examples/lib/tour/04-working-with-config/common.go create mode 100644 examples/lib/tour/04-working-with-config/main.go create mode 100644 examples/lib/tour/04-working-with-config/resources/ocmconfig create mode 100644 examples/lib/tour/05-transporting-component-versions/README.md rename examples/lib/tour/06-signing-component-versions/{example-a.go => 01-basic-signing.go} (100%) rename examples/lib/tour/06-signing-component-versions/{example-b.go => 02-using-context-settings.go} (98%) create mode 100644 examples/lib/tour/06-signing-component-versions/README.md create mode 100644 examples/lib/tour/README.md diff --git a/examples/lib/helper/helper.go b/examples/lib/helper/helper.go index 26bd9ad00d..0739fa781c 100644 --- a/examples/lib/helper/helper.go +++ b/examples/lib/helper/helper.go @@ -15,14 +15,14 @@ import ( ) type Config struct { - Username string `json:"username"` - Password string `json:"password"` - Component string `json:"component"` - Repository string `json:"repository"` - Version string `json:"version"` - - Target json.RawMessage `json:"targetRepository"` - OCMConfig string `json:"ocmConfig"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Component string `json:"component,omitempty"` + Repository string `json:"repository,omitempty"` + Version string `json:"version,omitempty"` + + Target json.RawMessage `json:"targetRepository,omitempty"` + OCMConfig string `json:"ocmConfig,omitempty"` } func ReadConfig(path string) (*Config, error) { diff --git a/examples/lib/tour/01-getting-started/README.md b/examples/lib/tour/01-getting-started/README.md new file mode 100644 index 0000000000..b9623c4739 --- /dev/null +++ b/examples/lib/tour/01-getting-started/README.md @@ -0,0 +1,13 @@ +# Basic Usage of OCM Repositories + +This [tour](example.go) illustrates the basic usage of the API to +access component versions in an OCM repository. + +You can just call the main program with some config file argument +with the following content: + +```yaml +component: github.com/mandelsoft/examples/cred1 +repository: ghcr.io/mandelsoft/ocm +version: 0.1.0 +``` \ No newline at end of file diff --git a/examples/lib/tour/02-composing-a-component-version/example-a.go b/examples/lib/tour/02-composing-a-component-version/01-basic-componentversion-creation.go similarity index 99% rename from examples/lib/tour/02-composing-a-component-version/example-a.go rename to examples/lib/tour/02-composing-a-component-version/01-basic-componentversion-creation.go index 6a5fa0142d..08785c7097 100644 --- a/examples/lib/tour/02-composing-a-component-version/example-a.go +++ b/examples/lib/tour/02-composing-a-component-version/01-basic-componentversion-creation.go @@ -279,6 +279,7 @@ func listVersions(repo ocm.Repository, list ...string) error { } return nil } + func ComposingAComponentVersionA() error { // yes, we need an OCM context, again ctx := ocm.DefaultContext() diff --git a/examples/lib/tour/02-composing-a-component-version/example-b.go b/examples/lib/tour/02-composing-a-component-version/02-composition-version.go similarity index 100% rename from examples/lib/tour/02-composing-a-component-version/example-b.go rename to examples/lib/tour/02-composing-a-component-version/02-composition-version.go diff --git a/examples/lib/tour/02-composing-a-component-version/README.md b/examples/lib/tour/02-composing-a-component-version/README.md new file mode 100644 index 0000000000..6a7ac375cf --- /dev/null +++ b/examples/lib/tour/02-composing-a-component-version/README.md @@ -0,0 +1,10 @@ +# Composing a Component Version + +This tor illustrates the basic usage of the API to +create/compose component versions. + +It covers two basic scenarios: +- [`basic`](01-basic-componentversion-creation.go) Create a component version stored in the filesystem +- [`compose`](02-composition-version.go) Create a component version stored in memory using a non-persistent composition version. + +You can just call the main program with the scenario as argument. diff --git a/examples/lib/tour/03-working-with-credentials/example-a.go b/examples/lib/tour/03-working-with-credentials/01-using-credentials.go similarity index 100% rename from examples/lib/tour/03-working-with-credentials/example-a.go rename to examples/lib/tour/03-working-with-credentials/01-using-credentials.go diff --git a/examples/lib/tour/03-working-with-credentials/example-b.go b/examples/lib/tour/03-working-with-credentials/02-basic-credential-management.go similarity index 95% rename from examples/lib/tour/03-working-with-credentials/example-b.go rename to examples/lib/tour/03-working-with-credentials/02-basic-credential-management.go index 96efde1dd3..4cd8527215 100644 --- a/examples/lib/tour/03-working-with-credentials/example-b.go +++ b/examples/lib/tour/03-working-with-credentials/02-basic-credential-management.go @@ -10,7 +10,6 @@ import ( "os" "github.com/open-component-model/ocm/examples/lib/helper" - "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/contexts/credentials" ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity" "github.com/open-component-model/ocm/pkg/contexts/oci" @@ -19,19 +18,6 @@ import ( "github.com/open-component-model/ocm/pkg/errors" ) -func obfuscate(props common.Properties) string { - if pw, ok := props[credentials.ATTR_PASSWORD]; ok { - if len(pw) > 5 { - pw = pw[:5] + "***" - } else { - pw = "***" - } - props = props.Copy() - props[credentials.ATTR_PASSWORD] = pw - } - return props.String() -} - func UsingCredentialsB(cfg *helper.Config, create bool) error { ctx := ocm.DefaultContext() @@ -137,6 +123,11 @@ func UsingCredentialsB(cfg *helper.Config, create bool) error { if err != nil { return errors.Wrapf(err, "no credentials") } + // an error is only provided if something went wrong while determining + // the credentials. Delivering NO credentials is a valid result. + if creds == nil { + return fmt.Errorf("no credentials found") + } fmt.Printf("credentials: %s\n", obfuscate(creds.Properties())) // Now we can continue with our basic component version composition diff --git a/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go b/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go new file mode 100644 index 0000000000..bd85ffa486 --- /dev/null +++ b/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + + "github.com/open-component-model/ocm/examples/lib/helper" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/dockerconfig" + "github.com/open-component-model/ocm/pkg/contexts/oci" + "github.com/open-component-model/ocm/pkg/contexts/ocm" + "github.com/open-component-model/ocm/pkg/errors" +) + +func UsingCredentialsRepositories(cfg *helper.Config) error { + ctx := ocm.DefaultContext() + credctx := ctx.CredentialsContext() + + // The OCM toolset embraces multiple storage + // backend technologies, for OCM meta data as well + // as for artifacts described by a component version. + // All those technologies typically have their own + // way to configure crednetials for command line + // tools or servers. + // + // The credential management provides so-called + // credential repositories. Such a repository + // is able to provide any number of names + // credential sets. This way any special + // credential store can be connected to the + // OCM credential management jsu by providing + // an own implementation for the repository interface. + + // One such case is the docker config json, a config + // file used by docker login to store + // credentials for dedicatd OCI regsitries. + dspec := dockerconfig.NewRepositorySpec("~/.docker/config.json") + + // There are general credential stores, like a HashiCorp Vault + // or type-specific ones, like the docker config json + // used to configure credentials for the docker client. + // (working with OCI registries). + // Those specialized repository implementation are not only able to + // provide credential sets, they also know about the usage context. + // Such repository implementations are able to provide credential + // mappings for consumer ids, also. + + // The docker config is such a case, so we can instruct the + // repository to automatically propagate appropriate the consumer id + // mappings. + dspec = dspec.WithConsumerPropagation(true) + + // now we can jsut add the repository for this specification to + // the credential context. + _, err := credctx.RepositoryForSpec(dspec) + if err != nil { + return errors.Wrapf(err, "invalid credential repository") + } + // we are not interested in the repository object, so we just ignore + // the result. + + // so, if you have done the appropriate docker login for your + // OCI registry, it should be possible now to get the credentials + // for the configured repository. + id, err := oci.GetConsumerIdForRef(cfg.Repository) + if err != nil { + return errors.Wrapf(err, "invalid consumer") + } + + // the returned credentials are provided via an interface, which might change its + // content, if the underlying credential source changes. + creds, err := credentials.CredentialsForConsumer(credctx, id, ociidentity.IdentityMatcher) + if err != nil { + return errors.Wrapf(err, "no credentials") + } + // an error is only provided if something went wrong while determining + // the credentials. Delivering NO credentials is a valid result. + if creds == nil { + return fmt.Errorf("no credentials found") + } + fmt.Printf("credentials: %s\n", obfuscate(creds.Properties())) + + return nil +} diff --git a/examples/lib/tour/03-working-with-credentials/README.md b/examples/lib/tour/03-working-with-credentials/README.md new file mode 100644 index 0000000000..44398bc239 --- /dev/null +++ b/examples/lib/tour/03-working-with-credentials/README.md @@ -0,0 +1,21 @@ +# Working with Credentials + +This tour illustrates the basic handling of credentials +using the OCM library. The library provides +an extensible framework to bring together credential providers +and credential cosunmers in a technology-agnostic way. + +It covers four basic scenarios: +- [`basic`](01-using-credentials.go) Writing to a repository with directly specified credentials. +- [`generic`](02-basic-credential-management.go) Using credentials via the credential management. +- [`read`](02-basic-credential-management.go) Read the previously component version using the crednetial management. +- [`credrepo`](03-credential-repositories.go) Providing credentials via credential repositories. + +You can just call the main program with some config file option (`--config `) and the name of the scenario. +The config file should have the following content: + +```yaml +repository: ghcr.io/mandelsoft/ocm +username: +password: +``` \ No newline at end of file diff --git a/examples/lib/tour/03-working-with-credentials/common.go b/examples/lib/tour/03-working-with-credentials/common.go index d5efc5f78c..4846450c8c 100644 --- a/examples/lib/tour/03-working-with-credentials/common.go +++ b/examples/lib/tour/03-working-with-credentials/common.go @@ -10,6 +10,7 @@ import ( "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" + "github.com/open-component-model/ocm/pkg/contexts/credentials" "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartifact" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" @@ -278,3 +279,16 @@ func listVersions(repo ocm.Repository, list ...string) error { } return nil } + +func obfuscate(props common.Properties) string { + if pw, ok := props[credentials.ATTR_PASSWORD]; ok { + if len(pw) > 5 { + pw = pw[:5] + "***" + } else { + pw = "***" + } + props = props.Copy() + props[credentials.ATTR_PASSWORD] = pw + } + return props.String() +} diff --git a/examples/lib/tour/03-working-with-credentials/main.go b/examples/lib/tour/03-working-with-credentials/main.go index 53b9465fbf..7abd06c7e0 100644 --- a/examples/lib/tour/03-working-with-credentials/main.go +++ b/examples/lib/tour/03-working-with-credentials/main.go @@ -52,6 +52,8 @@ func main() { err = UsingCredentialsB(cfg, true) case "read": err = UsingCredentialsB(cfg, false) + case "credrepo": + err = UsingCredentialsRepositories(cfg) default: err = fmt.Errorf("unknown example %q", cmd) } diff --git a/examples/lib/tour/04-working-with-config/01-basic-config-management.go b/examples/lib/tour/04-working-with-config/01-basic-config-management.go new file mode 100644 index 0000000000..bea3a74d7c --- /dev/null +++ b/examples/lib/tour/04-working-with-config/01-basic-config-management.go @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "encoding/json" + "fmt" + + "github.com/go-test/deep" + "github.com/open-component-model/ocm/examples/lib/helper" + "github.com/open-component-model/ocm/pkg/contexts/config" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + credcfg "github.com/open-component-model/ocm/pkg/contexts/credentials/config" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/directcreds" + "github.com/open-component-model/ocm/pkg/contexts/oci" + "github.com/open-component-model/ocm/pkg/errors" +) + +func BasicConfigurationHandling(cfg *helper.Config) error { + // configuration is handled by the configuration context. + ctx := config.DefaultContext() + + // the configuration context handles configuration objects. + // a configuration object is any object implementing + // the config.Config interface. + + // one such object is the configuration object for + // credentials. + + creds := credcfg.New() + + // here we can configure credential settings: + // credential repositories and consumer is mappings. + id, err := oci.GetConsumerIdForRef(cfg.Repository) + if err != nil { + return errors.Wrapf(err, "invalid consumer") + } + creds.AddConsumer( + id, + directcreds.NewRepositorySpec(cfg.GetCredentials().Properties()), + ) + + // credential objects are typically serializable and deserializable. + + spec, err := json.MarshalIndent(creds, " ", " ") + if err != nil { + return errors.Wrapf(err, "marshal credential config") + } + + fmt.Printf("this a a credential configuration object:\n%s\n", string(spec)) + + // like all the other maifest based description this format always includes + // a type field, which can be used to deserialize a specification into + // the appropriate object. + // This can ebe done by the config context. It accepts YAML or JSON. + + o, err := ctx.GetConfigForData(spec, nil) + if err != nil { + return errors.Wrapf(err, "deserialize config") + } + + if diff := deep.Equal(o, creds); len(diff) != 0 { + fmt.Printf("diff:\n%v\n", diff) + return fmt.Errorf("invalid des/erialization") + } + + // regardless what variant is used (direct object or descriptor) + // the config object can be added to a config context. + err = ctx.ApplyConfig(creds, "explicit cred setting") + if err != nil { + return errors.Wrapf(err, "cannot apply config") + } + + // Every config object implements the + // ApplyTo(ctx config.Context, target interface{}) error method. + // It takes an object, which wants to be configured. + // The config object then decides, whether it provides + // settings for the given object and calls the appropriate + // methods on this object (after a type cast). + // + // This way the config mechanism reverts the configuration + // request, it does not actively configure something, instead + // an object, which wants to be configured calls the config + // context to apply pending configs. + // The config context manages a queue of config objects + // and applys them to an object to be configured. + + // If ask he credential context now for credentials, + // it asks the config context for pending config objects + // and apply them. + // Theregore, we now should the configured creentials, here. + + credctx := credentials.DefaultContext() + + found, err := credentials.CredentialsForConsumer(credctx, id) + if err != nil { + return errors.Wrapf(err, "cannot get credentials") + } + // an error is only provided if something went wrong while determining + // the credentials. Delivering NO credentials is a valid result. + if found == nil { + return fmt.Errorf("no credentials found") + } + fmt.Printf("consumer id: %s\n", id) + fmt.Printf("credentials: %s\n", obfuscate(found)) + + if found.GetProperty(credentials.ATTR_USERNAME) != cfg.Username { + return fmt.Errorf("password mismatch") + } + if found.GetProperty(credentials.ATTR_PASSWORD) != cfg.Password { + return fmt.Errorf("password mismatch") + } + return nil +} diff --git a/examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go b/examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go new file mode 100644 index 0000000000..e130d31f5b --- /dev/null +++ b/examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "encoding/json" + "fmt" + + "github.com/open-component-model/ocm/examples/lib/helper" + "github.com/open-component-model/ocm/pkg/contexts/config" + configcfg "github.com/open-component-model/ocm/pkg/contexts/config/config" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + credcfg "github.com/open-component-model/ocm/pkg/contexts/credentials/config" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/directcreds" + "github.com/open-component-model/ocm/pkg/contexts/oci" + "github.com/open-component-model/ocm/pkg/errors" +) + +func credConfig(cfg *helper.Config) (config.Config, error) { + creds := credcfg.New() + + // here we can configure credential settings: + // credential repositories and consumer is mappings. + id, err := oci.GetConsumerIdForRef(cfg.Repository) + if err != nil { + return nil, errors.Wrapf(err, "invalid consumer") + } + creds.AddConsumer( + id, + directcreds.NewRepositorySpec(cfg.GetCredentials().Properties()), + ) + return creds, nil +} + +func HandleArbitraryConfiguration(cfg *helper.Config) error { + // The configuration management provides a configuration object + // for it own. + + generic := configcfg.New() + + // the generic config holds a list of config objects, + // or their specification formats. + // Additionally, it is possible to configure names sets + // of configurations, which can later be enabled + // on-demand at the config context. + + // we recycle our credential config from the last example. + creds, err := credConfig(cfg) + if err != nil { + return err + } + err = generic.AddConfig(creds) + if err != nil { + return errors.Wrapf(err, "adding config") + } + + // credential objects are typically serializable and deserializable. + // this also holds for the generic config object of the config context. + + spec, err := json.MarshalIndent(generic, " ", " ") + if err != nil { + return errors.Wrapf(err, "marshal credential config") + } + + // the result is a config object hosting a list (with 1 entry) + // of other config object specifications. + fmt.Printf("this a a generic configuration object:\n%s\n", string(spec)) + + // the generic config object can be added to a config context, again. + ctx := config.DefaultContext() + err = ctx.ApplyConfig(creds, "generic setting") + if err != nil { + return errors.Wrapf(err, "cannot apply config") + } + credctx := credentials.DefaultContext() + + // query now works, also. + id, err := oci.GetConsumerIdForRef(cfg.Repository) + if err != nil { + return errors.Wrapf(err, "invalid consumer") + } + found, err := credentials.CredentialsForConsumer(credctx, id) + if err != nil { + return errors.Wrapf(err, "cannot get credentials") + } + fmt.Printf("consumer id: %s\n", id) + fmt.Printf("credentials: %s\n", obfuscate(found)) + return nil +} diff --git a/examples/lib/tour/04-working-with-config/03-using-ocm-config.go b/examples/lib/tour/04-working-with-config/03-using-ocm-config.go new file mode 100644 index 0000000000..ab6e2e7435 --- /dev/null +++ b/examples/lib/tour/04-working-with-config/03-using-ocm-config.go @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + + "github.com/open-component-model/ocm/examples/lib/helper" + configcfg "github.com/open-component-model/ocm/pkg/contexts/config/config" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + credcfg "github.com/open-component-model/ocm/pkg/contexts/credentials/config" + "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/dockerconfig" + "github.com/open-component-model/ocm/pkg/contexts/oci" + "github.com/open-component-model/ocm/pkg/contexts/ocm" + "github.com/open-component-model/ocm/pkg/contexts/ocm/utils" + "github.com/open-component-model/ocm/pkg/errors" + "sigs.k8s.io/yaml" +) + +func HandleOCMConfig(cfg *helper.Config) error { + + // Although the configuration of an OCM context can + // be done by a sequence of explicit calls the mechanism + // shown in the example before, is used to provide a simple + // library function, which can be used to configure an OCM + // context and all related other contexts with a single call + // based on a central configuration file (~/.ocmconfig) + ctx := ocm.DefaultContext() + _, err := utils.Configure(ctx, "") + if err != nil { + return errors.Wrapf(err, "configuration") + } + + // It is typically such a generic configuration specification, + // enriched with specialized config specifications for + // crednetials, default repositories signing keys and any + // other configuration specification. + // Most important are here the crednetials. + // Because OCM embraces lots of storage technologies + // for artifact storage as well as storing OCM meta data, + // tzere are typically multiple technology specific ways + // to configure credentials for command line tools. + // Using the crednetials settings shown in the previous examples, + // it ius possible to specify crednetials for all + // required purposes, but the configuration mangement provides + // an extensible way to embed native technology specific ways + // to provide crednetials just by adding an appropriate type + // of config objects, which reads the specialized stoarge and + // feeds it into the crednetial context. + // + // One such config object type is the docker config type. It + // reads a dockerconfig.json file and fed in the crednetials. + // because it is sed for a dedicated purpose (crednetials for + // OCI registires), it not only can feed the crednetials, but + // also their mapping to consumer ids. + + // create the specification for a new credential repository of + // type dockerconfig. + credspec := dockerconfig.NewRepositorySpec("~/.docker/config.json", true) + + // add this repository specification to a credential configuration. + ccfg := credcfg.New() + err = ccfg.AddRepository(credspec) + if err != nil { + return errors.Wrapf(err, "invalid credential config") + } + + // By adding the default location for the standard docker config + // file, all credentials provided by the docker login + // are available in the OCM toolset, also. + + // A typical minimal .ocmconfig file can be composed as follows. + + ocmcfg := configcfg.New() + err = ocmcfg.AddConfig(ccfg) + + spec, err := yaml.Marshal(ocmcfg) + if err != nil { + return errors.Wrapf(err, "marshal ocm config") + } + + // the result is a typical minimal ocm configuration file + // just providing the credentials configured with + // doicker login. + fmt.Printf("this a typical ocm config file:\n%s\n", string(spec)) + + // Besides from a file, such a config can be provided as data, also, + // taken from any other source, for example from a Kubernetes secret + + err = utils.ConfigureByData(ctx, spec, "from data") + if err != nil { + return errors.Wrapf(err, "configuration") + } + + // If you have provided your OCI credentials with + // docker login, they should now be available. + + id, err := oci.GetConsumerIdForRef(cfg.Repository) + if err != nil { + return errors.Wrapf(err, "invalid consumer") + } + found, err := credentials.CredentialsForConsumer(ctx, id) + if err != nil { + return errors.Wrapf(err, "cannot get credentials") + } + fmt.Printf("consumer id: %s\n", id) + fmt.Printf("credentials: %s\n", obfuscate(found)) + return nil +} diff --git a/examples/lib/tour/04-working-with-config/04-write-config-type.go b/examples/lib/tour/04-working-with-config/04-write-config-type.go new file mode 100644 index 0000000000..93282c0556 --- /dev/null +++ b/examples/lib/tour/04-working-with-config/04-write-config-type.go @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + + "github.com/open-component-model/ocm/examples/lib/helper" + configcfg "github.com/open-component-model/ocm/pkg/contexts/config/config" + "github.com/open-component-model/ocm/pkg/contexts/config/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity" + "github.com/open-component-model/ocm/pkg/contexts/oci" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/runtime" + "sigs.k8s.io/yaml" +) + +// TYPE is the name of our new configuration object type. +// To be globally unique, it should always end with a +// DNS domain owned by the provider of the new type. +const TYPE = "example.config.acme.org" + +// ExampleConfigSpec is a new type of config specification +// covering our example configuration. +type ExampleConfigSpec struct { + // ObjectVersionedType is the base type providing the type feature + // form config specifications. + runtime.ObjectVersionedType `json:",inline"` + // Config is our example config representation. + helper.Config `json:",inline"` +} + +// NewConfig provides a config object for out helper configuration. +func NewConfig(cfg *helper.Config) cpi.Config { + return &ExampleConfigSpec{ + ObjectVersionedType: runtime.NewVersionedTypedObject(TYPE), + Config: *cfg, + } +} + +// RepositoryTarget consumes a repository name. +type RepositoryTarget interface { + SetRepository(r string) +} + +// ApplyTo is used to apply the provided configuration settings +// to a dedicated object, which wants to be configured. +func (c *ExampleConfigSpec) ApplyTo(_ cpi.Context, tgt interface{}) error { + + switch t := tgt.(type) { + // if the target is a credentials context + // configure the credentials to be used for the + // described OCI repository. + case credentials.Context: + // determine the consumer id for our target repository- + id, err := oci.GetConsumerIdForRef(c.Repository) + if err != nil { + return errors.Wrapf(err, "invalid consumer") + } + // create the crednetials. + creds := c.GetCredentials() + + // configure the targeted credential context with + // the provided credentials (see previous examples). + t.SetCredentialsForConsumer(id, creds) + + // if the target consumes an OCI repository, propagate + // the provided OCI repository ref. + case RepositoryTarget: + t.SetRepository(c.Repository) + + // all other targets are ignored, we don't have + // something to set at these objects. + default: + return cpi.ErrNoContext(TYPE) + } + return nil +} + +func init() { + // register the new config type, so that is can be used + // by the config management to deserialize appropriately + // typed specifications. + cpi.RegisterConfigType(cpi.NewConfigType[*ExampleConfigSpec](TYPE, "this ia config object type based on the example config data.")) +} + +func WriteConfigType(cfg *helper.Config) error { + + // after preparing aout new special config type + // we can feed it into the config management. + + credctx := credentials.DefaultContext() + + // the credential context is based on a config context + // used to configure it. + ctx := credctx.ConfigContext() + + // create our new config based on the actual settings + // and apply it to the config context. + examplecfg := NewConfig(cfg) + ctx.ApplyConfig(examplecfg, "special acme config") + // If you omit the above call, no credentials + // will be found later. + // _, _ = ctx, examplecfg + + // now we should be prepared to get the credentials + id, err := oci.GetConsumerIdForRef(cfg.Repository) + if err != nil { + return errors.Wrapf(err, "cannot get consumer id") + } + fmt.Printf("usage context: %s\n", id) + + // the returned credentials are provided via an interface, which might change its + // content, if the underlying credential source changes. + creds, err := credentials.CredentialsForConsumer(credctx, id, ociidentity.IdentityMatcher) + if err != nil { + return errors.Wrapf(err, "credentials") + } + fmt.Printf("credentials: %s\n", obfuscate(creds)) + + // Because of the new credential type, such a specification can + // now be added to the ocm config, also. + // So, we could use our special tour config file content + // directly as part of the ocm config. + + ocmcfg := configcfg.New() + err = ocmcfg.AddConfig(examplecfg) + + spec, err := yaml.Marshal(ocmcfg) + if err != nil { + return errors.Wrapf(err, "marshal ocm config") + } + + // the result is a minimal ocm configuration file + // just providing our new example configuration. + fmt.Printf("this a typical ocm config file:\n%s\n", string(spec)) + + // above, we added a new kind of target, the RepositoryTarget interface. + // Just by providing an implementation for this interface, we can + // configure such an object using the config management. + target := &SimpleRepositoryTarget{} + + _, err = ctx.ApplyTo(0, target) + if err != nil { + return errors.Wrapf(err, "applying to new target") + } + fmt.Printf("repository for target: %s\n", target.repository) + + // This way any specialized configuration object can be added + // by a user of the OCM library. It can be used to configure + // existing objects or even new object types, even in combination. + // + // What is still required is a way + // to implement new config targets, objects, which want + // to be configured and which autoconfigure themselves when + // used. Our simple repository target is just an example + // for some kind of ad-hoc configuration. + // This is shown in the next example. + return nil +} + +type SimpleRepositoryTarget struct { + repository string +} + +var _ RepositoryTarget = (*SimpleRepositoryTarget)(nil) + +func (t *SimpleRepositoryTarget) SetRepository(repo string) { + t.repository = repo +} diff --git a/examples/lib/tour/04-working-with-config/05-write-config-consumer.go b/examples/lib/tour/04-working-with-config/05-write-config-consumer.go new file mode 100644 index 0000000000..af95a8d29f --- /dev/null +++ b/examples/lib/tour/04-working-with-config/05-write-config-consumer.go @@ -0,0 +1,125 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "sync" + + "github.com/open-component-model/ocm/examples/lib/helper" + "github.com/open-component-model/ocm/pkg/contexts/config/cpi" + "github.com/open-component-model/ocm/pkg/contexts/credentials" + ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity" + "github.com/open-component-model/ocm/pkg/contexts/oci" + "github.com/open-component-model/ocm/pkg/errors" +) + +// we already have our new acme.org config object type, +// now we want to provide an object, which configures +// itself when used. + +// RepositoryProvider should be an object, which is +// able to provide an OCI repository reference. +// It has a setter and a getter (the setter is +// provided by our ad-hoc SimpleRepositoryTarget). +type RepositoryProvider struct { + lock sync.Mutex + // updater is a utility, which ia able to + // configure an object basesd a a managed configuration + // watermark. It remembers which config objects from the + // config queue are already applies, and replays + // the config objects applied to the config context + // after the last update. + updater cpi.Updater + SimpleRepositoryTarget +} + +func NewRepositoryProvider(ctx cpi.ContextProvider) *RepositoryProvider { + p := &RepositoryProvider{} + // To do its work, the updater needs a connection to + // the config context to use and the object, which should be + // configured. + p.updater = cpi.NewUpdater(ctx.ConfigContext(), p) + return p +} + +// GetRepository returns a repository ref. +func (p *RepositoryProvider) GetRepository() (string, error) { + p.lock.Lock() + defer p.lock.Unlock() + + // the first step for methods of configurable objects + // dependent on potential configuration is always + // to update itself using the embedded updater. + // Please remember, the config management reverses the + // request direction. Applying a config object to + // the config context does not configure dependent objects, + // it just manages a config queue, which is used by potential + // configuration targets to configure themselves. + // The reason for this is to avoid references from the + // management to managed objects. This would prohibit + // the garbage collection of all configurable objects. + err := p.updater.Update() + if err != nil { + return "", err + } + // now, we can do our regular function, aka + // providing a repository ref. + return p.repository, nil +} + +func WriteConfigTargets(cfg *helper.Config) error { + credctx := credentials.DefaultContext() + + // after defining or repository provider type + // we can now use it. + prov := NewRepositoryProvider(credctx) + + // If we ask now for a repository we will get the empty + // answer. + repo, err := prov.GetRepository() + if err != nil { + errors.Wrapf(err, "get repo") + } + if repo != "" { + return fmt.Errorf("Oops, found repository %q", repo) + } + + // Now, we apply our config from the last example. + ctx := credctx.ConfigContext() + examplecfg := NewConfig(cfg) + err = ctx.ApplyConfig(examplecfg, "special acme config") + if err != nil { + errors.Wrapf(err, "apply config") + } + + // asking for a repository now will return the configured + // ref. + repo, err = prov.GetRepository() + if err != nil { + errors.Wrapf(err, "get repo") + } + if repo == "" { + return fmt.Errorf("no repository provided") + } + fmt.Printf("using repository: %s\n", repo) + + // now, we should also be prepared to get the credentials, + // our config object configures the provider as well as + // the credential context. + id, err := oci.GetConsumerIdForRef(repo) + if err != nil { + return errors.Wrapf(err, "cannot get consumer id") + } + fmt.Printf("usage context: %s\n", id) + + creds, err := credentials.CredentialsForConsumer(credctx, id, ociidentity.IdentityMatcher) + if err != nil { + return errors.Wrapf(err, "credentials") + } + fmt.Printf("credentials: %s\n", obfuscate(creds)) + + return nil +} diff --git a/examples/lib/tour/04-working-with-config/README.md b/examples/lib/tour/04-working-with-config/README.md new file mode 100644 index 0000000000..48397d7179 --- /dev/null +++ b/examples/lib/tour/04-working-with-config/README.md @@ -0,0 +1,23 @@ +# Working with Configurations + +This tour illustrates the basic configuration management +included in the OCM library. The library provides +an extensible framework to bring together configuration settings +and configurable objects. + +It covers five basic scenarios: +- [`basic`](01-basic-config-management.go) Basic configuration management illustarting the configuration of credentials. +- [`generic`](02-handle-arbitrary-config.go) Handling of arbitrary configuration. +- [`ocm`](03-using-ocm-config.go) Central configuration +- [`provide`](04-write-config-type.go) Providing new config object types +- [`consume`](05-write-config-consumer.go) Preparing objects to be configured by the config management + + +You can just call the main program with some config file option (`--config `) and the name of the scenario. +The config file should have the following content: + +```yaml +repository: ghcr.io/mandelsoft/ocm +username: +password: +``` \ No newline at end of file diff --git a/examples/lib/tour/04-working-with-config/common.go b/examples/lib/tour/04-working-with-config/common.go new file mode 100644 index 0000000000..4957898867 --- /dev/null +++ b/examples/lib/tour/04-working-with-config/common.go @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/open-component-model/ocm/pkg/contexts/credentials" +) + +func obfuscate(creds credentials.Credentials) string { + if creds == nil { + return "no credentials" + } + props := creds.Properties() + if pw, ok := props[credentials.ATTR_PASSWORD]; ok { + if len(pw) > 5 { + pw = pw[:5] + "***" + } else { + pw = "***" + } + props = props.Copy() + props[credentials.ATTR_PASSWORD] = pw + } + return props.String() +} diff --git a/examples/lib/tour/04-working-with-config/main.go b/examples/lib/tour/04-working-with-config/main.go new file mode 100644 index 0000000000..e25482bcd5 --- /dev/null +++ b/examples/lib/tour/04-working-with-config/main.go @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors. +// +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/open-component-model/ocm/examples/lib/helper" +) + +// CFG is the path to the file containing the credentials +var CFG = "../examples/lib/cred.yaml" + +var current_version string + +func init() { + data, err := os.ReadFile("VERSION") + if err != nil { + panic("VERSION not found") + } + current_version = strings.TrimSpace(string(data)) +} + +func main() { + arg := 1 + if len(os.Args) > 1 { + if os.Args[1] == "--config" { + if len(os.Args) > 2 { + CFG = os.Args[2] + arg = 3 + } else { + fmt.Fprintf(os.Stderr, "error: config file missing\n") + os.Exit(1) + } + } + } + cfg, err := helper.ReadConfig(CFG) + if err == nil { + cmd := "basic" + + if len(os.Args) > arg { + cmd = os.Args[arg] + } + switch cmd { + case "basic": + err = BasicConfigurationHandling(cfg) + case "generic": + err = HandleArbitraryConfiguration(cfg) + case "ocm": + err = HandleOCMConfig(cfg) + case "provide": + err = WriteConfigType(cfg) + case "consume": + err = WriteConfigTargets(cfg) + default: + err = fmt.Errorf("unknown example %q", cmd) + } + } + + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", err) + os.Exit(1) + } +} diff --git a/examples/lib/tour/04-working-with-config/resources/ocmconfig b/examples/lib/tour/04-working-with-config/resources/ocmconfig new file mode 100644 index 0000000000..6a663d533c --- /dev/null +++ b/examples/lib/tour/04-working-with-config/resources/ocmconfig @@ -0,0 +1,8 @@ +type: generic.config.ocm.software +configurations: +- type: credentials.config.ocm.software + repositories: + - repository: + type: DockerConfig + dockerConfigFile: ~/.docker/config.json + propagateConsumerIdentity: true diff --git a/examples/lib/tour/05-transporting-component-versions/README.md b/examples/lib/tour/05-transporting-component-versions/README.md new file mode 100644 index 0000000000..a7950b3cc5 --- /dev/null +++ b/examples/lib/tour/05-transporting-component-versions/README.md @@ -0,0 +1,35 @@ +# Transporting Component Versions + +This [tour](example.go) illustrates the basic support for +transporting content from one environment into another. + + +You can just call the main program with some config file option (`--config `). +The config file should have the following content: + +```yaml +repository: ghcr.io/mandelsoft/ocm +targetRepository: + type: CommonTransportFormat + filePath: /tmp/example05.target.ctf + fileFormat: directory + accessMode: 2 +username: +password: +``` + +Any supported kind of target repository can be specified by using its +specification type. An OCI regisztry target would look like this: + +```yaml +repository: ghcr.io/mandelsoft/ocm +username: +password: +targetRepository: + type: OCIRegistry + baseUrl: ghcr.io/mandelsoft/targetocm +``` + +The actual version of the example just works with the filesystem +target, because it is not possible to specify credentials for the +target repository. \ No newline at end of file diff --git a/examples/lib/tour/06-signing-component-versions/example-a.go b/examples/lib/tour/06-signing-component-versions/01-basic-signing.go similarity index 100% rename from examples/lib/tour/06-signing-component-versions/example-a.go rename to examples/lib/tour/06-signing-component-versions/01-basic-signing.go diff --git a/examples/lib/tour/06-signing-component-versions/example-b.go b/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go similarity index 98% rename from examples/lib/tour/06-signing-component-versions/example-b.go rename to examples/lib/tour/06-signing-component-versions/02-using-context-settings.go index 107ec85bf1..05cdf32758 100644 --- a/examples/lib/tour/06-signing-component-versions/example-b.go +++ b/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go @@ -54,7 +54,7 @@ func SigningComponentVersionInRepo(cfg *helper.Config) error { return errors.Wrapf(err, "cannot prepare component version in target repo") } - // evrey context features a signing registry, which provides available + // every context features a signing registry, which provides available // signers and hashers, but also keys for various purposes. // It is always asked, if a key is required for a purpose, which is // not explicitly given to a signing/verification call. diff --git a/examples/lib/tour/06-signing-component-versions/README.md b/examples/lib/tour/06-signing-component-versions/README.md new file mode 100644 index 0000000000..21bff204e5 --- /dev/null +++ b/examples/lib/tour/06-signing-component-versions/README.md @@ -0,0 +1,19 @@ +# Signing Component Versions + +This tour illustrates the basic functionality to +sign and verify signatures. + +It covers two basic scenarios: +- [`sign`](01-basic-signing.go) Create, Sign, Transport and Verify a component version. +- [`repo`](02-using-context-settings.go) Using context settings to configure signing and verification in target repo. + +You can just call the main program with some config file option (`--config `) and the name of the scenario. +The config file should have the following content: + +```yaml +targetRepository: + type: CommonTransportFormat + filePath: /tmp/example06.target.ctf + fileFormat: directory + accessMode: 2 +``` \ No newline at end of file diff --git a/examples/lib/tour/06-signing-component-versions/main.go b/examples/lib/tour/06-signing-component-versions/main.go index 905a6921d6..e2b91e6d83 100644 --- a/examples/lib/tour/06-signing-component-versions/main.go +++ b/examples/lib/tour/06-signing-component-versions/main.go @@ -46,7 +46,7 @@ func main() { cmd = os.Args[arg] } switch cmd { - case "basic": + case "sign": err = SigningComponentVersions(cfg) case "repo": err = SigningComponentVersionInRepo(cfg) diff --git a/examples/lib/tour/README.md b/examples/lib/tour/README.md new file mode 100644 index 0000000000..58ac1d1da1 --- /dev/null +++ b/examples/lib/tour/README.md @@ -0,0 +1,15 @@ +# A Tour through Usage Scenarios of the OCM Library + +This tour guides you from a very basic usage of the +OCM repository API to more complex scenarios +handling credentials and configurations. + +So far, it does not cover the implementation +of extension points of the library. + +- [Basic Usage of OCM Repositories](01-getting-started/README.md) +- [Composing Component Versions](02-composing-a-component-version/README.md) +- [Working with Credentials](03-working-with-credentials/README.md) +- [Working with Configuration](04-working-with-config/README.md) +- [Transporting Component Versions](05-transporting-component-versions/README.md) +- [Signing Component Versions](06-signing-component-versions/README.md) \ No newline at end of file diff --git a/pkg/contexts/config/internal/config.go b/pkg/contexts/config/internal/config.go index 9ec143cb66..b65e3bc599 100644 --- a/pkg/contexts/config/internal/config.go +++ b/pkg/contexts/config/internal/config.go @@ -7,6 +7,7 @@ package internal import ( "fmt" + "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/runtime" ) @@ -37,3 +38,17 @@ func (c *ConfigurationList) AddConfig(cfg Config) error { return nil } + +func (c *ConfigurationList) AddConfigData(ctx Context, data []byte) error { + cfg, err := ctx.GetConfigForData(data, nil) + if err != nil { + return errors.Wrapf(err, "invalid config specification") + } + g, err := ToGenericConfig(cfg) + if err != nil { + return fmt.Errorf("unable to convert config to generic: %w", err) + } + + c.Configurations = append(c.Configurations, g) + return nil +} diff --git a/pkg/contexts/credentials/repositories/dockerconfig/type.go b/pkg/contexts/credentials/repositories/dockerconfig/type.go index 30acfd93c9..bba00f371b 100644 --- a/pkg/contexts/credentials/repositories/dockerconfig/type.go +++ b/pkg/contexts/credentials/repositories/dockerconfig/type.go @@ -41,6 +41,9 @@ func NewRepositorySpec(path string, prop ...bool) *RepositorySpec { for _, e := range prop { p = p || e } + if path == "" { + path = "~/.docker/config.json" + } return &RepositorySpec{ ObjectVersionedType: runtime.NewVersionedTypedObject(Type), DockerConfigFile: path, From 121acf21c6b6758fb3fbab99ae111548b73f471f Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 14 Nov 2023 02:36:47 -0800 Subject: [PATCH 15/18] support ocm config file for tour05+06 --- .../03-credential-repositories.go | 2 +- .../03-working-with-credentials/README.md | 2 +- .../03-using-ocm-config.go | 18 ++++++++--------- .../04-write-config-type.go | 2 +- .../README.md | 5 ++++- .../example.go | 20 +++++++++++++++++++ .../01-basic-signing.go | 7 +++++++ .../02-using-context-settings.go | 9 ++++++++- .../06-signing-component-versions/README.md | 9 ++++++++- .../06-signing-component-versions/common.go | 14 +++++++++++++ 10 files changed, 73 insertions(+), 15 deletions(-) diff --git a/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go b/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go index bd85ffa486..26a1c3a66e 100644 --- a/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go +++ b/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go @@ -24,7 +24,7 @@ func UsingCredentialsRepositories(cfg *helper.Config) error { // backend technologies, for OCM meta data as well // as for artifacts described by a component version. // All those technologies typically have their own - // way to configure crednetials for command line + // way to configure credentials for command line // tools or servers. // // The credential management provides so-called diff --git a/examples/lib/tour/03-working-with-credentials/README.md b/examples/lib/tour/03-working-with-credentials/README.md index 44398bc239..56407a1b75 100644 --- a/examples/lib/tour/03-working-with-credentials/README.md +++ b/examples/lib/tour/03-working-with-credentials/README.md @@ -8,7 +8,7 @@ and credential cosunmers in a technology-agnostic way. It covers four basic scenarios: - [`basic`](01-using-credentials.go) Writing to a repository with directly specified credentials. - [`generic`](02-basic-credential-management.go) Using credentials via the credential management. -- [`read`](02-basic-credential-management.go) Read the previously component version using the crednetial management. +- [`read`](02-basic-credential-management.go) Read the previously component version using the credential management. - [`credrepo`](03-credential-repositories.go) Providing credentials via credential repositories. You can just call the main program with some config file option (`--config `) and the name of the scenario. diff --git a/examples/lib/tour/04-working-with-config/03-using-ocm-config.go b/examples/lib/tour/04-working-with-config/03-using-ocm-config.go index ab6e2e7435..f6e4872ee1 100644 --- a/examples/lib/tour/04-working-with-config/03-using-ocm-config.go +++ b/examples/lib/tour/04-working-with-config/03-using-ocm-config.go @@ -35,25 +35,25 @@ func HandleOCMConfig(cfg *helper.Config) error { // It is typically such a generic configuration specification, // enriched with specialized config specifications for - // crednetials, default repositories signing keys and any + // credentials, default repositories signing keys and any // other configuration specification. - // Most important are here the crednetials. + // Most important are here the credentials. // Because OCM embraces lots of storage technologies // for artifact storage as well as storing OCM meta data, // tzere are typically multiple technology specific ways // to configure credentials for command line tools. - // Using the crednetials settings shown in the previous examples, - // it ius possible to specify crednetials for all + // Using the credentials settings shown in the previous examples, + // it ius possible to specify credentials for all // required purposes, but the configuration mangement provides // an extensible way to embed native technology specific ways - // to provide crednetials just by adding an appropriate type + // to provide credentials just by adding an appropriate type // of config objects, which reads the specialized stoarge and - // feeds it into the crednetial context. + // feeds it into the credential context. // // One such config object type is the docker config type. It - // reads a dockerconfig.json file and fed in the crednetials. - // because it is sed for a dedicated purpose (crednetials for - // OCI registires), it not only can feed the crednetials, but + // reads a dockerconfig.json file and fed in the credentials. + // because it is sed for a dedicated purpose (credentials for + // OCI registries), it not only can feed the credentials, but // also their mapping to consumer ids. // create the specification for a new credential repository of diff --git a/examples/lib/tour/04-working-with-config/04-write-config-type.go b/examples/lib/tour/04-working-with-config/04-write-config-type.go index 93282c0556..931dab7e75 100644 --- a/examples/lib/tour/04-working-with-config/04-write-config-type.go +++ b/examples/lib/tour/04-working-with-config/04-write-config-type.go @@ -60,7 +60,7 @@ func (c *ExampleConfigSpec) ApplyTo(_ cpi.Context, tgt interface{}) error { if err != nil { return errors.Wrapf(err, "invalid consumer") } - // create the crednetials. + // create the credentials. creds := c.GetCredentials() // configure the targeted credential context with diff --git a/examples/lib/tour/05-transporting-component-versions/README.md b/examples/lib/tour/05-transporting-component-versions/README.md index a7950b3cc5..a5b494a501 100644 --- a/examples/lib/tour/05-transporting-component-versions/README.md +++ b/examples/lib/tour/05-transporting-component-versions/README.md @@ -28,8 +28,11 @@ password: targetRepository: type: OCIRegistry baseUrl: ghcr.io/mandelsoft/targetocm +ocmConfig: ``` The actual version of the example just works with the filesystem target, because it is not possible to specify credentials for the -target repository. \ No newline at end of file +target repository in this simple config file. But, if you specific an [OCM config file](../04-working-with-config/README.md) you can +add more credential settings to make target repositories possible +requiring credentials. \ No newline at end of file diff --git a/examples/lib/tour/05-transporting-component-versions/example.go b/examples/lib/tour/05-transporting-component-versions/example.go index 9ba4943cf1..e22b66d08e 100644 --- a/examples/lib/tour/05-transporting-component-versions/example.go +++ b/examples/lib/tour/05-transporting-component-versions/example.go @@ -14,12 +14,32 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ocireg" "github.com/open-component-model/ocm/pkg/contexts/ocm/transfer" "github.com/open-component-model/ocm/pkg/contexts/ocm/transfer/transferhandler/standard" + "github.com/open-component-model/ocm/pkg/contexts/ocm/utils" "github.com/open-component-model/ocm/pkg/errors" ) +func ReadConfiguration(ctx ocm.Context, cfg *helper.Config) error { + if cfg.OCMConfig != "" { + fmt.Printf("*** applying config from %s\n", cfg.OCMConfig) + + _, err := utils.Configure(ctx, cfg.OCMConfig) + if err != nil { + return errors.Wrapf(err, "error in ocm config %s", cfg.OCMConfig) + } + } + return nil +} + func TransportingComponentVersions(cfg *helper.Config) error { ctx := ocm.DefaultContext() + // Configure context with optional ocm config. + // See OCM config scenario in tour 04. + err := ReadConfiguration(ctx, cfg) + if err != nil { + return err + } + // the context acts as factory for various model types based on // specification descriptor serialization formats in YAML or JSON. // Access method specifications and repository specification are diff --git a/examples/lib/tour/06-signing-component-versions/01-basic-signing.go b/examples/lib/tour/06-signing-component-versions/01-basic-signing.go index 82aaf4a5dd..0154795279 100644 --- a/examples/lib/tour/06-signing-component-versions/01-basic-signing.go +++ b/examples/lib/tour/06-signing-component-versions/01-basic-signing.go @@ -19,6 +19,13 @@ func SigningComponentVersions(cfg *helper.Config) error { ctx := ocm.DefaultContext() + // Configure context with optional ocm config. + // See OCM config scenario in tour 04. + err := ReadConfiguration(ctx, cfg) + if err != nil { + return err + } + // siginfo := signingattr.Get(ctx) // to sign a component version we need a private key. diff --git a/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go b/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go index 05cdf32758..9656119a5e 100644 --- a/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go +++ b/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go @@ -49,7 +49,14 @@ func prepareComponentInRepo(ctx ocm.Context, cfg *helper.Config) error { func SigningComponentVersionInRepo(cfg *helper.Config) error { ctx := ocm.DefaultContext() - err := prepareComponentInRepo(ctx, cfg) + // Configure context with optional ocm config. + // See OCM config scenario in tour 04. + err := ReadConfiguration(ctx, cfg) + if err != nil { + return err + } + + err = prepareComponentInRepo(ctx, cfg) if err != nil { return errors.Wrapf(err, "cannot prepare component version in target repo") } diff --git a/examples/lib/tour/06-signing-component-versions/README.md b/examples/lib/tour/06-signing-component-versions/README.md index 21bff204e5..0fa356f6ed 100644 --- a/examples/lib/tour/06-signing-component-versions/README.md +++ b/examples/lib/tour/06-signing-component-versions/README.md @@ -16,4 +16,11 @@ targetRepository: filePath: /tmp/example06.target.ctf fileFormat: directory accessMode: 2 -``` \ No newline at end of file +ocmConfig: +``` + +The actual version of the example just works with the filesystem +target, because it is not possible to specify credentials for the +target repository in this simple config file. But, if you specific an [OCM config file](../04-working-with-config/README.md) you can +add more credential settings to make target repositories possible +requiring credentials. diff --git a/examples/lib/tour/06-signing-component-versions/common.go b/examples/lib/tour/06-signing-component-versions/common.go index ee36877893..0bb09b78cf 100644 --- a/examples/lib/tour/06-signing-component-versions/common.go +++ b/examples/lib/tour/06-signing-component-versions/common.go @@ -8,6 +8,7 @@ import ( "fmt" "strings" + "github.com/open-component-model/ocm/examples/lib/helper" "github.com/open-component-model/ocm/pkg/blobaccess" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/contexts/ocm" @@ -18,6 +19,7 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactblob/dockermultiblob" "github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactblob/textblob" "github.com/open-component-model/ocm/pkg/contexts/ocm/resourcetypes" + "github.com/open-component-model/ocm/pkg/contexts/ocm/utils" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/finalizer" "github.com/open-component-model/ocm/pkg/mime" @@ -300,3 +302,15 @@ func listVersions(repo ocm.Repository, list ...string) error { } return nil } + +func ReadConfiguration(ctx ocm.Context, cfg *helper.Config) error { + if cfg.OCMConfig != "" { + fmt.Printf("*** applying config from %s\n", cfg.OCMConfig) + + _, err := utils.Configure(ctx, cfg.OCMConfig) + if err != nil { + return errors.Wrapf(err, "error in ocm config %s", cfg.OCMConfig) + } + } + return nil +} From c0d5a9d094bfbcd1f5005a73d9a44bad5a8805fb Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 15 Nov 2023 07:11:45 -0800 Subject: [PATCH 16/18] compatible ResourceAccess interface --- .../ocm/accessmethods/ociartifact/method.go | 13 ++++--- pkg/contexts/ocm/cpi/dummy.go | 28 +++++++------- pkg/contexts/ocm/cpi/view_rsc.go | 26 +++++++------ .../genericaccess/resource_test.go | 2 +- .../artifactblob/externalblob/resource.go | 37 +++++++++---------- pkg/contexts/ocm/internal/repository.go | 1 + 6 files changed, 56 insertions(+), 51 deletions(-) diff --git a/pkg/contexts/ocm/accessmethods/ociartifact/method.go b/pkg/contexts/ocm/accessmethods/ociartifact/method.go index d669c4a034..5e937f7cf3 100644 --- a/pkg/contexts/ocm/accessmethods/ociartifact/method.go +++ b/pkg/contexts/ocm/accessmethods/ociartifact/method.go @@ -100,12 +100,15 @@ func (a *AccessSpec) GetReferenceHint(cv accspeccpi.ComponentVersionAccess) stri if err != nil { return "" } - prefix := ocmcpi.RepositoryPrefix(cv.Repository().GetSpecification()) hint := ref.Repository - if strings.HasPrefix(hint, prefix+grammar.RepositorySeparator) { - // try to keep hint identical, even across intermediate - // artifact globalizations - hint = hint[len(prefix)+1:] + r := cv.Repository() + if r != nil { + prefix := ocmcpi.RepositoryPrefix(cv.Repository().GetSpecification()) + if strings.HasPrefix(hint, prefix+grammar.RepositorySeparator) { + // try to keep hint identical, even across intermediate + // artifact globalizations + hint = hint[len(prefix)+1:] + } } if ref.Tag != nil { hint += grammar.TagSeparator + *ref.Tag diff --git a/pkg/contexts/ocm/cpi/dummy.go b/pkg/contexts/ocm/cpi/dummy.go index 8282a5fbce..099bf84ab4 100644 --- a/pkg/contexts/ocm/cpi/dummy.go +++ b/pkg/contexts/ocm/cpi/dummy.go @@ -36,39 +36,39 @@ func (d *DummyComponentVersionAccess) Dup() (ComponentVersionAccess, error) { } func (d *DummyComponentVersionAccess) GetProvider() *compdesc.Provider { - panic("implement me") + return nil } func (d *DummyComponentVersionAccess) SetProvider(p *compdesc.Provider) error { - panic("implement me") + return errors.ErrNotSupported() } func (d *DummyComponentVersionAccess) AdjustSourceAccess(meta *internal.SourceMeta, acc compdesc.AccessSpec) error { - panic("implement me") + return errors.ErrNotSupported() } func (c *DummyComponentVersionAccess) Repository() Repository { - panic("implement me") + return nil } func (d *DummyComponentVersionAccess) GetName() string { - panic("implement me") + return "" } func (d *DummyComponentVersionAccess) GetVersion() string { - panic("implement me") + return "" } func (d *DummyComponentVersionAccess) GetDescriptor() *compdesc.ComponentDescriptor { - panic("implement me") + return nil } func (d *DummyComponentVersionAccess) GetResources() []ResourceAccess { return nil } -func (d *DummyComponentVersionAccess) GetResource(meta metav1.Identity) (ResourceAccess, error) { - return nil, errors.ErrNotFound("resource", meta.String()) +func (d *DummyComponentVersionAccess) GetResource(id metav1.Identity) (ResourceAccess, error) { + return nil, errors.ErrNotFound("resource", id.String()) } func (d *DummyComponentVersionAccess) GetResourceIndex(metav1.Identity) int { @@ -84,11 +84,11 @@ func (d *DummyComponentVersionAccess) GetResourcesByName(name string, selectors } func (d *DummyComponentVersionAccess) GetSources() []SourceAccess { - panic("implement me") + return nil } -func (d *DummyComponentVersionAccess) GetSource(meta metav1.Identity) (SourceAccess, error) { - panic("implement me") +func (d *DummyComponentVersionAccess) GetSource(id metav1.Identity) (SourceAccess, error) { + return nil, errors.ErrNotFound(KIND_SOURCE, id.String()) } func (d *DummyComponentVersionAccess) GetSourceIndex(metav1.Identity) int { @@ -158,11 +158,11 @@ func (d *DummyComponentVersionAccess) SetSource(meta *SourceMeta, spec compdesc. } func (d *DummyComponentVersionAccess) SetSourceByAccess(art SourceAccess) error { - panic("implement me") + return errors.ErrNotSupported() } func (d *DummyComponentVersionAccess) SetReference(ref *ComponentReference) error { - panic("implement me") + return errors.ErrNotSupported() } func (d *DummyComponentVersionAccess) DiscardChanges() { diff --git a/pkg/contexts/ocm/cpi/view_rsc.go b/pkg/contexts/ocm/cpi/view_rsc.go index 4b8868dafb..a88c2a5f40 100644 --- a/pkg/contexts/ocm/cpi/view_rsc.go +++ b/pkg/contexts/ocm/cpi/view_rsc.go @@ -158,6 +158,9 @@ func (b *accessAccessProvider) GetOCMContext() cpi.Context { } func (b *accessAccessProvider) ReferenceHint() string { + if h, ok := b.spec.(HintProvider); ok { + return h.GetReferenceHint(&DummyComponentVersionAccess{b.ctx}) + } return "" } @@ -180,32 +183,24 @@ func (b *accessAccessProvider) BlobAccess() (blobaccess.BlobAccess, error) { //////////////////////////////////////////////////////////////////////////////// type ( - accessProvider = AccessProvider - componentVersionProvider = ComponentVersionProvider + accessProvider = AccessProvider ) type artifactAccessProvider[M any] struct { accessProvider - meta *M + componentVersionProvider ComponentVersionProvider + meta *M } var _ credentials.ConsumerIdentityProvider = (*artifactAccessProvider[any])(nil) -type artifactCVAccessProvider[M any] struct { - artifactAccessProvider[M] - componentVersionProvider -} - func NewArtifactAccessForProvider[M any](meta *M, prov AccessProvider) cpi.ArtifactAccess[M] { aa := &artifactAccessProvider[M]{ accessProvider: prov, meta: meta, } if p, ok := prov.(ComponentVersionProvider); ok { - return &artifactCVAccessProvider[M]{ - artifactAccessProvider: *aa, - componentVersionProvider: p, - } + aa.componentVersionProvider = p } return aa } @@ -232,6 +227,13 @@ func (b *artifactAccessProvider[M]) GetIdentityMatcher() string { return credentials.GetProvidedIdentityMatcher(m) } +func (b *artifactAccessProvider[M]) GetComponentVersion() (ComponentVersionAccess, error) { + if b.componentVersionProvider != nil { + return b.componentVersionProvider.GetComponentVersion() + } + return nil, nil +} + //////////////////////////////////////////////////////////////////////////////// var _ ResourceAccess = (*artifactAccessProvider[ResourceMeta])(nil) diff --git a/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go b/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go index 963bbb27bf..34b7f4bb46 100644 --- a/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go +++ b/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go @@ -46,7 +46,7 @@ var _ = Describe("dir tree resource access", func() { acc := Must(me.ResourceAccess(env.OCMContext(), compdesc.NewResourceMeta("test", resourcetypes.OCI_IMAGE, compdesc.LocalRelation), spec)) - Expect(acc.ReferenceHint()).To(Equal("")) + Expect(acc.ReferenceHint()).To(Equal(OCINAMESPACE + ":" + OCIVERSION)) Expect(acc.GlobalAccess()).To(BeNil()) Expect(acc.Meta().Type).To(Equal(resourcetypes.OCI_IMAGE)) diff --git a/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go b/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go index cbb3130654..29c039f900 100644 --- a/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go +++ b/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go @@ -8,7 +8,8 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm" "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc" "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi" - "github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactaccess/genericaccess" + "github.com/open-component-model/ocm/pkg/errors" + "github.com/open-component-model/ocm/pkg/generics" "github.com/open-component-model/ocm/pkg/optionutils" ) @@ -24,43 +25,41 @@ func Access[M any, P compdesc.ArtifactMetaPointer[M]](ctx ocm.Context, meta P, a global = ocm.GlobalAccess(access, ctx) } - a, err := genericaccess.Access(ctx, meta, access) + prov, err := cpi.NewAccessProviderForExternalAccessSpec(ctx, access) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "invalid external access method %q", access.GetKind()) } - return newAccessProvider[M](a, hint, global), nil + return cpi.NewArtifactAccessForProvider(generics.As[*M](meta), newAccessProvider(prov, hint, global)), nil } -type accessProvider[M any] struct { - cpi.ArtifactAccess[M] +type _accessProvider = cpi.AccessProvider + +type accessProvider struct { + _accessProvider hint string global cpi.AccessSpec } -func newAccessProvider[M any](prov cpi.ArtifactAccess[M], hint string, global cpi.AccessSpec) cpi.ArtifactAccess[M] { - return &accessProvider[M]{ - ArtifactAccess: prov, - hint: hint, - global: global, +func newAccessProvider(prov cpi.AccessProvider, hint string, global cpi.AccessSpec) cpi.AccessProvider { + return &accessProvider{ + _accessProvider: prov, + hint: hint, + global: global, } } -func (p *accessProvider[M]) AccessSpec() cpi.AccessSpec { - return nil -} - -func (p *accessProvider[M]) ReferenceHint() string { +func (p *accessProvider) ReferenceHint() string { if p.hint != "" { return p.hint } - return p.ArtifactAccess.ReferenceHint() + return p._accessProvider.ReferenceHint() } -func (p *accessProvider[M]) GlobalAccess() cpi.AccessSpec { +func (p *accessProvider) GlobalAccess() cpi.AccessSpec { if p.global != nil { return p.global } - return p.ArtifactAccess.GlobalAccess() + return p._accessProvider.GlobalAccess() } func ResourceAccess(ctx ocm.Context, meta *cpi.ResourceMeta, access cpi.AccessSpec, opts ...Option) (cpi.ResourceAccess, error) { diff --git a/pkg/contexts/ocm/internal/repository.go b/pkg/contexts/ocm/internal/repository.go index 7a4246c1af..e87fc06d04 100644 --- a/pkg/contexts/ocm/internal/repository.go +++ b/pkg/contexts/ocm/internal/repository.go @@ -89,6 +89,7 @@ type AccessProvider interface { type ArtifactAccess[M any] interface { Meta() *M + GetComponentVersion() (ComponentVersionAccess, error) AccessProvider } From 203c9eea74c318435dd4021b5fe0d0f5143b2e9c Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 28 Nov 2023 16:33:34 +0100 Subject: [PATCH 17/18] rename base to proxy --- pkg/contexts/ocm/cpi/repocpi/backend.go | 34 +++--- pkg/contexts/ocm/cpi/repocpi/doc.go | 6 +- .../ocm/cpi/repocpi/{base_c.go => proxy_c.go} | 76 ++++++------ .../cpi/repocpi/{base_cv.go => proxy_cv.go} | 110 ++++++++---------- .../ocm/cpi/repocpi/{base_r.go => proxy_r.go} | 40 +++---- pkg/contexts/ocm/cpi/repocpi/view_c.go | 40 +++---- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 82 ++++++------- pkg/contexts/ocm/cpi/repocpi/view_r.go | 42 +++---- .../repositories/comparch/componentarchive.go | 6 +- .../ocm/repositories/comparch/repository.go | 16 +-- .../repositories/genericocireg/component.go | 6 +- .../genericocireg/componentversion.go | 6 +- .../repositories/genericocireg/repository.go | 4 +- .../ocm/repositories/virtual/component.go | 6 +- .../repositories/virtual/componentversion.go | 6 +- .../ocm/repositories/virtual/repository.go | 4 +- 16 files changed, 240 insertions(+), 244 deletions(-) rename pkg/contexts/ocm/cpi/repocpi/{base_c.go => proxy_c.go} (58%) rename pkg/contexts/ocm/cpi/repocpi/{base_cv.go => proxy_cv.go} (73%) rename pkg/contexts/ocm/cpi/repocpi/{base_r.go => proxy_r.go} (59%) diff --git a/pkg/contexts/ocm/cpi/repocpi/backend.go b/pkg/contexts/ocm/cpi/repocpi/backend.go index f342e9adce..de54b51aac 100644 --- a/pkg/contexts/ocm/cpi/repocpi/backend.go +++ b/pkg/contexts/ocm/cpi/repocpi/backend.go @@ -21,7 +21,7 @@ import ( // and component version objects, which are generically implemented // based on the methods of this interface. // -// A repositotry interface based on this implementation interface can be +// A repository interface based on this implementation interface can be // created using the function NewStorageBackend. type StorageBackendImpl interface { // repository related methods. @@ -52,7 +52,7 @@ type StorageBackendImpl interface { } type storageBackendRepository struct { - base RepositoryBase + proxy RepositoryProxy closed atomic.Bool noref cpi.Repository kind string @@ -78,9 +78,9 @@ func NewStorageBackend(kind string, impl StorageBackendImpl) cpi.Repository { return NewRepository(backend, kind) } -func (s *storageBackendRepository) SetBase(base RepositoryBase) { - s.base = base - s.noref = NewNoneRefRepositoryView(base) +func (s *storageBackendRepository) SetProxy(proxy RepositoryProxy) { + s.proxy = proxy + s.noref = NewNoneRefRepositoryView(proxy) } func (s *storageBackendRepository) Close() error { @@ -124,19 +124,19 @@ func (s *storageBackendRepository) LookupComponent(name string) (*ComponentAcces //////////////////////////////////////////////////////////////////////////////// type storageBackendComponent struct { - base ComponentAccessBase - repo *storageBackendRepository - name string + proxy ComponentAccessProxy + repo *storageBackendRepository + name string } var _ ComponentAccessImpl = (*storageBackendComponent)(nil) -func (s *storageBackendComponent) SetBase(base ComponentAccessBase) { - s.base = base +func (s *storageBackendComponent) SetProxy(proxy ComponentAccessProxy) { + s.proxy = proxy } -func (s *storageBackendComponent) GetParentBase() RepositoryViewManager { - return s.repo.base +func (s *storageBackendComponent) GetParentProxy() RepositoryViewManager { + return s.repo.proxy } func (s *storageBackendComponent) Close() error { @@ -213,7 +213,7 @@ func (s *storageBackendComponent) NewVersion(version string, overwrite ...bool) //////////////////////////////////////////////////////////////////////////////// type storageBackendComponentVersion struct { - base ComponentVersionAccessBase + proxy ComponentVersionAccessProxy comp *storageBackendComponent name common.NameVersion descriptor *compdesc.ComponentDescriptor @@ -229,12 +229,12 @@ func (s *storageBackendComponentVersion) GetContext() cpi.Context { return s.comp.repo.impl.GetContext() } -func (s *storageBackendComponentVersion) SetBase(base ComponentVersionAccessBase) { - s.base = base +func (s *storageBackendComponentVersion) SetProxy(proxy ComponentVersionAccessProxy) { + s.proxy = proxy } -func (s *storageBackendComponentVersion) GetParentBase() ComponentAccessBase { - return s.comp.base +func (s *storageBackendComponentVersion) GetParentProxy() ComponentAccessProxy { + return s.comp.proxy } func (s *storageBackendComponentVersion) Repository() cpi.Repository { diff --git a/pkg/contexts/ocm/cpi/repocpi/doc.go b/pkg/contexts/ocm/cpi/repocpi/doc.go index e72f369079..4b7f92a21f 100644 --- a/pkg/contexts/ocm/cpi/repocpi/doc.go +++ b/pkg/contexts/ocm/cpi/repocpi/doc.go @@ -13,11 +13,15 @@ // in package [github.com/open-component-model/ocm/pkg/contexts/ocm]. // // - on layer 2 (this package) there is a backend agnostic -// implementation od standard functionality based on layer 3. +// implementation of standard functionality based on layer 3. // This is divided into two parts // // a) the view objects provided by the Dup() calls of the layer 1 API. // All dups are internally based on a single base object. +// These objects are called proxy. They act as base object +// for the views and as wrapper for the implementation objects +// providing generic implementations potentially based on +// the implementation functionality. // // b) the base object for all dup views is used to implement some // common functionality like the view management. The base object diff --git a/pkg/contexts/ocm/cpi/repocpi/base_c.go b/pkg/contexts/ocm/cpi/repocpi/proxy_c.go similarity index 58% rename from pkg/contexts/ocm/cpi/repocpi/base_c.go rename to pkg/contexts/ocm/cpi/repocpi/proxy_c.go index 2ae2f964c4..3e300a7ec8 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/proxy_c.go @@ -27,8 +27,8 @@ type ComponentVersionAccessInfo struct { // ComponentAccessImpl is the provider implementation // interface for component versions. type ComponentAccessImpl interface { - SetBase(base ComponentAccessBase) - GetParentBase() RepositoryViewManager + SetProxy(proxy ComponentAccessProxy) + GetParentProxy() RepositoryViewManager GetContext() cpi.Context GetName() string @@ -42,67 +42,67 @@ type ComponentAccessImpl interface { io.Closer } -type _componentAccessImplBase = resource.ResourceImplBase[cpi.ComponentAccess] +type _componentAccessProxyBase = resource.ResourceImplBase[cpi.ComponentAccess] -type componentAccessBase struct { - *_componentAccessImplBase +type componentAccessProxy struct { + *_componentAccessProxyBase ctx cpi.Context name string impl ComponentAccessImpl } -func newComponentAccessImplBase(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessBase, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentAccess, cpi.Repository](impl.GetParentBase(), closer...) +func newComponentAccessProxy(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessProxy, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentAccess, cpi.Repository](impl.GetParentProxy(), closer...) if err != nil { return nil, err } - b := &componentAccessBase{ - _componentAccessImplBase: base, - ctx: impl.GetContext(), - name: impl.GetName(), - impl: impl, + b := &componentAccessProxy{ + _componentAccessProxyBase: base, + ctx: impl.GetContext(), + name: impl.GetName(), + impl: impl, } - impl.SetBase(b) + impl.SetProxy(b) return b, nil } -func (b *componentAccessBase) Close() error { +func (b *componentAccessProxy) Close() error { list := errors.ErrListf("closing component %s", b.name) - refmgmt.AllocLog.Trace("closing component base", "name", b.name) + refmgmt.AllocLog.Trace("closing component proxy", "name", b.name) list.Add(b.impl.Close()) - list.Add(b._componentAccessImplBase.Close()) - refmgmt.AllocLog.Trace("closed component base", "name", b.name) + list.Add(b._componentAccessProxyBase.Close()) + refmgmt.AllocLog.Trace("closed component proxy", "name", b.name) return list.Result() } -func (b *componentAccessBase) GetContext() cpi.Context { +func (b *componentAccessProxy) GetContext() cpi.Context { return b.ctx } -func (b *componentAccessBase) GetName() string { +func (b *componentAccessProxy) GetName() string { return b.name } -func (b *componentAccessBase) IsReadOnly() bool { +func (b *componentAccessProxy) IsReadOnly() bool { return b.impl.IsReadOnly() } -func (c *componentAccessBase) IsOwned(cv cpi.ComponentVersionAccess) bool { - base, err := GetComponentVersionAccessBase(cv) +func (c *componentAccessProxy) IsOwned(cv cpi.ComponentVersionAccess) bool { + proxy, err := GetComponentVersionAccessProxy(cv) if err != nil { return false } - impl := base.(*componentVersionAccessBase).impl - cvcompmgr := impl.GetParentBase() + impl := proxy.(*componentVersionAccessProxy).impl + cvcompmgr := impl.GetParentProxy() return c == cvcompmgr } -func (b *componentAccessBase) ListVersions() ([]string, error) { +func (b *componentAccessProxy) ListVersions() ([]string, error) { return b.impl.ListVersions() } -func (b *componentAccessBase) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { +func (b *componentAccessProxy) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { i, err := b.impl.LookupVersion(version) if err != nil { return nil, err @@ -113,11 +113,11 @@ func (b *componentAccessBase) LookupVersion(version string) (cpi.ComponentVersio return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, i.Persistent, !compositionmodeattr.Get(b.GetContext())) } -func (b *componentAccessBase) HasVersion(vers string) (bool, error) { +func (b *componentAccessProxy) HasVersion(vers string) (bool, error) { return b.impl.HasVersion(vers) } -func (b *componentAccessBase) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { +func (b *componentAccessProxy) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { i, err := b.impl.NewVersion(version, overrides...) if err != nil { return nil, err @@ -128,11 +128,11 @@ func (b *componentAccessBase) NewVersion(version string, overrides ...bool) (cpi return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, false, !compositionmodeattr.Get(b.GetContext())) } -func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) { +func (c *componentAccessProxy) AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) { var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) - cvbase, err := GetComponentVersionAccessBase(cv) + cvproxy, err := GetComponentVersionAccessProxy(cv) if err != nil { return err } @@ -146,7 +146,7 @@ func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess, opts *cp finalize.With(func() error { return eff.Close() }) - cvbase, err = GetComponentVersionAccessBase(eff) + cvproxy, err = GetComponentVersionAccessProxy(eff) if err != nil { return err } @@ -154,20 +154,20 @@ func (c *componentAccessBase) AddVersion(cv cpi.ComponentVersionAccess, opts *cp d := eff.GetDescriptor() *d = *cv.GetDescriptor().Copy() - err = c.setupLocalBlobs("resource", cv, cvbase, d.Resources, &opts.BlobUploadOptions) + err = c.setupLocalBlobs("resource", cv, cvproxy, d.Resources, &opts.BlobUploadOptions) if err == nil { - err = c.setupLocalBlobs("source", cv, cvbase, d.Sources, &opts.BlobUploadOptions) + err = c.setupLocalBlobs("source", cv, cvproxy, d.Sources, &opts.BlobUploadOptions) } if err != nil { return err } } - cvbase.EnablePersistence() - err = cvbase.Update(!cvbase.UseDirectAccess()) + cvproxy.EnablePersistence() + err = cvproxy.Update(!cvproxy.UseDirectAccess()) return err } -func (c *componentAccessBase) setupLocalBlobs(kind string, src cpi.ComponentVersionAccess, tgtbase ComponentVersionAccessBase, it compdesc.ArtifactAccessor, opts *cpi.BlobUploadOptions) (ferr error) { +func (c *componentAccessProxy) setupLocalBlobs(kind string, src cpi.ComponentVersionAccess, tgtproxy ComponentVersionAccessProxy, it compdesc.ArtifactAccessor, opts *cpi.BlobUploadOptions) (ferr error) { ctx := src.GetContext() // transfer all local blobs prov := func(spec cpi.AccessSpec) (blob blobaccess.BlobAccess, ref string, global cpi.AccessSpec, err error) { @@ -176,10 +176,10 @@ func (c *componentAccessBase) setupLocalBlobs(kind string, src cpi.ComponentVers if err != nil { return nil, "", nil, err } - return m.AsBlobAccess(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, tgtbase.GetContext()), nil + return m.AsBlobAccess(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, tgtproxy.GetContext()), nil } return nil, "", nil, nil } - return tgtbase.(*componentVersionAccessBase).setupLocalBlobs(kind, prov, it, false, opts) + return tgtproxy.(*componentVersionAccessProxy).setupLocalBlobs(kind, prov, it, false, opts) } diff --git a/pkg/contexts/ocm/cpi/repocpi/base_cv.go b/pkg/contexts/ocm/cpi/repocpi/proxy_cv.go similarity index 73% rename from pkg/contexts/ocm/cpi/repocpi/base_cv.go rename to pkg/contexts/ocm/cpi/repocpi/proxy_cv.go index 0064d5f2bb..ea2d56d03a 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/proxy_cv.go @@ -35,8 +35,8 @@ import ( // interface for component versions. type ComponentVersionAccessImpl interface { GetContext() cpi.Context - SetBase(base ComponentVersionAccessBase) - GetParentBase() ComponentAccessBase + SetProxy(proxy ComponentVersionAccessProxy) + GetParentProxy() ComponentAccessProxy Repository() cpi.Repository @@ -52,25 +52,17 @@ type ComponentVersionAccessImpl interface { io.Closer } -type ComponentVersionAccessImplSupport struct { - Base ComponentVersionAccessBase -} - -func (b *ComponentVersionAccessImplSupport) SetBase(base ComponentVersionAccessBase) { - b.Base = base -} - -type _componentVersionAccessImplBase = resource.ResourceImplBase[cpi.ComponentVersionAccess] +type _componentVersionAccessProxyBase = resource.ResourceImplBase[cpi.ComponentVersionAccess] -// componentVersionAccessBase is the counterpart to views, all views +// componentVersionAccessProxy is the counterpart to views, all views // created by Dup calls use this base object to work on. // Besides some functionality covered by view objects these base objects // implement provider-agnostic parts of the ComponentVersionAccess API. -type componentVersionAccessBase struct { +type componentVersionAccessProxy struct { lock sync.Mutex id finalizer.ObjectIdentity - *_componentVersionAccessImplBase + *_componentVersionAccessProxyBase ctx cpi.Context name string version string @@ -86,37 +78,37 @@ type componentVersionAccessBase struct { impl ComponentVersionAccessImpl } -var _ ComponentVersionAccessBase = (*componentVersionAccessBase)(nil) +var _ ComponentVersionAccessProxy = (*componentVersionAccessProxy)(nil) -func newComponentVersionAccessBase(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBase, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess, cpi.ComponentAccess](impl.GetParentBase(), closer...) +func newComponentVersionAccessProxy(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessProxy, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess, cpi.ComponentAccess](impl.GetParentProxy(), closer...) if err != nil { return nil, err } - b := &componentVersionAccessBase{ - _componentVersionAccessImplBase: base, - id: finalizer.NewObjectIdentity(fmt.Sprintf("%s:%s", name, version)), - ctx: impl.GetContext(), - name: name, - version: version, - blobcache: NewBlobCache(), - lazy: lazy, - persistent: persistent, - directAccess: direct, - impl: impl, - } - impl.SetBase(b) + b := &componentVersionAccessProxy{ + _componentVersionAccessProxyBase: base, + id: finalizer.NewObjectIdentity(fmt.Sprintf("%s:%s", name, version)), + ctx: impl.GetContext(), + name: name, + version: version, + blobcache: NewBlobCache(), + lazy: lazy, + persistent: persistent, + directAccess: direct, + impl: impl, + } + impl.SetProxy(b) return b, nil } func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersionAccess) (T, error) { var _nil T - impl, err := GetComponentVersionAccessBase(cv) + impl, err := GetComponentVersionAccessProxy(cv) if err != nil { return _nil, err } - if mine, ok := impl.(*componentVersionAccessBase); ok { + if mine, ok := impl.(*componentVersionAccessProxy); ok { cont, ok := mine.impl.(T) if ok { return cont, nil @@ -126,7 +118,7 @@ func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersi return _nil, errors.Newf("non-matching component version implementation %T", impl) } -func (b *componentVersionAccessBase) Close() error { +func (b *componentVersionAccessProxy) Close() error { list := errors.ErrListf("closing component version %s", common.VersionedElementKey(b)) refmgmt.AllocLog.Trace("closing component version base", "name", common.VersionedElementKey(b)) // prepare artifact access for final close in @@ -135,33 +127,33 @@ func (b *componentVersionAccessBase) Close() error { list.Add(b.update(true)) } list.Add(b.impl.Close()) - list.Add(b._componentVersionAccessImplBase.Close()) + list.Add(b._componentVersionAccessProxyBase.Close()) list.Add(b.blobcache.Clear()) refmgmt.AllocLog.Trace("closed component version base", "name", common.VersionedElementKey(b)) return list.Result() } -func (b *componentVersionAccessBase) GetContext() cpi.Context { +func (b *componentVersionAccessProxy) GetContext() cpi.Context { return b.ctx } -func (b *componentVersionAccessBase) GetName() string { +func (b *componentVersionAccessProxy) GetName() string { return b.name } -func (b *componentVersionAccessBase) GetVersion() string { +func (b *componentVersionAccessProxy) GetVersion() string { return b.version } -func (b *componentVersionAccessBase) GetImplementation() ComponentVersionAccessImpl { +func (b *componentVersionAccessProxy) GetImplementation() ComponentVersionAccessImpl { return b.impl } -func (b *componentVersionAccessBase) GetBlobCache() BlobCache { +func (b *componentVersionAccessProxy) GetBlobCache() BlobCache { return b.blobcache } -func (b *componentVersionAccessBase) EnablePersistence() bool { +func (b *componentVersionAccessProxy) EnablePersistence() bool { if b.discardChanges { return false } @@ -170,30 +162,30 @@ func (b *componentVersionAccessBase) EnablePersistence() bool { return true } -func (b *componentVersionAccessBase) IsPersistent() bool { +func (b *componentVersionAccessProxy) IsPersistent() bool { return b.persistent } -func (b *componentVersionAccessBase) UseDirectAccess() bool { +func (b *componentVersionAccessProxy) UseDirectAccess() bool { return b.directAccess } -func (b *componentVersionAccessBase) DiscardChanges() { +func (b *componentVersionAccessProxy) DiscardChanges() { b.discardChanges = true } -func (b *componentVersionAccessBase) Repository() cpi.Repository { +func (b *componentVersionAccessProxy) Repository() cpi.Repository { return b.impl.Repository() } -func (b *componentVersionAccessBase) IsReadOnly() bool { +func (b *componentVersionAccessProxy) IsReadOnly() bool { return b.impl.IsReadOnly() } //////////////////////////////////////////////////////////////////////////////// // with access to actual view -func (b *componentVersionAccessBase) AccessMethod(spec cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (meth cpi.AccessMethod, err error) { +func (b *componentVersionAccessProxy) AccessMethod(spec cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (meth cpi.AccessMethod, err error) { switch { case compose.Is(spec): cspec, ok := spec.(*compose.AccessSpec) @@ -216,36 +208,36 @@ func (b *componentVersionAccessBase) AccessMethod(spec cpi.AccessSpec, cv refmgm return meth, err } -func (b *componentVersionAccessBase) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { +func (b *componentVersionAccessProxy) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { return b.impl.GetInexpensiveContentVersionIdentity(acc, cv) } -func (b *componentVersionAccessBase) GetDescriptor() *compdesc.ComponentDescriptor { +func (b *componentVersionAccessProxy) GetDescriptor() *compdesc.ComponentDescriptor { b.lock.Lock() defer b.lock.Unlock() return b.getDescriptor() } -func (b *componentVersionAccessBase) getDescriptor() *compdesc.ComponentDescriptor { +func (b *componentVersionAccessProxy) getDescriptor() *compdesc.ComponentDescriptor { if b.descriptor == nil { b.descriptor = b.impl.GetDescriptor() } return b.descriptor } -func (b *componentVersionAccessBase) GetStorageContext() cpi.StorageContext { +func (b *componentVersionAccessProxy) GetStorageContext() cpi.StorageContext { return b.impl.GetStorageContext() } -func (b *componentVersionAccessBase) ShouldUpdate(final bool) bool { +func (b *componentVersionAccessProxy) ShouldUpdate(final bool) bool { b.lock.Lock() defer b.lock.Unlock() return b.shouldUpdate(final) } -func (b *componentVersionAccessBase) shouldUpdate(final bool) bool { +func (b *componentVersionAccessProxy) shouldUpdate(final bool) bool { if b.discardChanges { return false } @@ -255,14 +247,14 @@ func (b *componentVersionAccessBase) shouldUpdate(final bool) bool { return !b.lazy && b.directAccess && b.persistent } -func (b *componentVersionAccessBase) Update(final bool) error { +func (b *componentVersionAccessProxy) Update(final bool) error { b.lock.Lock() defer b.lock.Unlock() return b.update(final) } -func (b *componentVersionAccessBase) update(final bool) error { +func (b *componentVersionAccessProxy) update(final bool) error { if !b.shouldUpdate(final) { return nil } @@ -288,7 +280,7 @@ func (b *componentVersionAccessBase) update(final bool) error { return err } -func (b *componentVersionAccessBase) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { +func (b *componentVersionAccessProxy) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { key, err := json.Marshal(acc) if err != nil { return nil @@ -296,7 +288,7 @@ func (b *componentVersionAccessBase) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAc return b.blobcache.GetBlobFor(string(key)) } -func (b *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { +func (b *componentVersionAccessProxy) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } @@ -357,7 +349,7 @@ func (b *componentVersionAccessBase) AddBlob(blob cpi.BlobAccess, artType, refNa return b.cacheLocalBlob(acc, blob) } -func (b *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { +func (b *componentVersionAccessProxy) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { key, err := json.Marshal(acc) if err != nil { return nil, errors.Wrapf(err, "cannot marshal access spec") @@ -385,7 +377,7 @@ func (b *componentVersionAccessBase) cacheLocalBlob(acc cpi.AccessSpec, blob cpi //////////////////////////////////////////////////////////////////////////////// -func (b *componentVersionAccessBase) composeAccess(spec cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error) { +func (b *componentVersionAccessProxy) composeAccess(spec cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error) { if !compose.Is(spec) { return nil, "", nil, nil } @@ -405,7 +397,7 @@ func (b *componentVersionAccessBase) composeAccess(spec cpi.AccessSpec) (blobacc return blob, cspec.ReferenceName, cspec.GlobalAccess.Get(), nil } -func (b *componentVersionAccessBase) setupLocalBlobs(kind string, accprov func(cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error), it compdesc.ArtifactAccessor, final bool, opts *cpi.BlobUploadOptions) (ferr error) { +func (b *componentVersionAccessProxy) setupLocalBlobs(kind string, accprov func(cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error), it compdesc.ArtifactAccessor, final bool, opts *cpi.BlobUploadOptions) (ferr error) { var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) diff --git a/pkg/contexts/ocm/cpi/repocpi/base_r.go b/pkg/contexts/ocm/cpi/repocpi/proxy_r.go similarity index 59% rename from pkg/contexts/ocm/cpi/repocpi/base_r.go rename to pkg/contexts/ocm/cpi/repocpi/proxy_r.go index e2eb5687d4..1adae9295b 100644 --- a/pkg/contexts/ocm/cpi/repocpi/base_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/proxy_r.go @@ -20,7 +20,7 @@ type ComponentAccessInfo struct { } type RepositoryImpl interface { - SetBase(base RepositoryBase) + SetProxy(proxy RepositoryProxy) GetContext() cpi.Context @@ -33,52 +33,52 @@ type RepositoryImpl interface { io.Closer } -type _repositoryImplBase = resource.ResourceImplBase[cpi.Repository] +type _repositoryProxyBase = resource.ResourceImplBase[cpi.Repository] -type repositoryBase struct { - *_repositoryImplBase +type repositoryProxy struct { + *_repositoryProxyBase ctx cpi.Context kind string impl RepositoryImpl } -func newRepositoryImplBase(impl RepositoryImpl, kind string, closer ...io.Closer) RepositoryBase { +func newRepositoryProxy(impl RepositoryImpl, kind string, closer ...io.Closer) RepositoryProxy { base := resource.NewSimpleResourceImplBase[cpi.Repository](closer...) - b := &repositoryBase{ - _repositoryImplBase: base, - ctx: impl.GetContext(), - impl: impl, + b := &repositoryProxy{ + _repositoryProxyBase: base, + ctx: impl.GetContext(), + impl: impl, } - impl.SetBase(b) + impl.SetProxy(b) return b } -func (b *repositoryBase) Close() error { +func (b *repositoryProxy) Close() error { list := errors.ErrListf("closing %s", b.kind) - refmgmt.AllocLog.Trace("closing repository base", "kind", b.kind) + refmgmt.AllocLog.Trace("closing repository proxy", "kind", b.kind) list.Add(b.impl.Close()) - list.Add(b._repositoryImplBase.Close()) - refmgmt.AllocLog.Trace("closed repository base", "kind", b.kind) + list.Add(b._repositoryProxyBase.Close()) + refmgmt.AllocLog.Trace("closed repository proxy", "kind", b.kind) return list.Result() } -func (b *repositoryBase) GetContext() cpi.Context { +func (b *repositoryProxy) GetContext() cpi.Context { return b.ctx } -func (b *repositoryBase) GetSpecification() cpi.RepositorySpec { +func (b *repositoryProxy) GetSpecification() cpi.RepositorySpec { return b.impl.GetSpecification() } -func (b *repositoryBase) ComponentLister() cpi.ComponentLister { +func (b *repositoryProxy) ComponentLister() cpi.ComponentLister { return b.impl.ComponentLister() } -func (b *repositoryBase) ExistsComponentVersion(name string, version string) (bool, error) { +func (b *repositoryProxy) ExistsComponentVersion(name string, version string) (bool, error) { return b.impl.ExistsComponentVersion(name, version) } -func (b *repositoryBase) LookupComponentVersion(name string, version string) (cv cpi.ComponentVersionAccess, rerr error) { +func (b *repositoryProxy) LookupComponentVersion(name string, version string) (cv cpi.ComponentVersionAccess, rerr error) { c, err := b.LookupComponent(name) if err != nil { return nil, err @@ -88,7 +88,7 @@ func (b *repositoryBase) LookupComponentVersion(name string, version string) (cv return c.LookupVersion(version) } -func (b *repositoryBase) LookupComponent(name string) (cpi.ComponentAccess, error) { +func (b *repositoryProxy) LookupComponent(name string) (cpi.ComponentAccess, error) { i, err := b.impl.LookupComponent(name) if err != nil { return nil, err diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index 883b7fc748..f393faa827 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -21,7 +21,7 @@ type _componentAccessView interface { type ComponentAccessViewManager = resource.ViewManager[cpi.ComponentAccess] // here you have to use an alias -type ComponentAccessBase interface { +type ComponentAccessProxy interface { resource.ResourceImplementation[cpi.ComponentAccess] GetContext() cpi.Context @@ -41,7 +41,7 @@ type ComponentAccessBase interface { type componentAccessView struct { _componentAccessView - base ComponentAccessBase + proxy ComponentAccessProxy } var ( @@ -49,57 +49,57 @@ var ( _ utils.Unwrappable = (*componentAccessView)(nil) ) -func GetComponentAccessBase(n cpi.ComponentAccess) (ComponentAccessBase, error) { +func GetComponentAccessProxy(n cpi.ComponentAccess) (ComponentAccessProxy, error) { if v, ok := n.(*componentAccessView); ok { - return v.base, nil + return v.proxy, nil } return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", n)) } func GetComponentAccessImplementation(n cpi.ComponentAccess) (ComponentAccessImpl, error) { if v, ok := n.(*componentAccessView); ok { - if b, ok := v.base.(*componentAccessBase); ok { + if b, ok := v.proxy.(*componentAccessProxy); ok { return b.impl, nil } - return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", v.base)) + return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", v.proxy)) } return nil, errors.ErrNotSupported("component implementation type", fmt.Sprintf("%T", n)) } -func componentAccessViewCreator(i ComponentAccessBase, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess { +func componentAccessViewCreator(i ComponentAccessProxy, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess { return &componentAccessView{ _componentAccessView: resource.NewView[cpi.ComponentAccess](v, d), - base: i, + proxy: i, } } func NewComponentAccess(impl ComponentAccessImpl, kind string, main bool, closer ...io.Closer) (cpi.ComponentAccess, error) { - base, err := newComponentAccessImplBase(impl, closer...) + proxy, err := newComponentAccessProxy(impl, closer...) if err != nil { return nil, errors.Join(err, impl.Close()) } if kind == "" { kind = "component" } - cv := resource.NewResource[cpi.ComponentAccess](base, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), main) + cv := resource.NewResource[cpi.ComponentAccess](proxy, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), main) return cv, nil } func (c *componentAccessView) Unwrap() interface{} { - return c.base + return c.proxy } func (c *componentAccessView) GetContext() cpi.Context { - return c.base.GetContext() + return c.proxy.GetContext() } func (c *componentAccessView) GetName() string { - return c.base.GetName() + return c.proxy.GetName() } func (c *componentAccessView) ListVersions() (list []string, err error) { err = c.Execute(func() error { - list, err = c.base.ListVersions() + list, err = c.proxy.ListVersions() return err }) return list, err @@ -107,7 +107,7 @@ func (c *componentAccessView) ListVersions() (list []string, err error) { func (c *componentAccessView) LookupVersion(version string) (acc cpi.ComponentVersionAccess, err error) { err = c.Execute(func() error { - acc, err = c.base.LookupVersion(version) + acc, err = c.proxy.LookupVersion(version) return err }) return acc, err @@ -119,7 +119,7 @@ func (c *componentAccessView) AddVersion(acc cpi.ComponentVersionAccess, overwri } return c.Execute(func() error { - return c.base.AddVersion(acc, cpi.NewAddVersionOptions(cpi.Overwrite(utils.Optional(overwrite...)))) + return c.proxy.AddVersion(acc, cpi.NewAddVersionOptions(cpi.Overwrite(utils.Optional(overwrite...)))) }) } @@ -128,16 +128,16 @@ func (c *componentAccessView) AddVersionOpt(acc cpi.ComponentVersionAccess, opts return errors.ErrInvalid("component name", acc.GetName()) } return c.Execute(func() error { - return c.base.AddVersion(acc, cpi.NewAddVersionOptions(opts...)) + return c.proxy.AddVersion(acc, cpi.NewAddVersionOptions(opts...)) }) } func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc cpi.ComponentVersionAccess, err error) { err = c.Execute(func() error { - if c.base.IsReadOnly() { + if c.proxy.IsReadOnly() { return accessio.ErrReadOnly } - acc, err = c.base.NewVersion(version, overrides...) + acc, err = c.proxy.NewVersion(version, overrides...) return err }) return acc, err @@ -145,7 +145,7 @@ func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc func (c *componentAccessView) HasVersion(vers string) (ok bool, err error) { err = c.Execute(func() error { - ok, err = c.base.HasVersion(vers) + ok, err = c.proxy.HasVersion(vers) return err }) return ok, err diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index 6f4df0e845..d196b97885 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -41,7 +41,7 @@ type _componentVersionAccessView interface { type ComponentVersionAccessViewManager = resource.ViewManager[cpi.ComponentVersionAccess] -type ComponentVersionAccessBase interface { +type ComponentVersionAccessProxy interface { resource.ResourceImplementation[cpi.ComponentVersionAccess] common.VersionedElement io.Closer @@ -96,8 +96,8 @@ type ComponentVersionAccessBase interface { type componentVersionAccessView struct { _componentVersionAccessView - base ComponentVersionAccessBase - err error + proxy ComponentVersionAccessProxy + err error } var ( @@ -105,41 +105,41 @@ var ( _ utils.Unwrappable = (*componentVersionAccessView)(nil) ) -func GetComponentVersionAccessBase(n cpi.ComponentVersionAccess) (ComponentVersionAccessBase, error) { +func GetComponentVersionAccessProxy(n cpi.ComponentVersionAccess) (ComponentVersionAccessProxy, error) { if v, ok := n.(*componentVersionAccessView); ok { - return v.base, nil + return v.proxy, nil } - return nil, errors.ErrNotSupported("component version base type", fmt.Sprintf("%T", n)) + return nil, errors.ErrNotSupported("component version proxy type", fmt.Sprintf("%T", n)) } func GetComponentVersionAccessImplementation(n cpi.ComponentVersionAccess) (ComponentVersionAccessImpl, error) { if v, ok := n.(*componentVersionAccessView); ok { - if b, ok := v.base.(*componentVersionAccessBase); ok { + if b, ok := v.proxy.(*componentVersionAccessProxy); ok { return b.impl, nil } - return nil, errors.ErrNotSupported("component version base type", fmt.Sprintf("%T", v.base)) + return nil, errors.ErrNotSupported("component version proxy type", fmt.Sprintf("%T", v.proxy)) } return nil, errors.ErrNotSupported("component version implementation type", fmt.Sprintf("%T", n)) } -func artifactAccessViewCreator(i ComponentVersionAccessBase, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess { +func artifactAccessViewCreator(i ComponentVersionAccessProxy, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess { cv := &componentVersionAccessView{ _componentVersionAccessView: resource.NewView[cpi.ComponentVersionAccess](v, d), - base: i, + proxy: i, } return cv } func NewComponentVersionAccess(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (cpi.ComponentVersionAccess, error) { - base, err := newComponentVersionAccessBase(name, version, impl, lazy, persistent, direct, closer...) + proxy, err := newComponentVersionAccessProxy(name, version, impl, lazy, persistent, direct, closer...) if err != nil { return nil, errors.Join(err, impl.Close()) } - return resource.NewResource[cpi.ComponentVersionAccess](base, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", name, version), true), nil + return resource.NewResource[cpi.ComponentVersionAccess](proxy, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", name, version), true), nil } func (c *componentVersionAccessView) Unwrap() interface{} { - return c.base + return c.proxy } func (c *componentVersionAccessView) Close() error { @@ -149,23 +149,23 @@ func (c *componentVersionAccessView) Close() error { } func (c *componentVersionAccessView) Repository() cpi.Repository { - return c.base.Repository() + return c.proxy.Repository() } func (c *componentVersionAccessView) GetContext() internal.Context { - return c.base.GetContext() + return c.proxy.GetContext() } func (c *componentVersionAccessView) GetName() string { - return c.base.GetName() + return c.proxy.GetName() } func (c *componentVersionAccessView) GetVersion() string { - return c.base.GetVersion() + return c.proxy.GetVersion() } func (c *componentVersionAccessView) GetDescriptor() *compdesc.ComponentDescriptor { - return c.base.GetDescriptor() + return c.proxy.GetDescriptor() } func (c *componentVersionAccessView) GetProvider() *compdesc.Provider { @@ -195,7 +195,7 @@ func (c *componentVersionAccessView) AccessMethod(spec cpi.AccessSpec) (meth cpi func (c *componentVersionAccessView) accessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) { switch { case spec.IsLocal(c.GetContext()): - return c.base.AccessMethod(spec, c.Allocatable()) + return c.proxy.AccessMethod(spec, c.Allocatable()) default: return spec.AccessMethod(c) } @@ -225,16 +225,16 @@ func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec c // fall back to original version return spec.GetInexpensiveContentVersionIdentity(c) default: - return c.base.GetInexpensiveContentVersionIdentity(spec, c.Allocatable()) + return c.proxy.GetInexpensiveContentVersionIdentity(spec, c.Allocatable()) } } func (c *componentVersionAccessView) Update() error { return c.Execute(func() error { - if !c.base.IsPersistent() { + if !c.proxy.IsPersistent() { return ErrTempVersion } - return c.base.Update(true) + return c.proxy.Update(true) }) } @@ -243,7 +243,7 @@ func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refNa eff := cpi.NewBlobUploadOptions(opts...) err := c.Execute(func() error { var err error - spec, err = c.base.AddBlob(blob, artType, refName, global, false, eff) + spec, err = c.proxy.AddBlob(blob, artType, refName, global, false, eff) return err }) @@ -306,7 +306,7 @@ func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessVie set func(*T, compdesc.AccessSpec) error, setblob func(*T, cpi.BlobAccess, string, cpi.AccessSpec) error, ) error { - if c.base.IsReadOnly() { + if c.proxy.IsReadOnly() { return accessio.ErrReadOnly } meta := art.Meta() @@ -366,7 +366,7 @@ func (c *componentVersionAccessView) SetResourceAccess(art cpi.ResourceAccess, m } func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, acc compdesc.AccessSpec, modopts ...cpi.ModificationOption) error { - if c.base.IsReadOnly() { + if c.proxy.IsReadOnly() { return accessio.ErrReadOnly } @@ -375,11 +375,11 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac Access: acc, } - ctx := c.base.GetContext() + ctx := c.proxy.GetContext() opts := internal.NewModificationOptions(modopts...) cpi.CompleteModificationOptions(ctx, opts) - spec, err := c.base.GetContext().AccessSpecForSpec(acc) + spec, err := c.proxy.GetContext().AccessSpecForSpec(acc) if err != nil { return err } @@ -403,14 +403,14 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } } - cd := c.base.GetDescriptor() + cd := c.proxy.GetDescriptor() idx := cd.GetResourceIndex(&res.ResourceMeta) if idx >= 0 { old = &cd.Resources[idx] } if old == nil { - if !opts.IsModifyResource() && c.base.IsPersistent() { + if !opts.IsModifyResource() && c.proxy.IsPersistent() { return fmt.Errorf("new resource would invalidate signature") } } @@ -456,7 +456,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac if old != nil { eq := res.Equivalent(old) - if !eq.IsLocalHashEqual() && c.base.IsPersistent() { + if !eq.IsLocalHashEqual() && c.proxy.IsPersistent() { if !opts.IsModifyResource() { return fmt.Errorf("resource would invalidate signature") } @@ -469,7 +469,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } else { cd.Resources[idx] = *res } - return c.base.Update(false) + return c.proxy.Update(false) }) } @@ -500,7 +500,7 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R if !old.Digest.IsNone() { digester.HashAlgorithm = old.Digest.HashAlgorithm digester.NormalizationAlgorithm = old.Digest.NormalisationAlgorithm - if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.base.IsPersistent() { + if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.proxy.IsPersistent() { res.Digest = old.Digest value = old.Digest.Value } @@ -515,7 +515,7 @@ func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess) err } func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error { - if c.base.IsReadOnly() { + if c.proxy.IsReadOnly() { return accessio.ErrReadOnly } @@ -525,40 +525,40 @@ func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdes } return c.Execute(func() error { if res.Version == "" { - res.Version = c.base.GetVersion() + res.Version = c.proxy.GetVersion() } - cd := c.base.GetDescriptor() + cd := c.proxy.GetDescriptor() if idx := cd.GetSourceIndex(&res.SourceMeta); idx == -1 { cd.Sources = append(cd.Sources, *res) } else { cd.Sources[idx] = *res } - return c.base.Update(false) + return c.proxy.Update(false) }) } func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference) error { return c.Execute(func() error { - cd := c.base.GetDescriptor() + cd := c.proxy.GetDescriptor() if idx := cd.GetComponentReferenceIndex(*ref); idx == -1 { cd.References = append(cd.References, *ref) } else { cd.References[idx] = *ref } - return c.base.Update(false) + return c.proxy.Update(false) }) } func (c *componentVersionAccessView) DiscardChanges() { - c.base.DiscardChanges() + c.proxy.DiscardChanges() } func (c *componentVersionAccessView) IsPersistent() bool { - return c.base.IsPersistent() + return c.proxy.IsPersistent() } func (c *componentVersionAccessView) UseDirectAccess() bool { - return c.base.UseDirectAccess() + return c.proxy.UseDirectAccess() } //////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/cpi/repocpi/view_r.go b/pkg/contexts/ocm/cpi/repocpi/view_r.go index 440fde63a5..f7073a3925 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_r.go @@ -30,7 +30,7 @@ type _repositoryView interface { type RepositoryViewManager = resource.ViewManager[cpi.Repository] // here you have to use an alias -type RepositoryBase interface { +type RepositoryProxy interface { resource.ResourceImplementation[cpi.Repository] GetContext() cpi.Context @@ -47,7 +47,7 @@ type RepositoryBase interface { type repositoryView struct { _repositoryView - base RepositoryBase + proxy RepositoryProxy } var ( @@ -56,74 +56,74 @@ var ( _ utils.Unwrappable = (*repositoryView)(nil) ) -func GetRepositoryBase(n cpi.Repository) (RepositoryBase, error) { +func GetRepositoryProxy(n cpi.Repository) (RepositoryProxy, error) { if v, ok := n.(*repositoryView); ok { - return v.base, nil + return v.proxy, nil } return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) } func GetRepositoryImplementation(n cpi.Repository) (RepositoryImpl, error) { if v, ok := n.(*repositoryView); ok { - if b, ok := v.base.(*repositoryBase); ok { + if b, ok := v.proxy.(*repositoryProxy); ok { return b.impl, nil } - return nil, errors.ErrNotSupported("repository base type", fmt.Sprintf("%T", v.base)) + return nil, errors.ErrNotSupported("repository base type", fmt.Sprintf("%T", v.proxy)) } return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) } -func repositoryViewCreator(i RepositoryBase, v resource.CloserView, d RepositoryViewManager) cpi.Repository { +func repositoryViewCreator(i RepositoryProxy, v resource.CloserView, d RepositoryViewManager) cpi.Repository { return &repositoryView{ _repositoryView: resource.NewView[cpi.Repository](v, d), - base: i, + proxy: i, } } // NewNoneRefRepositoryView provides a repository reflecting the state of the // view manager without holding an additional reference. -func NewNoneRefRepositoryView(i RepositoryBase) cpi.Repository { +func NewNoneRefRepositoryView(i RepositoryProxy) cpi.Repository { return &repositoryView{ _repositoryView: resource.NewView[cpi.Repository](resource.NewNonRefView[cpi.Repository](i), i), - base: i, + proxy: i, } } func NewRepository(impl RepositoryImpl, kind string, closer ...io.Closer) cpi.Repository { - base := newRepositoryImplBase(impl, kind, closer...) + proxy := newRepositoryProxy(impl, kind, closer...) if kind == "" { kind = "OCM repository" } - return resource.NewResource[cpi.Repository](base, repositoryViewCreator, kind, true) + return resource.NewResource[cpi.Repository](proxy, repositoryViewCreator, kind, true) } func (r *repositoryView) Unwrap() interface{} { - return r.base + return r.proxy } func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { - return credentials.GetProvidedConsumerId(r.base, uctx...) + return credentials.GetProvidedConsumerId(r.proxy, uctx...) } func (r *repositoryView) GetIdentityMatcher() string { - return credentials.GetProvidedIdentityMatcher(r.base) + return credentials.GetProvidedIdentityMatcher(r.proxy) } func (r *repositoryView) GetSpecification() cpi.RepositorySpec { - return r.base.GetSpecification() + return r.proxy.GetSpecification() } func (r *repositoryView) GetContext() cpi.Context { - return r.base.GetContext() + return r.proxy.GetContext() } func (r *repositoryView) ComponentLister() cpi.ComponentLister { - return r.base.ComponentLister() + return r.proxy.ComponentLister() } func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) { err = r.Execute(func() error { - ok, err = r.base.ExistsComponentVersion(name, version) + ok, err = r.proxy.ExistsComponentVersion(name, version) return err }) return ok, err @@ -131,7 +131,7 @@ func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok func (r *repositoryView) LookupComponentVersion(name string, version string) (acc cpi.ComponentVersionAccess, err error) { err = r.Execute(func() error { - acc, err = r.base.LookupComponentVersion(name, version) + acc, err = r.proxy.LookupComponentVersion(name, version) return err }) return acc, err @@ -139,7 +139,7 @@ func (r *repositoryView) LookupComponentVersion(name string, version string) (ac func (r *repositoryView) LookupComponent(name string) (acc cpi.ComponentAccess, err error) { err = r.Execute(func() error { - acc, err = r.base.LookupComponent(name) + acc, err = r.proxy.LookupComponent(name) return err }) return acc, err diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index e2bdad11e6..37b01a1f5c 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -103,7 +103,7 @@ func (c *ComponentArchive) SetVersion(v string) { type componentArchiveContainer struct { ctx cpi.Context - base repocpi.ComponentVersionAccessBase + base repocpi.ComponentVersionAccessProxy fsacc *accessobj.FileSystemBlobAccess spec *RepositorySpec repo cpi.Repository @@ -111,11 +111,11 @@ type componentArchiveContainer struct { var _ repocpi.ComponentVersionAccessImpl = (*componentArchiveContainer)(nil) -func (c *componentArchiveContainer) SetBase(base repocpi.ComponentVersionAccessBase) { +func (c *componentArchiveContainer) SetProxy(base repocpi.ComponentVersionAccessProxy) { c.base = base } -func (c *componentArchiveContainer) GetParentBase() repocpi.ComponentAccessBase { +func (c *componentArchiveContainer) GetParentProxy() repocpi.ComponentAccessProxy { return nil } diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index 7240aefbde..c2c81fd957 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -26,7 +26,7 @@ import ( type RepositoryImpl struct { lock sync.RWMutex - base repocpi.RepositoryBase + base repocpi.RepositoryProxy arch *ComponentArchive nonref cpi.Repository } @@ -57,7 +57,7 @@ func (r *RepositoryImpl) Close() error { return r.arch.container.Close() } -func (r *RepositoryImpl) SetBase(base repocpi.RepositoryBase) { +func (r *RepositoryImpl) SetProxy(base repocpi.RepositoryProxy) { r.base = base r.nonref = repocpi.NewNoneRefRepositoryView(base) } @@ -143,7 +143,7 @@ func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessI //////////////////////////////////////////////////////////////////////////////// type ComponentAccessImpl struct { - base repocpi.ComponentAccessBase + base repocpi.ComponentAccessProxy repo *RepositoryImpl } @@ -160,11 +160,11 @@ func (c *ComponentAccessImpl) Close() error { return nil } -func (c *ComponentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { +func (c *ComponentAccessImpl) SetProxy(base repocpi.ComponentAccessProxy) { c.base = base } -func (c *ComponentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { +func (c *ComponentAccessImpl) GetParentProxy() repocpi.RepositoryViewManager { return c.repo.base } @@ -208,7 +208,7 @@ func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (*re //////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl repocpi.ComponentVersionAccessBase + impl repocpi.ComponentVersionAccessProxy comp *ComponentAccessImpl @@ -232,11 +232,11 @@ func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionC }, nil } -func (c *ComponentVersionContainer) SetBase(impl repocpi.ComponentVersionAccessBase) { +func (c *ComponentVersionContainer) SetProxy(impl repocpi.ComponentVersionAccessProxy) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentBase() repocpi.ComponentAccessBase { +func (c *ComponentVersionContainer) GetParentProxy() repocpi.ComponentAccessProxy { return c.comp.base } diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index 22a57457da..acc87cf4ff 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -24,7 +24,7 @@ const META_SEPARATOR = ".build-" //////////////////////////////////////////////////////////////////////////////// type componentAccessImpl struct { - base repocpi.ComponentAccessBase + base repocpi.ComponentAccessProxy repo *RepositoryImpl name string namespace oci.NamespaceAccess @@ -54,11 +54,11 @@ func (c *componentAccessImpl) Close() error { return err } -func (c *componentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { +func (c *componentAccessImpl) SetProxy(base repocpi.ComponentAccessProxy) { c.base = base } -func (c *componentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { +func (c *componentAccessImpl) GetParentProxy() repocpi.RepositoryViewManager { return c.repo.base } diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 725a1173e7..8657f65775 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -44,7 +44,7 @@ func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessI // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl repocpi.ComponentVersionAccessBase + impl repocpi.ComponentVersionAccessProxy comp *componentAccessImpl version string @@ -75,11 +75,11 @@ func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAcce }, nil } -func (c *ComponentVersionContainer) SetBase(impl repocpi.ComponentVersionAccessBase) { +func (c *ComponentVersionContainer) SetProxy(impl repocpi.ComponentVersionAccessProxy) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentBase() repocpi.ComponentAccessBase { +func (c *ComponentVersionContainer) GetParentProxy() repocpi.ComponentAccessProxy { return c.comp.base } diff --git a/pkg/contexts/ocm/repositories/genericocireg/repository.go b/pkg/contexts/ocm/repositories/genericocireg/repository.go index 641ccb857b..9040e72e4a 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repository.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repository.go @@ -40,7 +40,7 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository { } type RepositoryImpl struct { - base repocpi.RepositoryBase + base repocpi.RepositoryProxy ctx cpi.Context meta ComponentRepositoryMeta nonref cpi.Repository @@ -65,7 +65,7 @@ func (r *RepositoryImpl) Close() error { return r.ocirepo.Close() } -func (r *RepositoryImpl) SetBase(base repocpi.RepositoryBase) { +func (r *RepositoryImpl) SetProxy(base repocpi.RepositoryProxy) { r.base = base r.nonref = repocpi.NewNoneRefRepositoryView(base) } diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index 5c434667a9..a930ba51f1 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -12,7 +12,7 @@ import ( ) type componentAccessImpl struct { - base repocpi.ComponentAccessBase + base repocpi.ComponentAccessProxy repo *RepositoryImpl name string @@ -32,11 +32,11 @@ func (c *componentAccessImpl) Close() error { return nil } -func (c *componentAccessImpl) SetBase(base repocpi.ComponentAccessBase) { +func (c *componentAccessImpl) SetProxy(base repocpi.ComponentAccessProxy) { c.base = base } -func (c *componentAccessImpl) GetParentBase() repocpi.RepositoryViewManager { +func (c *componentAccessImpl) GetParentProxy() repocpi.RepositoryViewManager { return c.repo.base } diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index f4ce58f882..0c9aa4fdd7 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -33,7 +33,7 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - base repocpi.ComponentVersionAccessBase + base repocpi.ComponentVersionAccessProxy comp *componentAccessImpl version string @@ -50,11 +50,11 @@ func newComponentVersionContainer(comp *componentAccessImpl, version string, acc }, nil } -func (c *ComponentVersionContainer) SetBase(base repocpi.ComponentVersionAccessBase) { +func (c *ComponentVersionContainer) SetProxy(base repocpi.ComponentVersionAccessProxy) { c.base = base } -func (c *ComponentVersionContainer) GetParentBase() repocpi.ComponentAccessBase { +func (c *ComponentVersionContainer) GetParentProxy() repocpi.ComponentAccessProxy { return c.comp.base } diff --git a/pkg/contexts/ocm/repositories/virtual/repository.go b/pkg/contexts/ocm/repositories/virtual/repository.go index b9a79174ac..3071faffe2 100644 --- a/pkg/contexts/ocm/repositories/virtual/repository.go +++ b/pkg/contexts/ocm/repositories/virtual/repository.go @@ -10,7 +10,7 @@ import ( ) type RepositoryImpl struct { - base repocpi.RepositoryBase + base repocpi.RepositoryProxy ctx cpi.Context access Access nonref cpi.Repository @@ -30,7 +30,7 @@ func (r *RepositoryImpl) Close() error { return r.access.Close() } -func (r *RepositoryImpl) SetBase(base repocpi.RepositoryBase) { +func (r *RepositoryImpl) SetProxy(base repocpi.RepositoryProxy) { r.base = base r.nonref = repocpi.NewNoneRefRepositoryView(base) } From d82a4f1d010d20a59e39d4c70d4e157e85c0d833 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 28 Nov 2023 16:40:13 +0100 Subject: [PATCH 18/18] rename proxy to bridge --- pkg/contexts/ocm/cpi/repocpi/README.md | 77 +++++++++++++ pkg/contexts/ocm/cpi/repocpi/backend.go | 32 +++--- .../cpi/repocpi/{proxy_c.go => bridge_c.go} | 76 ++++++------- .../cpi/repocpi/{proxy_cv.go => bridge_cv.go} | 102 +++++++++--------- .../cpi/repocpi/{proxy_r.go => bridge_r.go} | 40 +++---- pkg/contexts/ocm/cpi/repocpi/doc.go | 19 ++-- .../ocm/cpi/repocpi/ocmimpllayers.png | Bin 0 -> 168725 bytes pkg/contexts/ocm/cpi/repocpi/view_c.go | 40 +++---- pkg/contexts/ocm/cpi/repocpi/view_cv.go | 82 +++++++------- pkg/contexts/ocm/cpi/repocpi/view_r.go | 42 ++++---- .../repositories/comparch/componentarchive.go | 6 +- .../ocm/repositories/comparch/repository.go | 20 ++-- .../repositories/genericocireg/component.go | 10 +- .../genericocireg/componentversion.go | 10 +- .../repositories/genericocireg/repository.go | 6 +- .../ocm/repositories/virtual/component.go | 10 +- .../repositories/virtual/componentversion.go | 10 +- .../ocm/repositories/virtual/repository.go | 6 +- 18 files changed, 333 insertions(+), 255 deletions(-) create mode 100644 pkg/contexts/ocm/cpi/repocpi/README.md rename pkg/contexts/ocm/cpi/repocpi/{proxy_c.go => bridge_c.go} (58%) rename pkg/contexts/ocm/cpi/repocpi/{proxy_cv.go => bridge_cv.go} (73%) rename pkg/contexts/ocm/cpi/repocpi/{proxy_r.go => bridge_r.go} (58%) create mode 100755 pkg/contexts/ocm/cpi/repocpi/ocmimpllayers.png diff --git a/pkg/contexts/ocm/cpi/repocpi/README.md b/pkg/contexts/ocm/cpi/repocpi/README.md new file mode 100644 index 0000000000..c97d5c00e9 --- /dev/null +++ b/pkg/contexts/ocm/cpi/repocpi/README.md @@ -0,0 +1,77 @@ +# Context Programming Interface for Repositories + +Package repocpi contains the implementation support + for repository backends. It offers three methods + to create component version, component and repository + objects based on three simple implementation interfaces. + + The basic provisioning model is layered: + + ![Implamentation Layers](ocmimpllayers.png) + + - on layer 1 there is the *user facing API* defined + in package [github.com/open-component-model/ocm/pkg/contexts/ocm]. + + - on layer 2 (this package) there is a backend agnostic + implementation of standard functionality based on layer 3. + This is divided into two parts + + a) the *view* objects provided by the `Dup()` calls of the layer 1 API. + All dups are internally based on a single base object. + These objects are called *bridge*. They act as base object + for the views and as abstraction for the implementation objects + providing *generic* implementations potentially based on + the implementation functionality. + (see bridge design pattern https://refactoring.guru/design-patterns/bridge) + + b) the *bridge* object as base for all dup views is used to implement some + common functionality like the view management. The bridge object + is closed, when the last view disappears. + This bridge object then calls the final + storage backend implementation interface. + + - the storage backend implementations based on the implementation + interfaces provided by layer 2. + + The implementation interfaces and the functions to create API objects are: + + - interface [ComponentVersionAccessImpl] is used to create an ocm.ComponentVersionAccess object + using the function [NewComponentVersionAccess]. + - interface [ComponentAccessImpl] is used to create an ocm.ComponentAccess object + using the function [NewComponentAccess]. + - interface [RepositoryImpl] is used to create an ocm.ComponentAccess object + using the function [NewRepository]. + + Component version implementations provide basic access to component versions + and their descriptors. They keep a reference to component implementations, which are + again based on repository implementations. The task of repository implementations is + to provide component objects. Their implementations are responsible to provide + component version objects. + +## Simplified Respository Implementation Interface + Besides this basic implementation interfaces with separated objects for a + repository, component and component version, there is support for a simplified + implementation interface (`StorageBackendImpl`). This is a single interface + bundling all required functionality to implement the objects for the three + concerned elements. With `NewStorageBackend` it is possible to instantiate + a new kind of repository based on this single interface. The required + objects for components and component versions are generically provided + based on the methods provided by this interface. + +## Comparison of Implementation Models + +The simplified implementation model does not provide access to the +implementation objects for components and component versions. +Therefore, it is not possible to keep state for those elements. + +Storage Backend Implementations requiring such state, like the OCI +implementation based on the OCI abstraction provided by the OCI +context, therefore use dedicated implementations for repository, +component and component version objects. This model provides +complete control over the lifecycle of those elements. + +If a storage backend implementation is stateless or just keeps +state at the repository level, the simplified implementation model +can be chosen. + + diff --git a/pkg/contexts/ocm/cpi/repocpi/backend.go b/pkg/contexts/ocm/cpi/repocpi/backend.go index de54b51aac..cbfeabee7b 100644 --- a/pkg/contexts/ocm/cpi/repocpi/backend.go +++ b/pkg/contexts/ocm/cpi/repocpi/backend.go @@ -52,7 +52,7 @@ type StorageBackendImpl interface { } type storageBackendRepository struct { - proxy RepositoryProxy + bridge RepositoryBridge closed atomic.Bool noref cpi.Repository kind string @@ -78,9 +78,9 @@ func NewStorageBackend(kind string, impl StorageBackendImpl) cpi.Repository { return NewRepository(backend, kind) } -func (s *storageBackendRepository) SetProxy(proxy RepositoryProxy) { - s.proxy = proxy - s.noref = NewNoneRefRepositoryView(proxy) +func (s *storageBackendRepository) SetBridge(bridge RepositoryBridge) { + s.bridge = bridge + s.noref = NewNoneRefRepositoryView(bridge) } func (s *storageBackendRepository) Close() error { @@ -124,19 +124,19 @@ func (s *storageBackendRepository) LookupComponent(name string) (*ComponentAcces //////////////////////////////////////////////////////////////////////////////// type storageBackendComponent struct { - proxy ComponentAccessProxy - repo *storageBackendRepository - name string + bridge ComponentAccessBridge + repo *storageBackendRepository + name string } var _ ComponentAccessImpl = (*storageBackendComponent)(nil) -func (s *storageBackendComponent) SetProxy(proxy ComponentAccessProxy) { - s.proxy = proxy +func (s *storageBackendComponent) SetBridge(bridge ComponentAccessBridge) { + s.bridge = bridge } -func (s *storageBackendComponent) GetParentProxy() RepositoryViewManager { - return s.repo.proxy +func (s *storageBackendComponent) GetParentBridge() RepositoryViewManager { + return s.repo.bridge } func (s *storageBackendComponent) Close() error { @@ -213,7 +213,7 @@ func (s *storageBackendComponent) NewVersion(version string, overwrite ...bool) //////////////////////////////////////////////////////////////////////////////// type storageBackendComponentVersion struct { - proxy ComponentVersionAccessProxy + bridge ComponentVersionAccessBridge comp *storageBackendComponent name common.NameVersion descriptor *compdesc.ComponentDescriptor @@ -229,12 +229,12 @@ func (s *storageBackendComponentVersion) GetContext() cpi.Context { return s.comp.repo.impl.GetContext() } -func (s *storageBackendComponentVersion) SetProxy(proxy ComponentVersionAccessProxy) { - s.proxy = proxy +func (s *storageBackendComponentVersion) SetBridge(bridge ComponentVersionAccessBridge) { + s.bridge = bridge } -func (s *storageBackendComponentVersion) GetParentProxy() ComponentAccessProxy { - return s.comp.proxy +func (s *storageBackendComponentVersion) GetParentBridge() ComponentAccessBridge { + return s.comp.bridge } func (s *storageBackendComponentVersion) Repository() cpi.Repository { diff --git a/pkg/contexts/ocm/cpi/repocpi/proxy_c.go b/pkg/contexts/ocm/cpi/repocpi/bridge_c.go similarity index 58% rename from pkg/contexts/ocm/cpi/repocpi/proxy_c.go rename to pkg/contexts/ocm/cpi/repocpi/bridge_c.go index 3e300a7ec8..c552832888 100644 --- a/pkg/contexts/ocm/cpi/repocpi/proxy_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/bridge_c.go @@ -27,8 +27,8 @@ type ComponentVersionAccessInfo struct { // ComponentAccessImpl is the provider implementation // interface for component versions. type ComponentAccessImpl interface { - SetProxy(proxy ComponentAccessProxy) - GetParentProxy() RepositoryViewManager + SetBridge(bridge ComponentAccessBridge) + GetParentBridge() RepositoryViewManager GetContext() cpi.Context GetName() string @@ -42,67 +42,67 @@ type ComponentAccessImpl interface { io.Closer } -type _componentAccessProxyBase = resource.ResourceImplBase[cpi.ComponentAccess] +type _componentAccessBridgeBase = resource.ResourceImplBase[cpi.ComponentAccess] -type componentAccessProxy struct { - *_componentAccessProxyBase +type componentAccessBridge struct { + *_componentAccessBridgeBase ctx cpi.Context name string impl ComponentAccessImpl } -func newComponentAccessProxy(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessProxy, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentAccess, cpi.Repository](impl.GetParentProxy(), closer...) +func newComponentAccessBridge(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessBridge, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentAccess, cpi.Repository](impl.GetParentBridge(), closer...) if err != nil { return nil, err } - b := &componentAccessProxy{ - _componentAccessProxyBase: base, - ctx: impl.GetContext(), - name: impl.GetName(), - impl: impl, + b := &componentAccessBridge{ + _componentAccessBridgeBase: base, + ctx: impl.GetContext(), + name: impl.GetName(), + impl: impl, } - impl.SetProxy(b) + impl.SetBridge(b) return b, nil } -func (b *componentAccessProxy) Close() error { +func (b *componentAccessBridge) Close() error { list := errors.ErrListf("closing component %s", b.name) - refmgmt.AllocLog.Trace("closing component proxy", "name", b.name) + refmgmt.AllocLog.Trace("closing component bridge", "name", b.name) list.Add(b.impl.Close()) - list.Add(b._componentAccessProxyBase.Close()) - refmgmt.AllocLog.Trace("closed component proxy", "name", b.name) + list.Add(b._componentAccessBridgeBase.Close()) + refmgmt.AllocLog.Trace("closed component bridge", "name", b.name) return list.Result() } -func (b *componentAccessProxy) GetContext() cpi.Context { +func (b *componentAccessBridge) GetContext() cpi.Context { return b.ctx } -func (b *componentAccessProxy) GetName() string { +func (b *componentAccessBridge) GetName() string { return b.name } -func (b *componentAccessProxy) IsReadOnly() bool { +func (b *componentAccessBridge) IsReadOnly() bool { return b.impl.IsReadOnly() } -func (c *componentAccessProxy) IsOwned(cv cpi.ComponentVersionAccess) bool { - proxy, err := GetComponentVersionAccessProxy(cv) +func (c *componentAccessBridge) IsOwned(cv cpi.ComponentVersionAccess) bool { + bridge, err := GetComponentVersionAccessBridge(cv) if err != nil { return false } - impl := proxy.(*componentVersionAccessProxy).impl - cvcompmgr := impl.GetParentProxy() + impl := bridge.(*componentVersionAccessBridge).impl + cvcompmgr := impl.GetParentBridge() return c == cvcompmgr } -func (b *componentAccessProxy) ListVersions() ([]string, error) { +func (b *componentAccessBridge) ListVersions() ([]string, error) { return b.impl.ListVersions() } -func (b *componentAccessProxy) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { +func (b *componentAccessBridge) LookupVersion(version string) (cpi.ComponentVersionAccess, error) { i, err := b.impl.LookupVersion(version) if err != nil { return nil, err @@ -113,11 +113,11 @@ func (b *componentAccessProxy) LookupVersion(version string) (cpi.ComponentVersi return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, i.Persistent, !compositionmodeattr.Get(b.GetContext())) } -func (b *componentAccessProxy) HasVersion(vers string) (bool, error) { +func (b *componentAccessBridge) HasVersion(vers string) (bool, error) { return b.impl.HasVersion(vers) } -func (b *componentAccessProxy) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { +func (b *componentAccessBridge) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) { i, err := b.impl.NewVersion(version, overrides...) if err != nil { return nil, err @@ -128,11 +128,11 @@ func (b *componentAccessProxy) NewVersion(version string, overrides ...bool) (cp return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, false, !compositionmodeattr.Get(b.GetContext())) } -func (c *componentAccessProxy) AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) { +func (c *componentAccessBridge) AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) { var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) - cvproxy, err := GetComponentVersionAccessProxy(cv) + cvbridge, err := GetComponentVersionAccessBridge(cv) if err != nil { return err } @@ -146,7 +146,7 @@ func (c *componentAccessProxy) AddVersion(cv cpi.ComponentVersionAccess, opts *c finalize.With(func() error { return eff.Close() }) - cvproxy, err = GetComponentVersionAccessProxy(eff) + cvbridge, err = GetComponentVersionAccessBridge(eff) if err != nil { return err } @@ -154,20 +154,20 @@ func (c *componentAccessProxy) AddVersion(cv cpi.ComponentVersionAccess, opts *c d := eff.GetDescriptor() *d = *cv.GetDescriptor().Copy() - err = c.setupLocalBlobs("resource", cv, cvproxy, d.Resources, &opts.BlobUploadOptions) + err = c.setupLocalBlobs("resource", cv, cvbridge, d.Resources, &opts.BlobUploadOptions) if err == nil { - err = c.setupLocalBlobs("source", cv, cvproxy, d.Sources, &opts.BlobUploadOptions) + err = c.setupLocalBlobs("source", cv, cvbridge, d.Sources, &opts.BlobUploadOptions) } if err != nil { return err } } - cvproxy.EnablePersistence() - err = cvproxy.Update(!cvproxy.UseDirectAccess()) + cvbridge.EnablePersistence() + err = cvbridge.Update(!cvbridge.UseDirectAccess()) return err } -func (c *componentAccessProxy) setupLocalBlobs(kind string, src cpi.ComponentVersionAccess, tgtproxy ComponentVersionAccessProxy, it compdesc.ArtifactAccessor, opts *cpi.BlobUploadOptions) (ferr error) { +func (c *componentAccessBridge) setupLocalBlobs(kind string, src cpi.ComponentVersionAccess, tgtbridge ComponentVersionAccessBridge, it compdesc.ArtifactAccessor, opts *cpi.BlobUploadOptions) (ferr error) { ctx := src.GetContext() // transfer all local blobs prov := func(spec cpi.AccessSpec) (blob blobaccess.BlobAccess, ref string, global cpi.AccessSpec, err error) { @@ -176,10 +176,10 @@ func (c *componentAccessProxy) setupLocalBlobs(kind string, src cpi.ComponentVer if err != nil { return nil, "", nil, err } - return m.AsBlobAccess(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, tgtproxy.GetContext()), nil + return m.AsBlobAccess(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, tgtbridge.GetContext()), nil } return nil, "", nil, nil } - return tgtproxy.(*componentVersionAccessProxy).setupLocalBlobs(kind, prov, it, false, opts) + return tgtbridge.(*componentVersionAccessBridge).setupLocalBlobs(kind, prov, it, false, opts) } diff --git a/pkg/contexts/ocm/cpi/repocpi/proxy_cv.go b/pkg/contexts/ocm/cpi/repocpi/bridge_cv.go similarity index 73% rename from pkg/contexts/ocm/cpi/repocpi/proxy_cv.go rename to pkg/contexts/ocm/cpi/repocpi/bridge_cv.go index ea2d56d03a..95bdc20a47 100644 --- a/pkg/contexts/ocm/cpi/repocpi/proxy_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/bridge_cv.go @@ -35,8 +35,8 @@ import ( // interface for component versions. type ComponentVersionAccessImpl interface { GetContext() cpi.Context - SetProxy(proxy ComponentVersionAccessProxy) - GetParentProxy() ComponentAccessProxy + SetBridge(bridge ComponentVersionAccessBridge) + GetParentBridge() ComponentAccessBridge Repository() cpi.Repository @@ -52,17 +52,17 @@ type ComponentVersionAccessImpl interface { io.Closer } -type _componentVersionAccessProxyBase = resource.ResourceImplBase[cpi.ComponentVersionAccess] +type _componentVersionAccessBridgeBase = resource.ResourceImplBase[cpi.ComponentVersionAccess] -// componentVersionAccessProxy is the counterpart to views, all views +// componentVersionAccessBridge is the counterpart to views, all views // created by Dup calls use this base object to work on. // Besides some functionality covered by view objects these base objects // implement provider-agnostic parts of the ComponentVersionAccess API. -type componentVersionAccessProxy struct { +type componentVersionAccessBridge struct { lock sync.Mutex id finalizer.ObjectIdentity - *_componentVersionAccessProxyBase + *_componentVersionAccessBridgeBase ctx cpi.Context name string version string @@ -78,37 +78,37 @@ type componentVersionAccessProxy struct { impl ComponentVersionAccessImpl } -var _ ComponentVersionAccessProxy = (*componentVersionAccessProxy)(nil) +var _ ComponentVersionAccessBridge = (*componentVersionAccessBridge)(nil) -func newComponentVersionAccessProxy(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessProxy, error) { - base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess, cpi.ComponentAccess](impl.GetParentProxy(), closer...) +func newComponentVersionAccessBridge(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBridge, error) { + base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess, cpi.ComponentAccess](impl.GetParentBridge(), closer...) if err != nil { return nil, err } - b := &componentVersionAccessProxy{ - _componentVersionAccessProxyBase: base, - id: finalizer.NewObjectIdentity(fmt.Sprintf("%s:%s", name, version)), - ctx: impl.GetContext(), - name: name, - version: version, - blobcache: NewBlobCache(), - lazy: lazy, - persistent: persistent, - directAccess: direct, - impl: impl, - } - impl.SetProxy(b) + b := &componentVersionAccessBridge{ + _componentVersionAccessBridgeBase: base, + id: finalizer.NewObjectIdentity(fmt.Sprintf("%s:%s", name, version)), + ctx: impl.GetContext(), + name: name, + version: version, + blobcache: NewBlobCache(), + lazy: lazy, + persistent: persistent, + directAccess: direct, + impl: impl, + } + impl.SetBridge(b) return b, nil } func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersionAccess) (T, error) { var _nil T - impl, err := GetComponentVersionAccessProxy(cv) + impl, err := GetComponentVersionAccessBridge(cv) if err != nil { return _nil, err } - if mine, ok := impl.(*componentVersionAccessProxy); ok { + if mine, ok := impl.(*componentVersionAccessBridge); ok { cont, ok := mine.impl.(T) if ok { return cont, nil @@ -118,7 +118,7 @@ func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersi return _nil, errors.Newf("non-matching component version implementation %T", impl) } -func (b *componentVersionAccessProxy) Close() error { +func (b *componentVersionAccessBridge) Close() error { list := errors.ErrListf("closing component version %s", common.VersionedElementKey(b)) refmgmt.AllocLog.Trace("closing component version base", "name", common.VersionedElementKey(b)) // prepare artifact access for final close in @@ -127,33 +127,33 @@ func (b *componentVersionAccessProxy) Close() error { list.Add(b.update(true)) } list.Add(b.impl.Close()) - list.Add(b._componentVersionAccessProxyBase.Close()) + list.Add(b._componentVersionAccessBridgeBase.Close()) list.Add(b.blobcache.Clear()) refmgmt.AllocLog.Trace("closed component version base", "name", common.VersionedElementKey(b)) return list.Result() } -func (b *componentVersionAccessProxy) GetContext() cpi.Context { +func (b *componentVersionAccessBridge) GetContext() cpi.Context { return b.ctx } -func (b *componentVersionAccessProxy) GetName() string { +func (b *componentVersionAccessBridge) GetName() string { return b.name } -func (b *componentVersionAccessProxy) GetVersion() string { +func (b *componentVersionAccessBridge) GetVersion() string { return b.version } -func (b *componentVersionAccessProxy) GetImplementation() ComponentVersionAccessImpl { +func (b *componentVersionAccessBridge) GetImplementation() ComponentVersionAccessImpl { return b.impl } -func (b *componentVersionAccessProxy) GetBlobCache() BlobCache { +func (b *componentVersionAccessBridge) GetBlobCache() BlobCache { return b.blobcache } -func (b *componentVersionAccessProxy) EnablePersistence() bool { +func (b *componentVersionAccessBridge) EnablePersistence() bool { if b.discardChanges { return false } @@ -162,30 +162,30 @@ func (b *componentVersionAccessProxy) EnablePersistence() bool { return true } -func (b *componentVersionAccessProxy) IsPersistent() bool { +func (b *componentVersionAccessBridge) IsPersistent() bool { return b.persistent } -func (b *componentVersionAccessProxy) UseDirectAccess() bool { +func (b *componentVersionAccessBridge) UseDirectAccess() bool { return b.directAccess } -func (b *componentVersionAccessProxy) DiscardChanges() { +func (b *componentVersionAccessBridge) DiscardChanges() { b.discardChanges = true } -func (b *componentVersionAccessProxy) Repository() cpi.Repository { +func (b *componentVersionAccessBridge) Repository() cpi.Repository { return b.impl.Repository() } -func (b *componentVersionAccessProxy) IsReadOnly() bool { +func (b *componentVersionAccessBridge) IsReadOnly() bool { return b.impl.IsReadOnly() } //////////////////////////////////////////////////////////////////////////////// // with access to actual view -func (b *componentVersionAccessProxy) AccessMethod(spec cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (meth cpi.AccessMethod, err error) { +func (b *componentVersionAccessBridge) AccessMethod(spec cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (meth cpi.AccessMethod, err error) { switch { case compose.Is(spec): cspec, ok := spec.(*compose.AccessSpec) @@ -208,36 +208,36 @@ func (b *componentVersionAccessProxy) AccessMethod(spec cpi.AccessSpec, cv refmg return meth, err } -func (b *componentVersionAccessProxy) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { +func (b *componentVersionAccessBridge) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string { return b.impl.GetInexpensiveContentVersionIdentity(acc, cv) } -func (b *componentVersionAccessProxy) GetDescriptor() *compdesc.ComponentDescriptor { +func (b *componentVersionAccessBridge) GetDescriptor() *compdesc.ComponentDescriptor { b.lock.Lock() defer b.lock.Unlock() return b.getDescriptor() } -func (b *componentVersionAccessProxy) getDescriptor() *compdesc.ComponentDescriptor { +func (b *componentVersionAccessBridge) getDescriptor() *compdesc.ComponentDescriptor { if b.descriptor == nil { b.descriptor = b.impl.GetDescriptor() } return b.descriptor } -func (b *componentVersionAccessProxy) GetStorageContext() cpi.StorageContext { +func (b *componentVersionAccessBridge) GetStorageContext() cpi.StorageContext { return b.impl.GetStorageContext() } -func (b *componentVersionAccessProxy) ShouldUpdate(final bool) bool { +func (b *componentVersionAccessBridge) ShouldUpdate(final bool) bool { b.lock.Lock() defer b.lock.Unlock() return b.shouldUpdate(final) } -func (b *componentVersionAccessProxy) shouldUpdate(final bool) bool { +func (b *componentVersionAccessBridge) shouldUpdate(final bool) bool { if b.discardChanges { return false } @@ -247,14 +247,14 @@ func (b *componentVersionAccessProxy) shouldUpdate(final bool) bool { return !b.lazy && b.directAccess && b.persistent } -func (b *componentVersionAccessProxy) Update(final bool) error { +func (b *componentVersionAccessBridge) Update(final bool) error { b.lock.Lock() defer b.lock.Unlock() return b.update(final) } -func (b *componentVersionAccessProxy) update(final bool) error { +func (b *componentVersionAccessBridge) update(final bool) error { if !b.shouldUpdate(final) { return nil } @@ -280,7 +280,7 @@ func (b *componentVersionAccessProxy) update(final bool) error { return err } -func (b *componentVersionAccessProxy) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { +func (b *componentVersionAccessBridge) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess { key, err := json.Marshal(acc) if err != nil { return nil @@ -288,7 +288,7 @@ func (b *componentVersionAccessProxy) getLocalBlob(acc cpi.AccessSpec) cpi.BlobA return b.blobcache.GetBlobFor(string(key)) } -func (b *componentVersionAccessProxy) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { +func (b *componentVersionAccessBridge) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) { if blob == nil { return nil, errors.New("a resource has to be defined") } @@ -349,7 +349,7 @@ func (b *componentVersionAccessProxy) AddBlob(blob cpi.BlobAccess, artType, refN return b.cacheLocalBlob(acc, blob) } -func (b *componentVersionAccessProxy) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { +func (b *componentVersionAccessBridge) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) { key, err := json.Marshal(acc) if err != nil { return nil, errors.Wrapf(err, "cannot marshal access spec") @@ -377,7 +377,7 @@ func (b *componentVersionAccessProxy) cacheLocalBlob(acc cpi.AccessSpec, blob cp //////////////////////////////////////////////////////////////////////////////// -func (b *componentVersionAccessProxy) composeAccess(spec cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error) { +func (b *componentVersionAccessBridge) composeAccess(spec cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error) { if !compose.Is(spec) { return nil, "", nil, nil } @@ -397,7 +397,7 @@ func (b *componentVersionAccessProxy) composeAccess(spec cpi.AccessSpec) (blobac return blob, cspec.ReferenceName, cspec.GlobalAccess.Get(), nil } -func (b *componentVersionAccessProxy) setupLocalBlobs(kind string, accprov func(cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error), it compdesc.ArtifactAccessor, final bool, opts *cpi.BlobUploadOptions) (ferr error) { +func (b *componentVersionAccessBridge) setupLocalBlobs(kind string, accprov func(cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error), it compdesc.ArtifactAccessor, final bool, opts *cpi.BlobUploadOptions) (ferr error) { var finalize finalizer.Finalizer defer finalize.FinalizeWithErrorPropagation(&ferr) diff --git a/pkg/contexts/ocm/cpi/repocpi/proxy_r.go b/pkg/contexts/ocm/cpi/repocpi/bridge_r.go similarity index 58% rename from pkg/contexts/ocm/cpi/repocpi/proxy_r.go rename to pkg/contexts/ocm/cpi/repocpi/bridge_r.go index 1adae9295b..b2d6f093dc 100644 --- a/pkg/contexts/ocm/cpi/repocpi/proxy_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/bridge_r.go @@ -20,7 +20,7 @@ type ComponentAccessInfo struct { } type RepositoryImpl interface { - SetProxy(proxy RepositoryProxy) + SetBridge(bridge RepositoryBridge) GetContext() cpi.Context @@ -33,52 +33,52 @@ type RepositoryImpl interface { io.Closer } -type _repositoryProxyBase = resource.ResourceImplBase[cpi.Repository] +type _repositoryBridgeBase = resource.ResourceImplBase[cpi.Repository] -type repositoryProxy struct { - *_repositoryProxyBase +type repositoryBridge struct { + *_repositoryBridgeBase ctx cpi.Context kind string impl RepositoryImpl } -func newRepositoryProxy(impl RepositoryImpl, kind string, closer ...io.Closer) RepositoryProxy { +func newRepositoryBridge(impl RepositoryImpl, kind string, closer ...io.Closer) RepositoryBridge { base := resource.NewSimpleResourceImplBase[cpi.Repository](closer...) - b := &repositoryProxy{ - _repositoryProxyBase: base, - ctx: impl.GetContext(), - impl: impl, + b := &repositoryBridge{ + _repositoryBridgeBase: base, + ctx: impl.GetContext(), + impl: impl, } - impl.SetProxy(b) + impl.SetBridge(b) return b } -func (b *repositoryProxy) Close() error { +func (b *repositoryBridge) Close() error { list := errors.ErrListf("closing %s", b.kind) - refmgmt.AllocLog.Trace("closing repository proxy", "kind", b.kind) + refmgmt.AllocLog.Trace("closing repository bridge", "kind", b.kind) list.Add(b.impl.Close()) - list.Add(b._repositoryProxyBase.Close()) - refmgmt.AllocLog.Trace("closed repository proxy", "kind", b.kind) + list.Add(b._repositoryBridgeBase.Close()) + refmgmt.AllocLog.Trace("closed repository bridge", "kind", b.kind) return list.Result() } -func (b *repositoryProxy) GetContext() cpi.Context { +func (b *repositoryBridge) GetContext() cpi.Context { return b.ctx } -func (b *repositoryProxy) GetSpecification() cpi.RepositorySpec { +func (b *repositoryBridge) GetSpecification() cpi.RepositorySpec { return b.impl.GetSpecification() } -func (b *repositoryProxy) ComponentLister() cpi.ComponentLister { +func (b *repositoryBridge) ComponentLister() cpi.ComponentLister { return b.impl.ComponentLister() } -func (b *repositoryProxy) ExistsComponentVersion(name string, version string) (bool, error) { +func (b *repositoryBridge) ExistsComponentVersion(name string, version string) (bool, error) { return b.impl.ExistsComponentVersion(name, version) } -func (b *repositoryProxy) LookupComponentVersion(name string, version string) (cv cpi.ComponentVersionAccess, rerr error) { +func (b *repositoryBridge) LookupComponentVersion(name string, version string) (cv cpi.ComponentVersionAccess, rerr error) { c, err := b.LookupComponent(name) if err != nil { return nil, err @@ -88,7 +88,7 @@ func (b *repositoryProxy) LookupComponentVersion(name string, version string) (c return c.LookupVersion(version) } -func (b *repositoryProxy) LookupComponent(name string) (cpi.ComponentAccess, error) { +func (b *repositoryBridge) LookupComponent(name string) (cpi.ComponentAccess, error) { i, err := b.impl.LookupComponent(name) if err != nil { return nil, err diff --git a/pkg/contexts/ocm/cpi/repocpi/doc.go b/pkg/contexts/ocm/cpi/repocpi/doc.go index 4b7f92a21f..017ad14993 100644 --- a/pkg/contexts/ocm/cpi/repocpi/doc.go +++ b/pkg/contexts/ocm/cpi/repocpi/doc.go @@ -18,15 +18,16 @@ // // a) the view objects provided by the Dup() calls of the layer 1 API. // All dups are internally based on a single base object. -// These objects are called proxy. They act as base object -// for the views and as wrapper for the implementation objects +// These objects are called bridge. They act as base object +// for the views and as abstraction for the implementation objects // providing generic implementations potentially based on // the implementation functionality. +// (see bridge design pattern https://refactoring.guru/design-patterns/bridge) // -// b) the base object for all dup views is used to implement some -// common functionality like the view management. The base object +// b) the bridge object as base for all dup views is used to implement some +// common functionality like the view management. The bridge object // is closed, when the last view disappears. -// This base object the uses forwards calls to the final +// This bridge object then calls the final // storage backend implementation interface. // // - the storage backend implementations based on the implementation @@ -42,15 +43,15 @@ // using the function [NewRepository]. // // Component version implementations provide basic access to component versions -// and their descriptors. The keep a reference to component implementations, which are -// again based of repository implementations. The task of repository implementations is -// to provide component objects. Their implementations are the responsible to provide +// and their descriptors. They keep a reference to component implementations, which are +// again based on repository implementations. The task of repository implementations is +// to provide component objects. Their implementations are responsible to provide // component version objects. // // Besides this basic implementation interface with separated object for a // repository, component and component version, there is support for a simplified // implementation interface (StorageBackendImpl). This is a single interface -// bundling all required functionality to implement the object for the three +// bundling all required functionality to implement the objects for the three // concerned elements. With NewStorageBackend it is possible to instantiate // a new kind of repository based on this single interface. The required // objects for components and component versions are generically provided diff --git a/pkg/contexts/ocm/cpi/repocpi/ocmimpllayers.png b/pkg/contexts/ocm/cpi/repocpi/ocmimpllayers.png new file mode 100755 index 0000000000000000000000000000000000000000..d0fc75645f752807259a8fddbb7b4bc3de1751a2 GIT binary patch literal 168725 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 Nk~ovxx1|?D#uqY= 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� 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 literal 0 HcmV?d00001 diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go index f393faa827..fa6678c1a7 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_c.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go @@ -21,7 +21,7 @@ type _componentAccessView interface { type ComponentAccessViewManager = resource.ViewManager[cpi.ComponentAccess] // here you have to use an alias -type ComponentAccessProxy interface { +type ComponentAccessBridge interface { resource.ResourceImplementation[cpi.ComponentAccess] GetContext() cpi.Context @@ -41,7 +41,7 @@ type ComponentAccessProxy interface { type componentAccessView struct { _componentAccessView - proxy ComponentAccessProxy + bridge ComponentAccessBridge } var ( @@ -49,57 +49,57 @@ var ( _ utils.Unwrappable = (*componentAccessView)(nil) ) -func GetComponentAccessProxy(n cpi.ComponentAccess) (ComponentAccessProxy, error) { +func GetComponentAccessBridge(n cpi.ComponentAccess) (ComponentAccessBridge, error) { if v, ok := n.(*componentAccessView); ok { - return v.proxy, nil + return v.bridge, nil } return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", n)) } func GetComponentAccessImplementation(n cpi.ComponentAccess) (ComponentAccessImpl, error) { if v, ok := n.(*componentAccessView); ok { - if b, ok := v.proxy.(*componentAccessProxy); ok { + if b, ok := v.bridge.(*componentAccessBridge); ok { return b.impl, nil } - return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", v.proxy)) + return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", v.bridge)) } return nil, errors.ErrNotSupported("component implementation type", fmt.Sprintf("%T", n)) } -func componentAccessViewCreator(i ComponentAccessProxy, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess { +func componentAccessViewCreator(i ComponentAccessBridge, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess { return &componentAccessView{ _componentAccessView: resource.NewView[cpi.ComponentAccess](v, d), - proxy: i, + bridge: i, } } func NewComponentAccess(impl ComponentAccessImpl, kind string, main bool, closer ...io.Closer) (cpi.ComponentAccess, error) { - proxy, err := newComponentAccessProxy(impl, closer...) + bridge, err := newComponentAccessBridge(impl, closer...) if err != nil { return nil, errors.Join(err, impl.Close()) } if kind == "" { kind = "component" } - cv := resource.NewResource[cpi.ComponentAccess](proxy, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), main) + cv := resource.NewResource[cpi.ComponentAccess](bridge, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), main) return cv, nil } func (c *componentAccessView) Unwrap() interface{} { - return c.proxy + return c.bridge } func (c *componentAccessView) GetContext() cpi.Context { - return c.proxy.GetContext() + return c.bridge.GetContext() } func (c *componentAccessView) GetName() string { - return c.proxy.GetName() + return c.bridge.GetName() } func (c *componentAccessView) ListVersions() (list []string, err error) { err = c.Execute(func() error { - list, err = c.proxy.ListVersions() + list, err = c.bridge.ListVersions() return err }) return list, err @@ -107,7 +107,7 @@ func (c *componentAccessView) ListVersions() (list []string, err error) { func (c *componentAccessView) LookupVersion(version string) (acc cpi.ComponentVersionAccess, err error) { err = c.Execute(func() error { - acc, err = c.proxy.LookupVersion(version) + acc, err = c.bridge.LookupVersion(version) return err }) return acc, err @@ -119,7 +119,7 @@ func (c *componentAccessView) AddVersion(acc cpi.ComponentVersionAccess, overwri } return c.Execute(func() error { - return c.proxy.AddVersion(acc, cpi.NewAddVersionOptions(cpi.Overwrite(utils.Optional(overwrite...)))) + return c.bridge.AddVersion(acc, cpi.NewAddVersionOptions(cpi.Overwrite(utils.Optional(overwrite...)))) }) } @@ -128,16 +128,16 @@ func (c *componentAccessView) AddVersionOpt(acc cpi.ComponentVersionAccess, opts return errors.ErrInvalid("component name", acc.GetName()) } return c.Execute(func() error { - return c.proxy.AddVersion(acc, cpi.NewAddVersionOptions(opts...)) + return c.bridge.AddVersion(acc, cpi.NewAddVersionOptions(opts...)) }) } func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc cpi.ComponentVersionAccess, err error) { err = c.Execute(func() error { - if c.proxy.IsReadOnly() { + if c.bridge.IsReadOnly() { return accessio.ErrReadOnly } - acc, err = c.proxy.NewVersion(version, overrides...) + acc, err = c.bridge.NewVersion(version, overrides...) return err }) return acc, err @@ -145,7 +145,7 @@ func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc func (c *componentAccessView) HasVersion(vers string) (ok bool, err error) { err = c.Execute(func() error { - ok, err = c.proxy.HasVersion(vers) + ok, err = c.bridge.HasVersion(vers) return err }) return ok, err diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go index d196b97885..f6c131f989 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_cv.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go @@ -41,7 +41,7 @@ type _componentVersionAccessView interface { type ComponentVersionAccessViewManager = resource.ViewManager[cpi.ComponentVersionAccess] -type ComponentVersionAccessProxy interface { +type ComponentVersionAccessBridge interface { resource.ResourceImplementation[cpi.ComponentVersionAccess] common.VersionedElement io.Closer @@ -96,8 +96,8 @@ type ComponentVersionAccessProxy interface { type componentVersionAccessView struct { _componentVersionAccessView - proxy ComponentVersionAccessProxy - err error + bridge ComponentVersionAccessBridge + err error } var ( @@ -105,41 +105,41 @@ var ( _ utils.Unwrappable = (*componentVersionAccessView)(nil) ) -func GetComponentVersionAccessProxy(n cpi.ComponentVersionAccess) (ComponentVersionAccessProxy, error) { +func GetComponentVersionAccessBridge(n cpi.ComponentVersionAccess) (ComponentVersionAccessBridge, error) { if v, ok := n.(*componentVersionAccessView); ok { - return v.proxy, nil + return v.bridge, nil } - return nil, errors.ErrNotSupported("component version proxy type", fmt.Sprintf("%T", n)) + return nil, errors.ErrNotSupported("component version bridge type", fmt.Sprintf("%T", n)) } func GetComponentVersionAccessImplementation(n cpi.ComponentVersionAccess) (ComponentVersionAccessImpl, error) { if v, ok := n.(*componentVersionAccessView); ok { - if b, ok := v.proxy.(*componentVersionAccessProxy); ok { + if b, ok := v.bridge.(*componentVersionAccessBridge); ok { return b.impl, nil } - return nil, errors.ErrNotSupported("component version proxy type", fmt.Sprintf("%T", v.proxy)) + return nil, errors.ErrNotSupported("component version bridge type", fmt.Sprintf("%T", v.bridge)) } return nil, errors.ErrNotSupported("component version implementation type", fmt.Sprintf("%T", n)) } -func artifactAccessViewCreator(i ComponentVersionAccessProxy, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess { +func artifactAccessViewCreator(i ComponentVersionAccessBridge, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess { cv := &componentVersionAccessView{ _componentVersionAccessView: resource.NewView[cpi.ComponentVersionAccess](v, d), - proxy: i, + bridge: i, } return cv } func NewComponentVersionAccess(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (cpi.ComponentVersionAccess, error) { - proxy, err := newComponentVersionAccessProxy(name, version, impl, lazy, persistent, direct, closer...) + bridge, err := newComponentVersionAccessBridge(name, version, impl, lazy, persistent, direct, closer...) if err != nil { return nil, errors.Join(err, impl.Close()) } - return resource.NewResource[cpi.ComponentVersionAccess](proxy, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", name, version), true), nil + return resource.NewResource[cpi.ComponentVersionAccess](bridge, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", name, version), true), nil } func (c *componentVersionAccessView) Unwrap() interface{} { - return c.proxy + return c.bridge } func (c *componentVersionAccessView) Close() error { @@ -149,23 +149,23 @@ func (c *componentVersionAccessView) Close() error { } func (c *componentVersionAccessView) Repository() cpi.Repository { - return c.proxy.Repository() + return c.bridge.Repository() } func (c *componentVersionAccessView) GetContext() internal.Context { - return c.proxy.GetContext() + return c.bridge.GetContext() } func (c *componentVersionAccessView) GetName() string { - return c.proxy.GetName() + return c.bridge.GetName() } func (c *componentVersionAccessView) GetVersion() string { - return c.proxy.GetVersion() + return c.bridge.GetVersion() } func (c *componentVersionAccessView) GetDescriptor() *compdesc.ComponentDescriptor { - return c.proxy.GetDescriptor() + return c.bridge.GetDescriptor() } func (c *componentVersionAccessView) GetProvider() *compdesc.Provider { @@ -195,7 +195,7 @@ func (c *componentVersionAccessView) AccessMethod(spec cpi.AccessSpec) (meth cpi func (c *componentVersionAccessView) accessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) { switch { case spec.IsLocal(c.GetContext()): - return c.proxy.AccessMethod(spec, c.Allocatable()) + return c.bridge.AccessMethod(spec, c.Allocatable()) default: return spec.AccessMethod(c) } @@ -225,16 +225,16 @@ func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec c // fall back to original version return spec.GetInexpensiveContentVersionIdentity(c) default: - return c.proxy.GetInexpensiveContentVersionIdentity(spec, c.Allocatable()) + return c.bridge.GetInexpensiveContentVersionIdentity(spec, c.Allocatable()) } } func (c *componentVersionAccessView) Update() error { return c.Execute(func() error { - if !c.proxy.IsPersistent() { + if !c.bridge.IsPersistent() { return ErrTempVersion } - return c.proxy.Update(true) + return c.bridge.Update(true) }) } @@ -243,7 +243,7 @@ func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refNa eff := cpi.NewBlobUploadOptions(opts...) err := c.Execute(func() error { var err error - spec, err = c.proxy.AddBlob(blob, artType, refName, global, false, eff) + spec, err = c.bridge.AddBlob(blob, artType, refName, global, false, eff) return err }) @@ -306,7 +306,7 @@ func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessVie set func(*T, compdesc.AccessSpec) error, setblob func(*T, cpi.BlobAccess, string, cpi.AccessSpec) error, ) error { - if c.proxy.IsReadOnly() { + if c.bridge.IsReadOnly() { return accessio.ErrReadOnly } meta := art.Meta() @@ -366,7 +366,7 @@ func (c *componentVersionAccessView) SetResourceAccess(art cpi.ResourceAccess, m } func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, acc compdesc.AccessSpec, modopts ...cpi.ModificationOption) error { - if c.proxy.IsReadOnly() { + if c.bridge.IsReadOnly() { return accessio.ErrReadOnly } @@ -375,11 +375,11 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac Access: acc, } - ctx := c.proxy.GetContext() + ctx := c.bridge.GetContext() opts := internal.NewModificationOptions(modopts...) cpi.CompleteModificationOptions(ctx, opts) - spec, err := c.proxy.GetContext().AccessSpecForSpec(acc) + spec, err := c.bridge.GetContext().AccessSpecForSpec(acc) if err != nil { return err } @@ -403,14 +403,14 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } } - cd := c.proxy.GetDescriptor() + cd := c.bridge.GetDescriptor() idx := cd.GetResourceIndex(&res.ResourceMeta) if idx >= 0 { old = &cd.Resources[idx] } if old == nil { - if !opts.IsModifyResource() && c.proxy.IsPersistent() { + if !opts.IsModifyResource() && c.bridge.IsPersistent() { return fmt.Errorf("new resource would invalidate signature") } } @@ -456,7 +456,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac if old != nil { eq := res.Equivalent(old) - if !eq.IsLocalHashEqual() && c.proxy.IsPersistent() { + if !eq.IsLocalHashEqual() && c.bridge.IsPersistent() { if !opts.IsModifyResource() { return fmt.Errorf("resource would invalidate signature") } @@ -469,7 +469,7 @@ func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, ac } else { cd.Resources[idx] = *res } - return c.proxy.Update(false) + return c.bridge.Update(false) }) } @@ -500,7 +500,7 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R if !old.Digest.IsNone() { digester.HashAlgorithm = old.Digest.HashAlgorithm digester.NormalizationAlgorithm = old.Digest.NormalisationAlgorithm - if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.proxy.IsPersistent() { + if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.bridge.IsPersistent() { res.Digest = old.Digest value = old.Digest.Value } @@ -515,7 +515,7 @@ func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess) err } func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error { - if c.proxy.IsReadOnly() { + if c.bridge.IsReadOnly() { return accessio.ErrReadOnly } @@ -525,40 +525,40 @@ func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdes } return c.Execute(func() error { if res.Version == "" { - res.Version = c.proxy.GetVersion() + res.Version = c.bridge.GetVersion() } - cd := c.proxy.GetDescriptor() + cd := c.bridge.GetDescriptor() if idx := cd.GetSourceIndex(&res.SourceMeta); idx == -1 { cd.Sources = append(cd.Sources, *res) } else { cd.Sources[idx] = *res } - return c.proxy.Update(false) + return c.bridge.Update(false) }) } func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference) error { return c.Execute(func() error { - cd := c.proxy.GetDescriptor() + cd := c.bridge.GetDescriptor() if idx := cd.GetComponentReferenceIndex(*ref); idx == -1 { cd.References = append(cd.References, *ref) } else { cd.References[idx] = *ref } - return c.proxy.Update(false) + return c.bridge.Update(false) }) } func (c *componentVersionAccessView) DiscardChanges() { - c.proxy.DiscardChanges() + c.bridge.DiscardChanges() } func (c *componentVersionAccessView) IsPersistent() bool { - return c.proxy.IsPersistent() + return c.bridge.IsPersistent() } func (c *componentVersionAccessView) UseDirectAccess() bool { - return c.proxy.UseDirectAccess() + return c.bridge.UseDirectAccess() } //////////////////////////////////////////////////////////////////////////////// diff --git a/pkg/contexts/ocm/cpi/repocpi/view_r.go b/pkg/contexts/ocm/cpi/repocpi/view_r.go index f7073a3925..3c94f10fb6 100644 --- a/pkg/contexts/ocm/cpi/repocpi/view_r.go +++ b/pkg/contexts/ocm/cpi/repocpi/view_r.go @@ -30,7 +30,7 @@ type _repositoryView interface { type RepositoryViewManager = resource.ViewManager[cpi.Repository] // here you have to use an alias -type RepositoryProxy interface { +type RepositoryBridge interface { resource.ResourceImplementation[cpi.Repository] GetContext() cpi.Context @@ -47,7 +47,7 @@ type RepositoryProxy interface { type repositoryView struct { _repositoryView - proxy RepositoryProxy + bridge RepositoryBridge } var ( @@ -56,74 +56,74 @@ var ( _ utils.Unwrappable = (*repositoryView)(nil) ) -func GetRepositoryProxy(n cpi.Repository) (RepositoryProxy, error) { +func GetRepositoryBridge(n cpi.Repository) (RepositoryBridge, error) { if v, ok := n.(*repositoryView); ok { - return v.proxy, nil + return v.bridge, nil } return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) } func GetRepositoryImplementation(n cpi.Repository) (RepositoryImpl, error) { if v, ok := n.(*repositoryView); ok { - if b, ok := v.proxy.(*repositoryProxy); ok { + if b, ok := v.bridge.(*repositoryBridge); ok { return b.impl, nil } - return nil, errors.ErrNotSupported("repository base type", fmt.Sprintf("%T", v.proxy)) + return nil, errors.ErrNotSupported("repository base type", fmt.Sprintf("%T", v.bridge)) } return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n)) } -func repositoryViewCreator(i RepositoryProxy, v resource.CloserView, d RepositoryViewManager) cpi.Repository { +func repositoryViewCreator(i RepositoryBridge, v resource.CloserView, d RepositoryViewManager) cpi.Repository { return &repositoryView{ _repositoryView: resource.NewView[cpi.Repository](v, d), - proxy: i, + bridge: i, } } // NewNoneRefRepositoryView provides a repository reflecting the state of the // view manager without holding an additional reference. -func NewNoneRefRepositoryView(i RepositoryProxy) cpi.Repository { +func NewNoneRefRepositoryView(i RepositoryBridge) cpi.Repository { return &repositoryView{ _repositoryView: resource.NewView[cpi.Repository](resource.NewNonRefView[cpi.Repository](i), i), - proxy: i, + bridge: i, } } func NewRepository(impl RepositoryImpl, kind string, closer ...io.Closer) cpi.Repository { - proxy := newRepositoryProxy(impl, kind, closer...) + bridge := newRepositoryBridge(impl, kind, closer...) if kind == "" { kind = "OCM repository" } - return resource.NewResource[cpi.Repository](proxy, repositoryViewCreator, kind, true) + return resource.NewResource[cpi.Repository](bridge, repositoryViewCreator, kind, true) } func (r *repositoryView) Unwrap() interface{} { - return r.proxy + return r.bridge } func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity { - return credentials.GetProvidedConsumerId(r.proxy, uctx...) + return credentials.GetProvidedConsumerId(r.bridge, uctx...) } func (r *repositoryView) GetIdentityMatcher() string { - return credentials.GetProvidedIdentityMatcher(r.proxy) + return credentials.GetProvidedIdentityMatcher(r.bridge) } func (r *repositoryView) GetSpecification() cpi.RepositorySpec { - return r.proxy.GetSpecification() + return r.bridge.GetSpecification() } func (r *repositoryView) GetContext() cpi.Context { - return r.proxy.GetContext() + return r.bridge.GetContext() } func (r *repositoryView) ComponentLister() cpi.ComponentLister { - return r.proxy.ComponentLister() + return r.bridge.ComponentLister() } func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) { err = r.Execute(func() error { - ok, err = r.proxy.ExistsComponentVersion(name, version) + ok, err = r.bridge.ExistsComponentVersion(name, version) return err }) return ok, err @@ -131,7 +131,7 @@ func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok func (r *repositoryView) LookupComponentVersion(name string, version string) (acc cpi.ComponentVersionAccess, err error) { err = r.Execute(func() error { - acc, err = r.proxy.LookupComponentVersion(name, version) + acc, err = r.bridge.LookupComponentVersion(name, version) return err }) return acc, err @@ -139,7 +139,7 @@ func (r *repositoryView) LookupComponentVersion(name string, version string) (ac func (r *repositoryView) LookupComponent(name string) (acc cpi.ComponentAccess, err error) { err = r.Execute(func() error { - acc, err = r.proxy.LookupComponent(name) + acc, err = r.bridge.LookupComponent(name) return err }) return acc, err diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go index 37b01a1f5c..1ee8f4cf25 100644 --- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go +++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go @@ -103,7 +103,7 @@ func (c *ComponentArchive) SetVersion(v string) { type componentArchiveContainer struct { ctx cpi.Context - base repocpi.ComponentVersionAccessProxy + base repocpi.ComponentVersionAccessBridge fsacc *accessobj.FileSystemBlobAccess spec *RepositorySpec repo cpi.Repository @@ -111,11 +111,11 @@ type componentArchiveContainer struct { var _ repocpi.ComponentVersionAccessImpl = (*componentArchiveContainer)(nil) -func (c *componentArchiveContainer) SetProxy(base repocpi.ComponentVersionAccessProxy) { +func (c *componentArchiveContainer) SetBridge(base repocpi.ComponentVersionAccessBridge) { c.base = base } -func (c *componentArchiveContainer) GetParentProxy() repocpi.ComponentAccessProxy { +func (c *componentArchiveContainer) GetParentBridge() repocpi.ComponentAccessBridge { return nil } diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go index c2c81fd957..7ab49eea54 100644 --- a/pkg/contexts/ocm/repositories/comparch/repository.go +++ b/pkg/contexts/ocm/repositories/comparch/repository.go @@ -26,7 +26,7 @@ import ( type RepositoryImpl struct { lock sync.RWMutex - base repocpi.RepositoryProxy + bridge repocpi.RepositoryBridge arch *ComponentArchive nonref cpi.Repository } @@ -57,8 +57,8 @@ func (r *RepositoryImpl) Close() error { return r.arch.container.Close() } -func (r *RepositoryImpl) SetProxy(base repocpi.RepositoryProxy) { - r.base = base +func (r *RepositoryImpl) SetBridge(base repocpi.RepositoryBridge) { + r.bridge = base r.nonref = repocpi.NewNoneRefRepositoryView(base) } @@ -143,7 +143,7 @@ func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessI //////////////////////////////////////////////////////////////////////////////// type ComponentAccessImpl struct { - base repocpi.ComponentAccessProxy + base repocpi.ComponentAccessBridge repo *RepositoryImpl } @@ -160,12 +160,12 @@ func (c *ComponentAccessImpl) Close() error { return nil } -func (c *ComponentAccessImpl) SetProxy(base repocpi.ComponentAccessProxy) { +func (c *ComponentAccessImpl) SetBridge(base repocpi.ComponentAccessBridge) { c.base = base } -func (c *ComponentAccessImpl) GetParentProxy() repocpi.RepositoryViewManager { - return c.repo.base +func (c *ComponentAccessImpl) GetParentBridge() repocpi.RepositoryViewManager { + return c.repo.bridge } func (c *ComponentAccessImpl) GetContext() cpi.Context { @@ -208,7 +208,7 @@ func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (*re //////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl repocpi.ComponentVersionAccessProxy + impl repocpi.ComponentVersionAccessBridge comp *ComponentAccessImpl @@ -232,11 +232,11 @@ func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionC }, nil } -func (c *ComponentVersionContainer) SetProxy(impl repocpi.ComponentVersionAccessProxy) { +func (c *ComponentVersionContainer) SetBridge(impl repocpi.ComponentVersionAccessBridge) { c.impl = impl } -func (c *ComponentVersionContainer) GetParentProxy() repocpi.ComponentAccessProxy { +func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBridge { return c.comp.base } diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go index acc87cf4ff..9343493847 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/component.go +++ b/pkg/contexts/ocm/repositories/genericocireg/component.go @@ -24,7 +24,7 @@ const META_SEPARATOR = ".build-" //////////////////////////////////////////////////////////////////////////////// type componentAccessImpl struct { - base repocpi.ComponentAccessProxy + bridge repocpi.ComponentAccessBridge repo *RepositoryImpl name string namespace oci.NamespaceAccess @@ -54,12 +54,12 @@ func (c *componentAccessImpl) Close() error { return err } -func (c *componentAccessImpl) SetProxy(base repocpi.ComponentAccessProxy) { - c.base = base +func (c *componentAccessImpl) SetBridge(base repocpi.ComponentAccessBridge) { + c.bridge = base } -func (c *componentAccessImpl) GetParentProxy() repocpi.RepositoryViewManager { - return c.repo.base +func (c *componentAccessImpl) GetParentBridge() repocpi.RepositoryViewManager { + return c.repo.bridge } func (c *componentAccessImpl) GetContext() cpi.Context { diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go index 8657f65775..808f737f2b 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go +++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go @@ -44,7 +44,7 @@ func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessI // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - impl repocpi.ComponentVersionAccessProxy + bridge repocpi.ComponentVersionAccessBridge comp *componentAccessImpl version string @@ -75,12 +75,12 @@ func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAcce }, nil } -func (c *ComponentVersionContainer) SetProxy(impl repocpi.ComponentVersionAccessProxy) { - c.impl = impl +func (c *ComponentVersionContainer) SetBridge(impl repocpi.ComponentVersionAccessBridge) { + c.bridge = impl } -func (c *ComponentVersionContainer) GetParentProxy() repocpi.ComponentAccessProxy { - return c.comp.base +func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBridge { + return c.comp.bridge } func (c *ComponentVersionContainer) Close() error { diff --git a/pkg/contexts/ocm/repositories/genericocireg/repository.go b/pkg/contexts/ocm/repositories/genericocireg/repository.go index 9040e72e4a..88a5824eb2 100644 --- a/pkg/contexts/ocm/repositories/genericocireg/repository.go +++ b/pkg/contexts/ocm/repositories/genericocireg/repository.go @@ -40,7 +40,7 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository { } type RepositoryImpl struct { - base repocpi.RepositoryProxy + bridge repocpi.RepositoryBridge ctx cpi.Context meta ComponentRepositoryMeta nonref cpi.Repository @@ -65,8 +65,8 @@ func (r *RepositoryImpl) Close() error { return r.ocirepo.Close() } -func (r *RepositoryImpl) SetProxy(base repocpi.RepositoryProxy) { - r.base = base +func (r *RepositoryImpl) SetBridge(base repocpi.RepositoryBridge) { + r.bridge = base r.nonref = repocpi.NewNoneRefRepositoryView(base) } diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go index a930ba51f1..b09ad99ee2 100644 --- a/pkg/contexts/ocm/repositories/virtual/component.go +++ b/pkg/contexts/ocm/repositories/virtual/component.go @@ -12,7 +12,7 @@ import ( ) type componentAccessImpl struct { - base repocpi.ComponentAccessProxy + bridge repocpi.ComponentAccessBridge repo *RepositoryImpl name string @@ -32,12 +32,12 @@ func (c *componentAccessImpl) Close() error { return nil } -func (c *componentAccessImpl) SetProxy(base repocpi.ComponentAccessProxy) { - c.base = base +func (c *componentAccessImpl) SetBridge(base repocpi.ComponentAccessBridge) { + c.bridge = base } -func (c *componentAccessImpl) GetParentProxy() repocpi.RepositoryViewManager { - return c.repo.base +func (c *componentAccessImpl) GetParentBridge() repocpi.RepositoryViewManager { + return c.repo.bridge } func (c *componentAccessImpl) GetContext() cpi.Context { diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go index 0c9aa4fdd7..a74f4c1dbd 100644 --- a/pkg/contexts/ocm/repositories/virtual/componentversion.go +++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go @@ -33,7 +33,7 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis // ////////////////////////////////////////////////////////////////////////////// type ComponentVersionContainer struct { - base repocpi.ComponentVersionAccessProxy + bridge repocpi.ComponentVersionAccessBridge comp *componentAccessImpl version string @@ -50,12 +50,12 @@ func newComponentVersionContainer(comp *componentAccessImpl, version string, acc }, nil } -func (c *ComponentVersionContainer) SetProxy(base repocpi.ComponentVersionAccessProxy) { - c.base = base +func (c *ComponentVersionContainer) SetBridge(base repocpi.ComponentVersionAccessBridge) { + c.bridge = base } -func (c *ComponentVersionContainer) GetParentProxy() repocpi.ComponentAccessProxy { - return c.comp.base +func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBridge { + return c.comp.bridge } func (c *ComponentVersionContainer) Close() error { diff --git a/pkg/contexts/ocm/repositories/virtual/repository.go b/pkg/contexts/ocm/repositories/virtual/repository.go index 3071faffe2..a1cc262e70 100644 --- a/pkg/contexts/ocm/repositories/virtual/repository.go +++ b/pkg/contexts/ocm/repositories/virtual/repository.go @@ -10,7 +10,7 @@ import ( ) type RepositoryImpl struct { - base repocpi.RepositoryProxy + bridge repocpi.RepositoryBridge ctx cpi.Context access Access nonref cpi.Repository @@ -30,8 +30,8 @@ func (r *RepositoryImpl) Close() error { return r.access.Close() } -func (r *RepositoryImpl) SetProxy(base repocpi.RepositoryProxy) { - r.base = base +func (r *RepositoryImpl) SetBridge(base repocpi.RepositoryBridge) { + r.bridge = base r.nonref = repocpi.NewNoneRefRepositoryView(base) }