diff --git a/.github/config/wordlist.txt b/.github/config/wordlist.txt
index 9b99188932..7c36ab7707 100644
--- a/.github/config/wordlist.txt
+++ b/.github/config/wordlist.txt
@@ -11,6 +11,7 @@ anchore
api
archlinux
artifactid
+attr
aur
auth
autoconfigure
diff --git a/api/oci/extensions/repositories/ctf/ctf_test.go b/api/oci/extensions/repositories/ctf/ctf_test.go
index 405bad9231..305a3afbfa 100644
--- a/api/oci/extensions/repositories/ctf/ctf_test.go
+++ b/api/oci/extensions/repositories/ctf/ctf_test.go
@@ -84,7 +84,7 @@ var _ = Describe("ctf management", func() {
})
It("instantiate filesystem ctf", func() {
- r, err := spec.Repository(nil, nil)
+ r, err := spec.Repository(cpi.DefaultContext(), nil)
Expect(err).To(Succeed())
Expect(vfs.DirExists(tempfs, "test/"+ctf.BlobsDirectoryName)).To(BeTrue())
@@ -111,7 +111,7 @@ var _ = Describe("ctf management", func() {
It("instantiate tgz artifact", func() {
ctf.FormatTGZ.ApplyOption(&spec.StandardOptions)
spec.FilePath = "test.tgz"
- r, err := spec.Repository(nil, nil)
+ r, err := spec.Repository(cpi.DefaultContext(), nil)
Expect(err).To(Succeed())
n, err := r.LookupNamespace("mandelsoft/test")
@@ -156,7 +156,7 @@ var _ = Describe("ctf management", func() {
Context("manifest", func() {
It("read from filesystem ctf", func() {
- r, err := spec.Repository(nil, nil)
+ r, err := spec.Repository(cpi.DefaultContext(), nil)
Expect(err).To(Succeed())
Expect(vfs.DirExists(tempfs, "test/"+ctf.BlobsDirectoryName)).To(BeTrue())
n, err := r.LookupNamespace("mandelsoft/test")
@@ -165,7 +165,7 @@ var _ = Describe("ctf management", func() {
Expect(n.Close()).To(Succeed())
Expect(r.Close()).To(Succeed())
- r, err = ctf.Open(nil, accessobj.ACC_READONLY, "test", 0, accessio.PathFileSystem(tempfs))
+ r, err = ctf.Open(cpi.DefaultContext(), accessobj.ACC_READONLY, "test", 0, accessio.PathFileSystem(tempfs))
Expect(err).To(Succeed())
defer r.Close()
diff --git a/api/oci/extensions/repositories/ctf/format.go b/api/oci/extensions/repositories/ctf/format.go
index 90ed959b71..c8e972eda1 100644
--- a/api/oci/extensions/repositories/ctf/format.go
+++ b/api/oci/extensions/repositories/ctf/format.go
@@ -8,6 +8,7 @@ import (
"github.com/mandelsoft/goutils/errors"
"github.com/mandelsoft/vfs/pkg/vfs"
+ "ocm.software/ocm/api/datacontext/attrs/vfsattr"
"ocm.software/ocm/api/oci/cpi"
"ocm.software/ocm/api/oci/extensions/repositories/ctf/format"
"ocm.software/ocm/api/oci/extensions/repositories/ctf/index"
@@ -126,8 +127,12 @@ func OpenFromBlob(ctx cpi.ContextProvider, acc accessobj.AccessMode, blob blobac
return Open(ctx, acc&accessobj.ACC_READONLY, "", 0, o)
}
-func Open(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) {
- o, create, err := accessobj.HandleAccessMode(acc, path, nil, opts...)
+func Open(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode vfs.FileMode, olist ...accessio.Option) (*Object, error) {
+ opts, err := accessio.AccessOptions(&accessio.StandardOptions{PathFileSystem: vfsattr.Get(ctx.OCIContext())}, olist...)
+ if err != nil {
+ return nil, err
+ }
+ o, create, err := accessobj.HandleAccessMode(acc, path, opts)
if err != nil {
return nil, err
}
diff --git a/api/oci/extensions/repositories/ctf/type.go b/api/oci/extensions/repositories/ctf/type.go
index 67d782aeb5..0cfd4906ba 100644
--- a/api/oci/extensions/repositories/ctf/type.go
+++ b/api/oci/extensions/repositories/ctf/type.go
@@ -80,7 +80,10 @@ func (s *RepositorySpec) UniformRepositorySpec() *cpi.UniformRepositorySpec {
}
func (a *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentials) (cpi.Repository, error) {
- return Open(ctx, a.AccessMode, a.FilePath, 0o700, &a.StandardOptions)
+ opts := a.StandardOptions
+ opts.Default(vfsattr.Get(ctx))
+
+ return Open(ctx, a.AccessMode, a.FilePath, 0o700, &opts)
}
func (a *RepositorySpec) Validate(ctx cpi.Context, creds credentials.Credentials, context ...credentials.UsageContext) error {
diff --git a/api/ocm/add_test.go b/api/ocm/add_test.go
index af008f6e02..7890d50161 100644
--- a/api/ocm/add_test.go
+++ b/api/ocm/add_test.go
@@ -15,6 +15,11 @@ import (
"ocm.software/ocm/api/utils/mime"
)
+const (
+ COMPONENT = "acme.org/test"
+ VERSION = "v1"
+)
+
var _ = Describe("add resources", func() {
var ctx ocm.Context
var cv ocm.ComponentVersionAccess
diff --git a/api/ocm/compdesc/meta/v1/resourceref.go b/api/ocm/compdesc/meta/v1/resourceref.go
index 4630ecfab3..ef2a752166 100644
--- a/api/ocm/compdesc/meta/v1/resourceref.go
+++ b/api/ocm/compdesc/meta/v1/resourceref.go
@@ -7,8 +7,8 @@ type ResourceReference struct {
ReferencePath []Identity `json:"referencePath,omitempty"`
}
-func NewResourceRef(id Identity) ResourceReference {
- return ResourceReference{Resource: id}
+func NewResourceRef(id Identity, path ...Identity) ResourceReference {
+ return ResourceReference{Resource: id, ReferencePath: path}
}
func NewNestedResourceRef(id Identity, path []Identity) ResourceReference {
diff --git a/api/ocm/cpi/accspeccpi/interface.go b/api/ocm/cpi/accspeccpi/interface.go
index 2334058bd7..044fe9b2cd 100644
--- a/api/ocm/cpi/accspeccpi/interface.go
+++ b/api/ocm/cpi/accspeccpi/interface.go
@@ -21,6 +21,7 @@ type (
CosumerIdentityProvider = credentials.ConsumerIdentityProvider
ComponentVersionAccess = internal.ComponentVersionAccess
+ DigestSpecProvider = internal.DigestSpecProvider
)
var (
diff --git a/api/ocm/cpi/accspeccpi/methodview.go b/api/ocm/cpi/accspeccpi/methodview.go
index 670d258f0d..7ef1eeaa6c 100644
--- a/api/ocm/cpi/accspeccpi/methodview.go
+++ b/api/ocm/cpi/accspeccpi/methodview.go
@@ -7,6 +7,7 @@ import (
"github.com/opencontainers/go-digest"
"ocm.software/ocm/api/credentials"
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/utils"
"ocm.software/ocm/api/utils/blobaccess/blobaccess"
"ocm.software/ocm/api/utils/refmgmt"
@@ -47,6 +48,9 @@ func BlobAccessForAccessSpec(spec AccessSpec, cv ComponentVersionAccess) (blobac
}
func accessMethodViewCreator(impl AccessMethodImpl, view *refmgmt.View[AccessMethod]) AccessMethod {
+ if _, ok := impl.(DigestSpecProvider); ok {
+ return &accessMethodViewWithDigest{accessMethodView{view, impl}}
+ }
return &accessMethodView{view, impl}
}
@@ -55,11 +59,20 @@ type accessMethodView struct {
methodimpl AccessMethodImpl
}
+type accessMethodViewWithDigest struct {
+ accessMethodView
+}
+
var (
_ AccessMethodView = (*accessMethodView)(nil)
_ credentials.ConsumerIdentityProvider = (*accessMethodView)(nil)
+ _ DigestSpecProvider = (*accessMethodViewWithDigest)(nil)
)
+func (a *accessMethodViewWithDigest) GetDigestSpec() (*metav1.DigestSpec, error) {
+ return a.methodimpl.(DigestSpecProvider).GetDigestSpec()
+}
+
func (a *accessMethodView) Unwrap() interface{} {
return a.methodimpl
}
diff --git a/api/ocm/cpi/interface.go b/api/ocm/cpi/interface.go
index cfe0077b09..cfda6966a1 100644
--- a/api/ocm/cpi/interface.go
+++ b/api/ocm/cpi/interface.go
@@ -69,9 +69,12 @@ type (
GenericRepositorySpec = internal.GenericRepositorySpec
RepositoryType = internal.RepositoryType
ComponentReference = internal.ComponentReference
+ DigestSpecProvider = internal.DigestSpecProvider
)
-type ArtifactAccess[M any] internal.ArtifactAccess[M]
+type ArtifactAccess[M any] interface {
+ internal.ArtifactAccess[M]
+}
type (
BlobHandler = internal.BlobHandler
@@ -194,6 +197,8 @@ const (
KIND_RESOURCE = internal.KIND_RESOURCE
KIND_SOURCE = internal.KIND_SOURCE
KIND_REFERENCE = internal.KIND_REFERENCE
+ KIND_REPOSITORYSPEC = internal.KIND_REPOSITORYSPEC
+ KIND_OCM_REFERENCE = internal.KIND_OCM_REFERENCE
)
func ErrComponentVersionNotFound(name, version string) error {
diff --git a/api/ocm/cpi/ref.go b/api/ocm/cpi/ref.go
index c281602374..9d654f2761 100644
--- a/api/ocm/cpi/ref.go
+++ b/api/ocm/cpi/ref.go
@@ -1,7 +1,13 @@
package cpi
import (
+ "strings"
"sync"
+
+ "github.com/mandelsoft/goutils/errors"
+ "github.com/mandelsoft/goutils/general"
+
+ "ocm.software/ocm/api/ocm/grammar"
)
type ParseHandler func(u *UniformRepositorySpec) error
@@ -45,3 +51,89 @@ func GetRefParseHandler(ty string, h ParseHandler) {
func HandleRef(u UniformRepositorySpec) (UniformRepositorySpec, error) {
return parseregistry.Handle(u)
}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// ParseRepo parses a standard ocm repository reference into a internal representation.
+func ParseRepo(ref string) (UniformRepositorySpec, error) {
+ create := false
+ if strings.HasPrefix(ref, "+") {
+ create = true
+ ref = ref[1:]
+ }
+ if strings.HasPrefix(ref, ".") || strings.HasPrefix(ref, "/") {
+ return HandleRef(UniformRepositorySpec{
+ Info: ref,
+ CreateIfMissing: create,
+ })
+ }
+ match := grammar.AnchoredRepositoryRegexp.FindSubmatch([]byte(ref))
+ if match != nil {
+ h := string(match[1])
+ t, _ := grammar.SplitTypeSpec(h)
+ return HandleRef(UniformRepositorySpec{
+ Type: t,
+ TypeHint: h,
+ Scheme: string(match[2]),
+ Host: string(match[3]),
+ SubPath: string(match[4]),
+ CreateIfMissing: create,
+ })
+ }
+
+ match = grammar.AnchoredSchemedHostPortRepositoryRegexp.FindSubmatch([]byte(ref))
+ if match != nil {
+ h := string(match[1])
+ t, _ := grammar.SplitTypeSpec(h)
+ return HandleRef(UniformRepositorySpec{
+ Type: t,
+ TypeHint: h,
+ Scheme: string(match[2]),
+ Host: string(match[3]),
+ SubPath: string(match[4]),
+ CreateIfMissing: create,
+ })
+ }
+
+ match = grammar.AnchoredHostWithPortRepositoryRegexp.FindSubmatch([]byte(ref))
+ if match != nil {
+ h := string(match[1])
+ t, _ := grammar.SplitTypeSpec(h)
+ return HandleRef(UniformRepositorySpec{
+ Type: t,
+ TypeHint: h,
+ Scheme: string(match[2]),
+ Host: string(match[3]),
+ SubPath: string(match[4]),
+ CreateIfMissing: create,
+ })
+ }
+
+ match = grammar.AnchoredGenericRepositoryRegexp.FindSubmatch([]byte(ref))
+ if match == nil {
+ return UniformRepositorySpec{}, errors.ErrInvalid(KIND_OCM_REFERENCE, ref)
+ }
+ h := string(match[1])
+ t, _ := grammar.SplitTypeSpec(h)
+ return HandleRef(UniformRepositorySpec{
+ Type: t,
+ TypeHint: h,
+ Info: string(match[2]),
+ CreateIfMissing: create,
+ })
+}
+
+func ParseRepoToSpec(ctx Context, ref string, create ...bool) (RepositorySpec, error) {
+ uni, err := ParseRepo(ref)
+ if err != nil {
+ return nil, errors.ErrInvalidWrap(err, KIND_REPOSITORYSPEC, ref)
+ }
+ if !uni.CreateIfMissing {
+ uni.CreateIfMissing = general.Optional(create...)
+ }
+ repoSpec, err := ctx.MapUniformRepositorySpec(&uni)
+ if err != nil {
+ return nil, errors.ErrInvalidWrap(err, KIND_REPOSITORYSPEC, ref)
+ }
+ return repoSpec, nil
+}
diff --git a/api/ocm/cpi/repocpi/interface.go b/api/ocm/cpi/repocpi/interface.go
index f3b8be0f49..65da12e456 100644
--- a/api/ocm/cpi/repocpi/interface.go
+++ b/api/ocm/cpi/repocpi/interface.go
@@ -5,6 +5,7 @@ import (
)
type (
- Context = internal.Context
- Repository = internal.Repository
+ Context = internal.Context
+ Repository = internal.Repository
+ DigestSpecProvider = internal.DigestSpecProvider
)
diff --git a/api/ocm/cpi/repocpi/view_cv.go b/api/ocm/cpi/repocpi/view_cv.go
index 40b9c0fced..43916063ba 100644
--- a/api/ocm/cpi/repocpi/view_cv.go
+++ b/api/ocm/cpi/repocpi/view_cv.go
@@ -401,14 +401,30 @@ func (c *componentVersionAccessView) SetResource(meta *cpi.ResourceMeta, acc com
// evaluate given digesting constraints and settings
hashAlgo, digester, digest := c.evaluateResourceDigest(res, old, *opts)
+ digestForwarded := false
+ if digest == "" {
+ if p, ok := meth.(DigestSpecProvider); ok {
+ dig, err := p.GetDigestSpec()
+ if dig != nil && err == nil {
+ // always prefer already known digest with its method
+ // if no concrete digest value is given by the caller
+ digest = dig.Value
+ hashAlgo = dig.HashAlgorithm
+ digester.HashAlgorithm = hashAlgo
+ digester.NormalizationAlgorithm = dig.NormalisationAlgorithm
+ digestForwarded = true
+ }
+ }
+ }
+
hasher := opts.GetHasher(hashAlgo)
- if digester.HashAlgorithm == "" && hasher == nil {
+ if hasher == nil {
return errors.ErrUnknown(compdesc.KIND_HASH_ALGORITHM, hashAlgo)
}
if !compdesc.IsNoneAccessKind(res.Access.GetKind()) {
var calculatedDigest *cpi.DigestDescriptor
- if (!opts.IsSkipVerify() && digest != "") || (!opts.IsSkipDigest() && digest == "") {
+ if (!opts.IsSkipVerify() && !digestForwarded && digest != "") || (!opts.IsSkipDigest() && digest == "") {
dig, err := ctx.BlobDigesters().DetermineDigests(res.Type, hasher, opts.HasherProvider, meth, digester)
if err != nil {
return err
@@ -417,11 +433,11 @@ func (c *componentVersionAccessView) SetResource(meta *cpi.ResourceMeta, acc com
return fmt.Errorf("%s: no digester accepts resource", res.Name)
}
calculatedDigest = &dig[0]
- }
- if digest != "" && !opts.IsSkipVerify() {
- if digest != calculatedDigest.Value {
- return fmt.Errorf("digest mismatch: %s != %s", calculatedDigest.Value, digest)
+ if digest != "" && !opts.IsSkipVerify() {
+ if digest != calculatedDigest.Value {
+ return fmt.Errorf("digest mismatch: %s != %s", calculatedDigest.Value, digest)
+ }
}
}
diff --git a/api/ocm/elements/artifactaccess/ocmaccess/forward.go b/api/ocm/elements/artifactaccess/ocmaccess/forward.go
new file mode 100644
index 0000000000..b6fcfc4569
--- /dev/null
+++ b/api/ocm/elements/artifactaccess/ocmaccess/forward.go
@@ -0,0 +1,38 @@
+package ocmaccess
+
+import (
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/selectors/rscsel"
+ "ocm.software/ocm/api/utils/blobaccess/ocm"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Component Version
+
+func ByComponentVersion(cv cpi.ComponentVersionAccess) ocm.ComponentVersionProvider {
+ return ocm.ByComponentVersion(cv)
+}
+
+func ByResolverAndName(resolver cpi.ComponentVersionResolver, comp, vers string) ocm.ComponentVersionProvider {
+ return ocm.ByResolverAndName(resolver, comp, vers)
+}
+
+func ByRepositorySpecAndName(ctx cpi.ContextProvider, spec cpi.RepositorySpec, comp, vers string) ocm.ComponentVersionProvider {
+ return ocm.ByRepositorySpecAndName(ctx, spec, comp, vers)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Resource
+
+func ByResourceId(id metav1.Identity) ocm.ResourceProvider {
+ return ocm.ByResourceId(id)
+}
+
+func ByResourcePath(id metav1.Identity, path ...metav1.Identity) ocm.ResourceProvider {
+ return ocm.ByResourcePath(id, path...)
+}
+
+func ByResourceSelector(sel ...rscsel.Selector) ocm.ResourceProvider {
+ return ocm.ByResourceSelector(sel...)
+}
diff --git a/api/ocm/elements/artifactaccess/ocmaccess/resource.go b/api/ocm/elements/artifactaccess/ocmaccess/resource.go
new file mode 100644
index 0000000000..48dc7e96c5
--- /dev/null
+++ b/api/ocm/elements/artifactaccess/ocmaccess/resource.go
@@ -0,0 +1,27 @@
+package ocmaccess
+
+import (
+ "ocm.software/ocm/api/ocm"
+ "ocm.software/ocm/api/ocm/compdesc"
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/elements/artifactaccess/genericaccess"
+ access "ocm.software/ocm/api/ocm/extensions/accessmethods/ocm"
+)
+
+func Access[M any, P compdesc.ArtifactMetaPointer[M]](ctx ocm.Context, meta P, comp, vers string, repo cpi.RepositorySpec, id metav1.Identity, path ...metav1.Identity) (cpi.ArtifactAccess[M], error) {
+ spec, err := access.New(comp, vers, repo, id, path...)
+ if err != nil {
+ return nil, err
+ }
+ // is global access, must work, otherwise there is an error in the lib.
+ return genericaccess.MustAccess(ctx, meta, spec), nil
+}
+
+func ResourceAccess(ctx ocm.Context, meta *cpi.ResourceMeta, comp, vers string, repo cpi.RepositorySpec, id metav1.Identity, path ...metav1.Identity) (cpi.ResourceAccess, error) {
+ return Access(ctx, meta, comp, vers, repo, id, path...)
+}
+
+func SourceAccess(ctx ocm.Context, meta *cpi.SourceMeta, comp, vers string, repo cpi.RepositorySpec, id metav1.Identity, path ...metav1.Identity) (cpi.SourceAccess, error) {
+ return Access(ctx, meta, comp, vers, repo, id, path...)
+}
diff --git a/api/ocm/elements/artifactblob/ocmblob/access_test.go b/api/ocm/elements/artifactblob/ocmblob/access_test.go
new file mode 100644
index 0000000000..b118229b93
--- /dev/null
+++ b/api/ocm/elements/artifactblob/ocmblob/access_test.go
@@ -0,0 +1,71 @@
+package ocmblob_test
+
+import (
+ . "github.com/mandelsoft/goutils/testutils"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ . "ocm.software/ocm/api/helper/builder"
+
+ "ocm.software/ocm/api/ocm/elements"
+ me "ocm.software/ocm/api/ocm/elements/artifactblob/mavenblob"
+ resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
+ "ocm.software/ocm/api/ocm/extensions/repositories/composition"
+ "ocm.software/ocm/api/tech/maven"
+ "ocm.software/ocm/api/tech/maven/maventest"
+)
+
+const (
+ MAVEN_PATH = "/testdata/.m2/repository"
+ FAIL_PATH = "/testdata/.m2/fail"
+ MAVEN_CENTRAL_ADDRESS = "repo.maven.apache.org:443"
+ MAVEN_CENTRAL = "https://repo.maven.apache.org/maven2/"
+ MAVEN_GROUP_ID = "maven"
+ MAVEN_ARTIFACT_ID = "maven"
+ MAVEN_VERSION = "1.1"
+)
+
+var _ = Describe("blobaccess for maven", func() {
+ Context("maven filesystem repository", func() {
+ var env *Builder
+ var repo *maven.Repository
+
+ BeforeEach(func() {
+ env = NewBuilder(maventest.TestData())
+ repo = maven.NewFileRepository(MAVEN_PATH, env.FileSystem())
+ })
+
+ AfterEach(func() {
+ MustBeSuccessful(env.Cleanup())
+ })
+
+ It("blobaccess for a single file with classifier and extension", func() {
+ cv := composition.NewComponentVersion(env.OCMContext(), "acme.org/test", "1.0.0")
+ defer Close(cv)
+
+ coords := maven.NewCoordinates("com.sap.cloud.sdk", "sdk-modules-bom", "5.7.0",
+ maven.WithClassifier("random-content"), maven.WithExtension("json"))
+
+ a := me.ResourceAccessForMavenCoords(env.OCMContext(), Must(elements.ResourceMeta("mavenblob", resourcetypes.OCM_JSON, elements.WithLocalRelation())), repo, coords, me.WithCachingFileSystem(env.FileSystem()))
+ Expect(a.ReferenceHint()).To(Equal(""))
+ b := Must(a.BlobAccess())
+ defer Close(b)
+ Expect(string(Must(b.Get()))).To(Equal(`{"some": "test content"}`))
+
+ MustBeSuccessful(cv.SetResourceByAccess(a))
+ r := Must(cv.GetResourceByIndex(0))
+ m := Must(r.AccessMethod())
+ defer Close(m)
+ Expect(string(Must(m.Get()))).To(Equal(`{"some": "test content"}`))
+ })
+
+ It("blobaccess for package", func() {
+ cv := composition.NewComponentVersion(env.OCMContext(), "acme.org/test", "1.0.0")
+ defer Close(cv)
+
+ coords := maven.NewCoordinates("com.sap.cloud.sdk", "sdk-modules-bom", "5.7.0")
+
+ a := me.ResourceAccessForMavenCoords(env.OCMContext(), Must(elements.ResourceMeta("mavenblob", resourcetypes.OCM_JSON, elements.WithLocalRelation())), repo, coords, me.WithCachingFileSystem(env.FileSystem()))
+ Expect(a.ReferenceHint()).To(Equal(coords.GAV()))
+ })
+ })
+})
diff --git a/api/ocm/elements/artifactblob/ocmblob/forward.go b/api/ocm/elements/artifactblob/ocmblob/forward.go
new file mode 100644
index 0000000000..35b0517e11
--- /dev/null
+++ b/api/ocm/elements/artifactblob/ocmblob/forward.go
@@ -0,0 +1,38 @@
+package ocmblob
+
+import (
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/selectors/rscsel"
+ "ocm.software/ocm/api/utils/blobaccess/ocm"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Component Version
+
+func ByComponentVersion(cv cpi.ComponentVersionAccess) ocm.ComponentVersionProvider {
+ return ocm.ByComponentVersion(cv)
+}
+
+func ByResolverAndName(resolver cpi.ComponentVersionResolver, comp, vers string) ocm.ComponentVersionProvider {
+ return ocm.ByResolverAndName(resolver, comp, vers)
+}
+
+func ByRepositorySpecAndName(ctx cpi.ContextProvider, spec cpi.RepositorySpec, comp, vers string) ocm.ComponentVersionProvider {
+ return ocm.ByRepositorySpecAndName(ctx, spec, comp, vers)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Resource
+
+func ByResourceId(id metav1.Identity) ocm.ResourceProvider {
+ return ocm.ByResourceId(id)
+}
+
+func ByResourcePath(id metav1.Identity, path ...metav1.Identity) ocm.ResourceProvider {
+ return ocm.ByResourcePath(id, path...)
+}
+
+func ByResourceSelector(sel ...rscsel.Selector) ocm.ResourceProvider {
+ return ocm.ByResourceSelector(sel...)
+}
diff --git a/api/ocm/elements/artifactblob/ocmblob/options.go b/api/ocm/elements/artifactblob/ocmblob/options.go
new file mode 100644
index 0000000000..407177e3d3
--- /dev/null
+++ b/api/ocm/elements/artifactblob/ocmblob/options.go
@@ -0,0 +1,21 @@
+package ocmblob
+
+import (
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/elements/artifactblob/api"
+)
+
+type Option = api.Option
+
+type Options = api.Options
+
+////////////////////////////////////////////////////////////////////////////////
+// General Options
+
+func WithHint(h string) Option {
+ return api.WrapHint[Options](h)
+}
+
+func WithGlobalAccess(a cpi.AccessSpec) Option {
+ return api.WrapGlobalAccess[Options](a)
+}
diff --git a/api/ocm/elements/artifactblob/ocmblob/resource.go b/api/ocm/elements/artifactblob/ocmblob/resource.go
new file mode 100644
index 0000000000..9949f65a1b
--- /dev/null
+++ b/api/ocm/elements/artifactblob/ocmblob/resource.go
@@ -0,0 +1,26 @@
+package ocmblob
+
+import (
+ "github.com/mandelsoft/goutils/generics"
+ "github.com/mandelsoft/goutils/optionutils"
+
+ "ocm.software/ocm/api/ocm/compdesc"
+ "ocm.software/ocm/api/ocm/cpi"
+ base "ocm.software/ocm/api/utils/blobaccess/ocm"
+)
+
+func Access[M any, P compdesc.ArtifactMetaPointer[M]](ctx cpi.Context, meta P, cvp base.ComponentVersionProvider, res base.ResourceProvider, opts ...Option) cpi.ArtifactAccess[M] {
+ eff := optionutils.EvalOptions(opts...)
+ blobprov := base.Provider(cvp, res)
+ accprov := cpi.NewAccessProviderForBlobAccessProvider(ctx, blobprov, eff.Hint, eff.Global)
+ // strange type cast is required by Go compiler, meta has the correct type.
+ return cpi.NewArtifactAccessForProvider(generics.Cast[*M](meta), accprov)
+}
+
+func ResourceAccess(ctx cpi.Context, meta *cpi.ResourceMeta, cvp base.ComponentVersionProvider, res base.ResourceProvider, opts ...Option) cpi.ResourceAccess {
+ return Access(ctx, meta, cvp, res, opts...)
+}
+
+func SourceAccess(ctx cpi.Context, meta *cpi.SourceMeta, cvp base.ComponentVersionProvider, res base.ResourceProvider, opts ...Option) cpi.SourceAccess {
+ return Access(ctx, meta, cvp, res, opts...)
+}
diff --git a/api/ocm/elements/artifactblob/ocmblob/suite_test.go b/api/ocm/elements/artifactblob/ocmblob/suite_test.go
new file mode 100644
index 0000000000..bca94d33a2
--- /dev/null
+++ b/api/ocm/elements/artifactblob/ocmblob/suite_test.go
@@ -0,0 +1,13 @@
+package ocmblob_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "OCM Blob Access Test Suite")
+}
diff --git a/api/ocm/extensions/accessmethods/helm/method.go b/api/ocm/extensions/accessmethods/helm/method.go
index c4e9807138..715c640103 100644
--- a/api/ocm/extensions/accessmethods/helm/method.go
+++ b/api/ocm/extensions/accessmethods/helm/method.go
@@ -41,7 +41,7 @@ func New(chart string, repourl string) *AccessSpec {
type AccessSpec struct {
runtime.ObjectVersionedType `json:",inline"`
- // HelmRepository is the URL og the helm repository to load the chart from.
+ // HelmRepository is the URL of the helm repository to load the chart from.
HelmRepository string `json:"helmRepository"`
// HelmChart if the name of the helm chart and its version separated by a colon.
diff --git a/api/ocm/extensions/accessmethods/init.go b/api/ocm/extensions/accessmethods/init.go
index d7db2ddb76..5f4094d3d5 100644
--- a/api/ocm/extensions/accessmethods/init.go
+++ b/api/ocm/extensions/accessmethods/init.go
@@ -11,6 +11,7 @@ import (
_ "ocm.software/ocm/api/ocm/extensions/accessmethods/npm"
_ "ocm.software/ocm/api/ocm/extensions/accessmethods/ociartifact"
_ "ocm.software/ocm/api/ocm/extensions/accessmethods/ociblob"
+ _ "ocm.software/ocm/api/ocm/extensions/accessmethods/ocm"
_ "ocm.software/ocm/api/ocm/extensions/accessmethods/relativeociref"
_ "ocm.software/ocm/api/ocm/extensions/accessmethods/s3"
_ "ocm.software/ocm/api/ocm/extensions/accessmethods/wget"
diff --git a/api/ocm/extensions/accessmethods/ocm/cli.go b/api/ocm/extensions/accessmethods/ocm/cli.go
new file mode 100644
index 0000000000..3c51b4ef3f
--- /dev/null
+++ b/api/ocm/extensions/accessmethods/ocm/cli.go
@@ -0,0 +1,54 @@
+package ocm
+
+import (
+ "ocm.software/ocm/api/ocm/extensions/accessmethods/options"
+ "ocm.software/ocm/api/utils/cobrautils/flagsets"
+)
+
+func ConfigHandler() flagsets.ConfigOptionTypeSetHandler {
+ return flagsets.NewConfigOptionTypeSetHandler(
+ Type, AddConfig,
+ options.RepositoryOption,
+ options.ComponentOption,
+ options.VersionOption,
+ options.IdentityPathOption,
+ )
+}
+
+func AddConfig(opts flagsets.ConfigOptions, config flagsets.Config) error {
+ flagsets.AddFieldByMappedOptionP(opts, options.RepositoryOption, config, options.MapRepository, "ocmRepository")
+ flagsets.AddFieldByOptionP(opts, options.ComponentOption, config, "component")
+ flagsets.AddFieldByOptionP(opts, options.VersionOption, config, "version")
+ flagsets.AddFieldByMappedOptionP(opts, options.IdentityPathOption, config, options.MapResourceRef, "resourceRef")
+ return nil
+}
+
+var usage = `
+This method implements the access of any resource artifact stored in an OCM
+repository. Only repository types supporting remote access should be used.
+`
+
+var formatV1 = `
+The type specific specification fields are:
+
+- **ocmRepository
** *json*
+
+ The repository spec for the OCM repository
+
+- **component
** *string*
+
+ *(Optional)* The name of the component. The default is the
+ own component.
+
+- **version
** *string*
+
+ *(Optional)* The version of the component. The default is the
+ own component version.
+
+- **resourceRef
** *relative resource ref*
+
+ The resource reference of the denoted resource relative to the
+ given component version.
+
+It uses the consumer identity and credentials for the intermediate repositories
+and the final resource access.`
diff --git a/api/ocm/extensions/accessmethods/ocm/cli_test.go b/api/ocm/extensions/accessmethods/ocm/cli_test.go
new file mode 100644
index 0000000000..e79ba4bc65
--- /dev/null
+++ b/api/ocm/extensions/accessmethods/ocm/cli_test.go
@@ -0,0 +1,75 @@
+package ocm_test
+
+import (
+ . "github.com/mandelsoft/goutils/testutils"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/mandelsoft/goutils/sliceutils"
+ "github.com/mandelsoft/goutils/transformer"
+ "github.com/spf13/pflag"
+
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/extensions/accessmethods/ocm"
+ "ocm.software/ocm/api/utils/cobrautils/flagsets"
+)
+
+var _ = Describe("OCM access CLI Test Environment", func() {
+ ctx := cpi.DefaultContext()
+
+ Context("cli options", func() {
+ It("handles access options", func() {
+ at := ctx.AccessMethods().GetType(ocm.TypeV1)
+ Expect(at).NotTo(BeNil())
+
+ h := at.ConfigOptionTypeSetHandler()
+ Expect(h).NotTo(BeNil())
+ Expect(h.GetName()).To(Equal(ocm.Type))
+
+ ot := h.OptionTypes()
+ Expect(len(ot)).To(Equal(4))
+
+ opts := h.CreateOptions()
+ Expect(sliceutils.Transform(opts.Options(), transformer.GetName[flagsets.Option, string])).To(ConsistOf(
+ "accessComponent", "accessVersion", "accessRepository", "identityPath"))
+
+ fs := &pflag.FlagSet{}
+ fs.SortFlags = true
+ opts.AddFlags(fs)
+
+ Expect("\n" + fs.FlagUsages()).To(Equal(`
+ --accessComponent string component for access specification
+ --accessRepository string repository URL
+ --accessVersion string version for access specification
+ --identityPath {=} identity path for specification
+`))
+
+ MustBeSuccessful(fs.Parse([]string{
+ "--accessRepository", "ghcr.io/open-component-model/ocm",
+ "--accessComponent", COMP1,
+ "--accessVersion", VERS,
+ "--identityPath", "name=rsc1",
+ "--identityPath", "other=value",
+ "--identityPath", "name=rsc2",
+ }))
+
+ cfg := flagsets.Config{}
+ MustBeSuccessful(h.ApplyConfig(opts, cfg))
+ Expect(cfg).To(YAMLEqual(`
+ component: acme.org/test1
+ version: v1
+ ocmRepository:
+ type: OCIRegistry
+ baseUrl: ghcr.io
+ componentNameMapping: urlPath
+ subPath: open-component-model/ocm
+ resourceRef:
+ resource:
+ name: rsc1
+ other: value
+ referencePath:
+ - name: rsc2
+`))
+ })
+ })
+})
diff --git a/api/ocm/extensions/accessmethods/ocm/method.go b/api/ocm/extensions/accessmethods/ocm/method.go
new file mode 100644
index 0000000000..f48edac666
--- /dev/null
+++ b/api/ocm/extensions/accessmethods/ocm/method.go
@@ -0,0 +1,236 @@
+package ocm
+
+import (
+ "fmt"
+ "io"
+ "sync"
+
+ "github.com/mandelsoft/goutils/errors"
+ "github.com/mandelsoft/goutils/finalizer"
+
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/cpi/accspeccpi"
+ "ocm.software/ocm/api/ocm/resourcerefs"
+ "ocm.software/ocm/api/tech/helm"
+ "ocm.software/ocm/api/utils/blobaccess/blobaccess"
+ "ocm.software/ocm/api/utils/refmgmt"
+ "ocm.software/ocm/api/utils/runtime"
+)
+
+// Type is the access type for a blob in an OCI repository.
+const (
+ Type = "ocm"
+ TypeV1 = Type + runtime.VersionSeparator + "v1"
+)
+
+func init() {
+ accspeccpi.RegisterAccessType(accspeccpi.NewAccessSpecType[*AccessSpec](Type, accspeccpi.WithDescription(usage)))
+ accspeccpi.RegisterAccessType(accspeccpi.NewAccessSpecType[*AccessSpec](TypeV1, accspeccpi.WithFormatSpec(formatV1), accspeccpi.WithConfigHandler(ConfigHandler())))
+}
+
+// New creates a new Helm Chart accessor for helm repositories.
+func New(comp, vers string, repo cpi.RepositorySpec, id metav1.Identity, path ...metav1.Identity) (*AccessSpec, error) {
+ spec, err := cpi.ToGenericRepositorySpec(repo)
+ if err != nil {
+ return nil, err
+ }
+ return &AccessSpec{
+ ObjectVersionedType: runtime.NewVersionedTypedObject(Type),
+ OCMRepository: spec,
+ Component: comp,
+ Version: vers,
+ ResourceRef: metav1.NewNestedResourceRef(id, path),
+ }, nil
+}
+
+// AccessSpec describes the access for an OCM repository.
+type AccessSpec struct {
+ runtime.ObjectVersionedType `json:",inline"`
+
+ // OCMRepository is the URL of the OCM repository to load the chart from.
+ OCMRepository *cpi.GenericRepositorySpec `json:"ocmRepository,omitempty"`
+
+ // Component if the name of the root component used to lookup the resource.
+ Component string `json:"component,omitempty"`
+
+ // Version is the version og the root component.
+ Version string `json:"version,omitempty,"`
+
+ ResourceRef metav1.ResourceReference `json:"resourceRef"`
+}
+
+var _ accspeccpi.AccessSpec = (*AccessSpec)(nil)
+
+func (a *AccessSpec) Describe(ctx accspeccpi.Context) string {
+ comp := a.Component
+ if a.Version != "" {
+ comp += ":" + a.Version
+ }
+ if comp != "" {
+ comp = " in " + comp
+ }
+ raw, _ := a.OCMRepository.GetRaw()
+ if a.OCMRepository != nil {
+ return fmt.Sprintf("OCM resource %s%s in repository %s", a.ResourceRef.String(), comp, string(raw))
+ }
+ return fmt.Sprintf("OCM resource %s%s", a.ResourceRef.String(), comp)
+}
+
+func (a *AccessSpec) IsLocal(ctx accspeccpi.Context) bool {
+ return false
+}
+
+func (a *AccessSpec) GlobalAccessSpec(ctx accspeccpi.Context) accspeccpi.AccessSpec {
+ return a
+}
+
+func (a *AccessSpec) AccessMethod(access accspeccpi.ComponentVersionAccess) (accspeccpi.AccessMethod, error) {
+ return accspeccpi.AccessMethodForImplementation(&accessMethod{comp: access, spec: a}, nil)
+}
+
+///////////////////
+
+func (a *AccessSpec) GetVersion() string {
+ return a.Version
+}
+
+func (a *AccessSpec) GetComponent() string {
+ return a.Component
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type accessMethod struct {
+ lock sync.Mutex
+ blob blobaccess.BlobAccess
+ repo cpi.Repository
+ comp accspeccpi.ComponentVersionAccess
+ spec *AccessSpec
+ acc cpi.ResourceAccess
+}
+
+var (
+ _ accspeccpi.AccessMethodImpl = (*accessMethod)(nil)
+ _ accspeccpi.DigestSpecProvider = (*accessMethod)(nil)
+)
+
+func (_ *accessMethod) IsLocal() bool {
+ return false
+}
+
+func (m *accessMethod) GetKind() string {
+ return Type
+}
+
+func (m *accessMethod) AccessSpec() accspeccpi.AccessSpec {
+ return m.spec
+}
+
+func (m *accessMethod) Close() error {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ if m.blob != nil {
+ m.blob.Close()
+ m.blob = nil
+ }
+ if m.repo != nil {
+ m.repo.Close()
+ m.repo = nil
+ }
+ return nil
+}
+
+func (m *accessMethod) Get() ([]byte, error) {
+ return blobaccess.BlobData(m.getBlob())
+}
+
+func (m *accessMethod) Reader() (io.ReadCloser, error) {
+ return blobaccess.BlobReader(m.getBlob())
+}
+
+func (m *accessMethod) MimeType() string {
+ return helm.ChartMediaType
+}
+
+func (m *accessMethod) getBlob() (bacc blobaccess.BlobAccess, efferr error) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+
+ var finalize finalizer.Finalizer
+ defer finalize.FinalizeWithErrorPropagation(&efferr)
+
+ if m.blob != nil {
+ return m.blob, nil
+ }
+
+ vers := m.spec.GetVersion()
+ name := m.spec.GetComponent()
+
+ if vers == "" {
+ vers = m.comp.GetVersion()
+ }
+ if name == "" {
+ vers = m.comp.GetName()
+ }
+
+ var err error
+
+ var cv cpi.ComponentVersionAccess
+ if name == m.comp.GetName() && vers == m.comp.GetVersion() {
+ cv = m.comp
+ if m.repo == nil {
+ m.repo, err = cv.Repository().Dup()
+ if err != nil {
+ return nil, err
+ }
+ }
+ } else {
+ if m.repo == nil {
+ if m.spec.OCMRepository == nil {
+ m.repo, err = m.comp.Repository().Dup()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ m.repo, err = refmgmt.ToLazy(m.comp.GetContext().RepositoryForSpec(m.spec.OCMRepository))
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ cv, err = refmgmt.ToLazy(m.repo.LookupComponentVersion(name, vers))
+ if errors.IsErrNotFound(err) || cv == nil {
+ r := m.comp.GetContext().GetResolver()
+ if r != nil {
+ cv, err = refmgmt.ToLazy(r.LookupComponentVersion(name, vers))
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ finalize.Close(cv)
+ }
+ if cv == nil {
+ return nil, errors.ErrNotFound(cpi.KIND_COMPONENTVERSION, name+":"+vers)
+ }
+
+ r, eff, err := resourcerefs.ResolveResourceReference(cv, m.spec.ResourceRef, m.comp.GetContext().GetResolver())
+ if err != nil {
+ return nil, err
+ }
+ finalize.Close(refmgmt.AsLazy(eff))
+
+ m.blob, err = r.BlobAccess()
+ m.acc = r
+ return m.blob, err
+}
+
+func (m *accessMethod) GetDigestSpec() (*metav1.DigestSpec, error) {
+ _, err := m.getBlob()
+ if err != nil {
+ return nil, err
+ }
+ return m.acc.Meta().Digest, nil
+}
diff --git a/api/ocm/extensions/accessmethods/ocm/method_test.go b/api/ocm/extensions/accessmethods/ocm/method_test.go
new file mode 100644
index 0000000000..0ebe2d5814
--- /dev/null
+++ b/api/ocm/extensions/accessmethods/ocm/method_test.go
@@ -0,0 +1,112 @@
+package ocm_test
+
+import (
+ . "github.com/mandelsoft/goutils/testutils"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ . "ocm.software/ocm/api/helper/builder"
+
+ v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/extensions/accessmethods/ocm"
+ resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
+ "ocm.software/ocm/api/ocm/extensions/repositories/ctf"
+ "ocm.software/ocm/api/utils/accessio"
+ "ocm.software/ocm/api/utils/accessobj"
+ "ocm.software/ocm/api/utils/mime"
+)
+
+const (
+ ARCH = "/tmp/ctf"
+ COMP1 = "acme.org/test1"
+ COMP2 = "acme.org/test2"
+ VERS = "v1"
+ RSC1 = "resource1"
+ RSC2 = "resource2"
+ REF1 = "reference1"
+
+ DATA = "some test data"
+)
+
+var _ = Describe("Method", func() {
+ var env *Builder
+
+ BeforeEach(func() {
+ env = NewBuilder()
+ })
+
+ AfterEach(func() {
+ env.Cleanup()
+ })
+
+ Context("remote access", func() {
+ BeforeEach(func() {
+ env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() {
+ env.ComponentVersion(COMP1, VERS, func() {
+ env.Resource(RSC1, VERS, resourcetypes.PLAIN_TEXT, v1.LocalRelation, func() {
+ env.ExtraIdentity("other", "value")
+ env.BlobStringData(mime.MIME_TEXT, DATA)
+ })
+ })
+
+ env.ComponentVersion(COMP2, VERS, func() {
+ env.Reference(REF1, COMP1, VERS, func() {
+ env.ExtraIdentity("purpose", "test")
+ })
+ })
+ })
+ })
+
+ It("accesses artifact", func() {
+ spec := Must(ocm.New(COMP1, VERS, Must(ctf.NewRepositorySpec(accessobj.ACC_READONLY, ARCH, env)), v1.NewIdentity(RSC1, "other", "value")))
+
+ m := Must(spec.AccessMethod(&cpi.DummyComponentVersionAccess{env.OCMContext()}))
+ defer Close(m)
+ data := Must(m.Get())
+ Expect(string(data)).To(Equal(DATA))
+ })
+
+ It("accesses indirect artifact", func() {
+ spec := Must(ocm.New(COMP2, VERS, Must(ctf.NewRepositorySpec(accessobj.ACC_READONLY, ARCH, env)), v1.NewIdentity(RSC1, "other", "value"), v1.NewIdentity(REF1, "purpose", "test")))
+
+ m := Must(spec.AccessMethod(&cpi.DummyComponentVersionAccess{env.OCMContext()}))
+ defer Close(m)
+ data := Must(m.Get())
+ Expect(string(data)).To(Equal(DATA))
+ })
+ })
+
+ Context("local access", func() {
+ BeforeEach(func() {
+ env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() {
+ env.ComponentVersion(COMP1, VERS, func() {
+ env.Resource(RSC1, VERS, resourcetypes.PLAIN_TEXT, v1.LocalRelation, func() {
+ env.ExtraIdentity("other", "value")
+ env.BlobStringData(mime.MIME_TEXT, DATA)
+ })
+ })
+
+ env.ComponentVersion(COMP2, VERS, func() {
+ env.Resource(RSC2, VERS, resourcetypes.PLAIN_TEXT, v1.LocalRelation, func() {
+ env.Access(Must(ocm.New(COMP1, VERS, nil, v1.NewIdentity(RSC1, "other", "value"))))
+ })
+ })
+ })
+ })
+
+ It("accesses artifact", func() {
+ repo := Must(ctf.Open(env, accessobj.ACC_READONLY, ARCH, 0, env))
+ defer Close(repo, "repo")
+
+ cv := Must(repo.LookupComponentVersion(COMP2, VERS))
+ defer Close(cv, "cv")
+
+ ra := Must(cv.GetResourceByIndex(0))
+
+ m := Must(ra.AccessMethod())
+ defer Close(m)
+ data := Must(m.Get())
+ Expect(string(data)).To(Equal(DATA))
+ })
+ })
+})
diff --git a/api/ocm/extensions/accessmethods/ocm/suite_test.go b/api/ocm/extensions/accessmethods/ocm/suite_test.go
new file mode 100644
index 0000000000..cc788df8ec
--- /dev/null
+++ b/api/ocm/extensions/accessmethods/ocm/suite_test.go
@@ -0,0 +1,13 @@
+package ocm_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "OCM Access Test Suite")
+}
diff --git a/api/ocm/extensions/accessmethods/options/init.go b/api/ocm/extensions/accessmethods/options/init.go
index 8de3a5fdd9..7e815b8f30 100644
--- a/api/ocm/extensions/accessmethods/options/init.go
+++ b/api/ocm/extensions/accessmethods/options/init.go
@@ -12,6 +12,7 @@ const (
TYPE_STRING2STRINGSLICE = "string=string,string"
TYPE_STRINGCOLONSTRINGSLICE = "string:string,string"
TYPE_BYTES = "[]byte"
+ TYPE_IDENTITYPATH = "[]identity"
)
func init() {
@@ -26,6 +27,7 @@ func init() {
DefaultRegistry.RegisterValueType(TYPE_STRING2STRINGSLICE, NewStringSliceMapOptionType, "string map defined by dedicated assignment of comma separated strings")
DefaultRegistry.RegisterValueType(TYPE_STRINGCOLONSTRINGSLICE, NewStringSliceMapColonOptionType, "string map defined by dedicated assignment of comma separated strings")
DefaultRegistry.RegisterValueType(TYPE_BYTES, NewBytesOptionType, "byte value")
+ DefaultRegistry.RegisterValueType(TYPE_IDENTITYPATH, NewIdentityPathOptionType, "identity path")
}
func RegisterOption(o OptionType) OptionType {
diff --git a/api/ocm/extensions/accessmethods/options/mapping.go b/api/ocm/extensions/accessmethods/options/mapping.go
new file mode 100644
index 0000000000..e98e1449f0
--- /dev/null
+++ b/api/ocm/extensions/accessmethods/options/mapping.go
@@ -0,0 +1,32 @@
+package options
+
+import (
+ "fmt"
+
+ "github.com/mandelsoft/goutils/errors"
+
+ v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+)
+
+func MapRepository(in any) (any, error) {
+ uni, err := cpi.ParseRepo(in.(string))
+ if err != nil {
+ return nil, errors.ErrInvalidWrap(err, cpi.KIND_REPOSITORYSPEC, in.(string))
+ }
+
+ // TODO: basically a context is required, here.
+ spec, err := cpi.DefaultContext().MapUniformRepositorySpec(&uni)
+ if err != nil {
+ return nil, err
+ }
+ return cpi.ToGenericRepositorySpec(spec)
+}
+
+func MapResourceRef(in any) (any, error) {
+ list := in.([]v1.Identity)
+ if len(list) == 0 {
+ return nil, fmt.Errorf("empty resource reference")
+ }
+ return v1.NewResourceRef(list[0], list[1:]...), nil
+}
diff --git a/api/ocm/extensions/accessmethods/options/standard.go b/api/ocm/extensions/accessmethods/options/standard.go
index b207daf70b..70c5ad0009 100644
--- a/api/ocm/extensions/accessmethods/options/standard.go
+++ b/api/ocm/extensions/accessmethods/options/standard.go
@@ -48,7 +48,13 @@ var BucketOption = RegisterOption(NewStringOptionType("bucket", "bucket name"))
// VersionOption .
var VersionOption = RegisterOption(NewStringOptionType("accessVersion", "version for access specification"))
-// URLOption .
+// ComponentOption.
+var ComponentOption = RegisterOption(NewStringOptionType("accessComponent", "component for access specification"))
+
+// IdentityPathOption.
+var IdentityPathOption = RegisterOption(NewIdentityPathOptionType("identityPath", "identity path for specification"))
+
+// URLOption.
var URLOption = RegisterOption(NewStringOptionType("url", "artifact or server url"))
var HTTPHeaderOption = RegisterOption(NewStringSliceMapColonOptionType("header", "http headers"))
@@ -76,3 +82,6 @@ var NPMPackageOption = RegisterOption(NewStringOptionType("package", "npm packag
// NPMVersionOption sets the version of the npm package.
var NPMVersionOption = RegisterOption(NewStringOptionType("version", "npm package version"))
+
+// IdPathOption is a path of identity specs.
+var IdPathOption = RegisterOption(NewStringArrayOptionType("idpath", "identity path (attr=value{,attr=value}"))
diff --git a/api/ocm/extensions/accessmethods/options/types.go b/api/ocm/extensions/accessmethods/options/types.go
index 1788c3b808..f02ecd03f7 100644
--- a/api/ocm/extensions/accessmethods/options/types.go
+++ b/api/ocm/extensions/accessmethods/options/types.go
@@ -116,3 +116,10 @@ func NewBytesOptionType(name, desc string) OptionType {
valueType: TYPE_BYTES,
}
}
+
+func NewIdentityPathOptionType(name, desc string) OptionType {
+ return &option{
+ base: flagsets.NewIdentityPathOptionType(name, desc),
+ valueType: TYPE_IDENTITYPATH,
+ }
+}
diff --git a/api/ocm/extensions/repositories/comparch/format.go b/api/ocm/extensions/repositories/comparch/format.go
index 0946f33e97..de3548141d 100644
--- a/api/ocm/extensions/repositories/comparch/format.go
+++ b/api/ocm/extensions/repositories/comparch/format.go
@@ -6,6 +6,7 @@ import (
"github.com/mandelsoft/goutils/errors"
"github.com/mandelsoft/vfs/pkg/vfs"
+ "ocm.software/ocm/api/datacontext/attrs/vfsattr"
"ocm.software/ocm/api/ocm/compdesc"
"ocm.software/ocm/api/ocm/cpi"
"ocm.software/ocm/api/utils/accessio"
@@ -87,8 +88,12 @@ func GetFormat(name accessio.FileFormat) FormatHandler {
////////////////////////////////////////////////////////////////////////////////
-func Open(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (*Object, error) {
- o, create, err := accessobj.HandleAccessMode(acc, path, nil, opts...)
+func Open(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode vfs.FileMode, olist ...accessio.Option) (*Object, error) {
+ opts, err := accessio.AccessOptions(&accessio.StandardOptions{PathFileSystem: vfsattr.Get(ctx.OCMContext())}, olist...)
+ if err != nil {
+ return nil, err
+ }
+ o, create, err := accessobj.HandleAccessMode(acc, path, opts)
if err != nil {
return nil, err
}
diff --git a/api/ocm/interface.go b/api/ocm/interface.go
index 9ff49258a9..b4c5f93d89 100644
--- a/api/ocm/interface.go
+++ b/api/ocm/interface.go
@@ -25,6 +25,7 @@ const (
KIND_SOURCE = internal.KIND_SOURCE
KIND_REFERENCE = internal.KIND_REFERENCE
KIND_REPOSITORYSPEC = internal.KIND_REPOSITORYSPEC
+ KIND_OCM_REFERENCE = internal.KIND_OCM_REFERENCE
)
const CONTEXT_TYPE = internal.CONTEXT_TYPE
diff --git a/api/ocm/internal/accesstypes.go b/api/ocm/internal/accesstypes.go
index 03613d9abb..707c223e64 100644
--- a/api/ocm/internal/accesstypes.go
+++ b/api/ocm/internal/accesstypes.go
@@ -11,6 +11,7 @@ import (
"github.com/modern-go/reflect2"
"ocm.software/ocm/api/ocm/compdesc"
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/utils/cobrautils/flagsets/flagsetscheme"
"ocm.software/ocm/api/utils/errkind"
"ocm.software/ocm/api/utils/refmgmt"
@@ -59,6 +60,12 @@ type GlobalAccessProvider interface {
GlobalAccessSpec(ctx Context) AccessSpec
}
+// DigestSpecProvider is an optional interface for an access method
+// to provide am Digest Specification.
+type DigestSpecProvider interface {
+ GetDigestSpec() (*metav1.DigestSpec, error)
+}
+
// AccessMethodImpl is the implementation interface
// for access methods provided by access types. It describes
// the access to a dedicated resource
diff --git a/api/ocm/internal/errors.go b/api/ocm/internal/errors.go
index aa4888505a..8c52e130db 100644
--- a/api/ocm/internal/errors.go
+++ b/api/ocm/internal/errors.go
@@ -17,6 +17,7 @@ const (
KIND_SOURCE = "component source"
KIND_REFERENCE = compdesc.KIND_REFERENCE
KIND_REPOSITORYSPEC = "repository specification"
+ KIND_OCM_REFERENCE = "ocm reference"
)
func ErrComponentVersionNotFound(name, version string) error {
diff --git a/api/ocm/ocmutils/localize/config.go b/api/ocm/ocmutils/localize/config.go
index 5c89b9a1bf..08d55cb557 100644
--- a/api/ocm/ocmutils/localize/config.go
+++ b/api/ocm/ocmutils/localize/config.go
@@ -9,7 +9,7 @@ import (
"ocm.software/ocm/api/ocm"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
- utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/utils/runtime"
"ocm.software/ocm/api/utils/spiff"
)
@@ -38,7 +38,7 @@ func Configure(
stubs := spiff.Options{}
for i, lib := range libraries {
opt, err := func() (spiff.OptionFunction, error) {
- res, eff, err := utils.ResolveResourceReference(cv, lib, resolver)
+ res, eff, err := resourcerefs.ResolveResourceReference(cv, lib, resolver)
if err != nil {
return nil, errors.ErrNotFound("library resource %s not found", lib.String())
}
diff --git a/api/ocm/ocmutils/localize/format.go b/api/ocm/ocmutils/localize/format.go
index b84df070b0..04404c9e75 100644
--- a/api/ocm/ocmutils/localize/format.go
+++ b/api/ocm/ocmutils/localize/format.go
@@ -10,6 +10,7 @@ import (
"ocm.software/ocm/api/ocm"
v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/utils/runtime"
)
@@ -53,7 +54,7 @@ func (m *ImageMapping) Evaluate(idx int, cv ocm.ComponentVersionAccess, resolver
name = fmt.Sprintf("%s %d", name, idx+1)
}
}
- acc, rcv, err := utils.ResolveResourceReference(cv, m.ResourceReference, resolver)
+ acc, rcv, err := resourcerefs.ResolveResourceReference(cv, m.ResourceReference, resolver)
if err != nil {
return nil, errors.Wrapf(err, "mapping", fmt.Sprintf("%s (%s)", name, &m.ResourceReference))
}
diff --git a/api/ocm/ocmutils/localize/instantiate.go b/api/ocm/ocmutils/localize/instantiate.go
index a510ca8000..ae0d8594cb 100644
--- a/api/ocm/ocmutils/localize/instantiate.go
+++ b/api/ocm/ocmutils/localize/instantiate.go
@@ -7,7 +7,7 @@ import (
"ocm.software/ocm/api/ocm"
resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
"ocm.software/ocm/api/ocm/extensions/download"
- utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
common "ocm.software/ocm/api/utils/misc"
)
@@ -22,7 +22,7 @@ func Instantiate(rules *InstantiationRules, cv ocm.ComponentVersionAccess, resol
return errors.Wrapf(err, "applying instance configuration")
}
- template, rcv, err := utils.ResolveResourceReference(cv, rules.Template, resolver)
+ template, rcv, err := resourcerefs.ResolveResourceReference(cv, rules.Template, resolver)
if err != nil {
return errors.Wrapf(err, "resolving template resource %s", rules.Template)
}
diff --git a/api/ocm/ocmutils/resource.go b/api/ocm/ocmutils/resource.go
index 9b4df7e262..40095df37c 100644
--- a/api/ocm/ocmutils/resource.go
+++ b/api/ocm/ocmutils/resource.go
@@ -5,6 +5,8 @@ import (
"ocm.software/ocm/api/ocm"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/resolvers"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/utils/blobaccess/blobaccess"
"ocm.software/ocm/api/utils/iotools"
)
@@ -17,12 +19,12 @@ func GetResourceDataForPath(cv ocm.ComponentVersionAccess, id metav1.Identity, p
return GetResourceDataForRef(cv, metav1.NewNestedResourceRef(id, path), resolvers...)
}
-func GetResourceDataForRef(cv ocm.ComponentVersionAccess, ref metav1.ResourceReference, resolvers ...ocm.ComponentVersionResolver) ([]byte, error) {
- var res ocm.ComponentVersionResolver
- if len(resolvers) > 0 {
- res = ocm.NewCompoundResolver(resolvers...)
+func GetResourceDataForRef(cv ocm.ComponentVersionAccess, ref metav1.ResourceReference, reslist ...ocm.ComponentVersionResolver) ([]byte, error) {
+ var res resolvers.ComponentVersionResolver
+ if len(reslist) > 0 {
+ res = resolvers.NewCompoundResolver(reslist...)
}
- a, c, err := ResolveResourceReference(cv, ref, res)
+ a, c, err := resourcerefs.ResolveResourceReference(cv, ref, res)
if err != nil {
return nil, err
}
@@ -39,12 +41,12 @@ func GetResourceReaderForPath(cv ocm.ComponentVersionAccess, id metav1.Identity,
return GetResourceReaderForRef(cv, metav1.NewNestedResourceRef(id, path), resolvers...)
}
-func GetResourceReaderForRef(cv ocm.ComponentVersionAccess, ref metav1.ResourceReference, resolvers ...ocm.ComponentVersionResolver) (io.ReadCloser, error) {
- var res ocm.ComponentVersionResolver
- if len(resolvers) > 0 {
- res = ocm.NewCompoundResolver(resolvers...)
+func GetResourceReaderForRef(cv ocm.ComponentVersionAccess, ref metav1.ResourceReference, reslist ...ocm.ComponentVersionResolver) (io.ReadCloser, error) {
+ var res resolvers.ComponentVersionResolver
+ if len(reslist) > 0 {
+ res = resolvers.NewCompoundResolver(reslist...)
}
- a, c, err := ResolveResourceReference(cv, ref, res)
+ a, c, err := resourcerefs.ResolveResourceReference(cv, ref, res)
if err != nil {
return nil, err
}
diff --git a/api/ocm/ocmutils/resourceref.go b/api/ocm/ocmutils/resourceref.go
index ec2b86b2fc..40aa8880e2 100644
--- a/api/ocm/ocmutils/resourceref.go
+++ b/api/ocm/ocmutils/resourceref.go
@@ -1,103 +1,22 @@
package ocmutils
import (
- "fmt"
-
- . "github.com/mandelsoft/goutils/finalizer"
-
- "github.com/mandelsoft/goutils/errors"
-
- "ocm.software/ocm/api/ocm"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
- common "ocm.software/ocm/api/utils/misc"
+ ocm "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/resourcerefs"
)
+// Deprectated: use resourcerefs.ResolveReferencePath.
func ResolveReferencePath(cv ocm.ComponentVersionAccess, path []metav1.Identity, resolver ocm.ComponentVersionResolver) (ocm.ComponentVersionAccess, error) {
- if cv == nil {
- return nil, fmt.Errorf("no component version specified")
- }
- eff, err := cv.Dup()
- if err != nil {
- return nil, errors.Wrapf(err, "component version already closed")
- }
-
- var final Finalizer
- defer final.Finalize()
-
- for _, cr := range path {
- final.Close(eff)
- cref, err := eff.GetReference(cr)
- if err != nil {
- return nil, errors.Wrapf(err, "%s", common.VersionedElementKey(cv))
- }
-
- compoundResolver := ocm.NewCompoundResolver(eff.Repository(), resolver)
- eff, err = compoundResolver.LookupComponentVersion(cref.GetComponentName(), cref.GetVersion())
- if err != nil {
- return nil, errors.Wrapf(err, "cannot resolve component version for reference %s", cr.String())
- }
- if eff == nil {
- return nil, errors.ErrNotFound(ocm.KIND_COMPONENTVERSION, cref.String())
- }
- final.Finalize()
- }
- return eff, nil
+ return resourcerefs.ResolveReferencePath(cv, path, resolver)
}
+// Deprecated: use resourcerefs.MatchResourceReference.
func MatchResourceReference(cv ocm.ComponentVersionAccess, typ string, ref metav1.ResourceReference, resolver ocm.ComponentVersionResolver) (ocm.ResourceAccess, ocm.ComponentVersionAccess, error) {
- eff, err := ResolveReferencePath(cv, ref.ReferencePath, resolver)
- if err != nil {
- return nil, nil, err
- }
-
- if len(eff.GetDescriptor().Resources) == 0 && len(ref.Resource) == 0 {
- return nil, nil, errors.ErrNotFound(ocm.KIND_RESOURCE)
- }
-outer:
- for i, r := range eff.GetDescriptor().Resources {
- if r.Type != typ && typ != "" {
- continue
- }
- for k, v := range ref.Resource {
- switch k {
- case metav1.SystemIdentityName:
- if v != r.Name {
- continue outer
- }
- case metav1.SystemIdentityVersion:
- if v != r.Version {
- continue outer
- }
- default:
- if r.ExtraIdentity == nil || r.ExtraIdentity[k] != v {
- continue outer
- }
- }
- }
- res, err := eff.GetResourceByIndex(i)
- if err != nil {
- eff.Close()
- return nil, nil, err
- }
- return res, eff, nil
- }
- eff.Close()
- return nil, nil, errors.ErrNotFound(ocm.KIND_RESOURCE, ref.Resource.String())
+ return resourcerefs.MatchResourceReference(cv, typ, ref, resolver)
}
+// Deprecated: use resourcerefs.ResolveResourceReference.
func ResolveResourceReference(cv ocm.ComponentVersionAccess, ref metav1.ResourceReference, resolver ocm.ComponentVersionResolver) (ocm.ResourceAccess, ocm.ComponentVersionAccess, error) {
- if len(ref.Resource) == 0 || len(ref.Resource["name"]) == 0 {
- return nil, nil, errors.Newf("at least resource name must be specified for resource reference")
- }
-
- eff, err := ResolveReferencePath(cv, ref.ReferencePath, resolver)
- if err != nil {
- return nil, nil, err
- }
- r, err := eff.GetResource(ref.Resource)
- if err != nil {
- eff.Close()
- return nil, nil, err
- }
- return r, eff, nil
+ return resourcerefs.ResolveResourceReference(cv, ref, resolver)
}
diff --git a/api/ocm/ref.go b/api/ocm/ref.go
index cf9de6550c..72f0b08e84 100644
--- a/api/ocm/ref.go
+++ b/api/ocm/ref.go
@@ -5,99 +5,19 @@ import (
"strings"
"github.com/mandelsoft/goutils/errors"
- "github.com/mandelsoft/goutils/general"
"ocm.software/ocm/api/ocm/cpi"
"ocm.software/ocm/api/ocm/grammar"
common "ocm.software/ocm/api/utils/misc"
)
-const (
- KIND_OCM_REFERENCE = "ocm reference"
-)
-
// ParseRepo parses a standard ocm repository reference into a internal representation.
func ParseRepo(ref string) (UniformRepositorySpec, error) {
- create := false
- if strings.HasPrefix(ref, "+") {
- create = true
- ref = ref[1:]
- }
- if strings.HasPrefix(ref, ".") || strings.HasPrefix(ref, "/") {
- return cpi.HandleRef(UniformRepositorySpec{
- Info: ref,
- CreateIfMissing: create,
- })
- }
- match := grammar.AnchoredRepositoryRegexp.FindSubmatch([]byte(ref))
- if match != nil {
- h := string(match[1])
- t, _ := grammar.SplitTypeSpec(h)
- return cpi.HandleRef(UniformRepositorySpec{
- Type: t,
- TypeHint: h,
- Scheme: string(match[2]),
- Host: string(match[3]),
- SubPath: string(match[4]),
- CreateIfMissing: create,
- })
- }
-
- match = grammar.AnchoredSchemedHostPortRepositoryRegexp.FindSubmatch([]byte(ref))
- if match != nil {
- h := string(match[1])
- t, _ := grammar.SplitTypeSpec(h)
- return cpi.HandleRef(UniformRepositorySpec{
- Type: t,
- TypeHint: h,
- Scheme: string(match[2]),
- Host: string(match[3]),
- SubPath: string(match[4]),
- CreateIfMissing: create,
- })
- }
-
- match = grammar.AnchoredHostWithPortRepositoryRegexp.FindSubmatch([]byte(ref))
- if match != nil {
- h := string(match[1])
- t, _ := grammar.SplitTypeSpec(h)
- return cpi.HandleRef(UniformRepositorySpec{
- Type: t,
- TypeHint: h,
- Scheme: string(match[2]),
- Host: string(match[3]),
- SubPath: string(match[4]),
- CreateIfMissing: create,
- })
- }
-
- match = grammar.AnchoredGenericRepositoryRegexp.FindSubmatch([]byte(ref))
- if match == nil {
- return UniformRepositorySpec{}, errors.ErrInvalid(KIND_OCM_REFERENCE, ref)
- }
- h := string(match[1])
- t, _ := grammar.SplitTypeSpec(h)
- return cpi.HandleRef(UniformRepositorySpec{
- Type: t,
- TypeHint: h,
- Info: string(match[2]),
- CreateIfMissing: create,
- })
+ return cpi.ParseRepo(ref)
}
func ParseRepoToSpec(ctx Context, ref string, create ...bool) (RepositorySpec, error) {
- uni, err := ParseRepo(ref)
- if err != nil {
- return nil, errors.ErrInvalidWrap(err, KIND_REPOSITORYSPEC, ref)
- }
- if !uni.CreateIfMissing {
- uni.CreateIfMissing = general.Optional(create...)
- }
- repoSpec, err := ctx.MapUniformRepositorySpec(&uni)
- if err != nil {
- return nil, errors.ErrInvalidWrap(err, KIND_REPOSITORYSPEC, ref)
- }
- return repoSpec, nil
+ return cpi.ParseRepoToSpec(ctx, ref, create...)
}
// RefSpec is a go internal representation of a oci reference.
diff --git a/api/ocm/resolver.go b/api/ocm/resolver.go
index 43f078b8bb..5351623ffa 100644
--- a/api/ocm/resolver.go
+++ b/api/ocm/resolver.go
@@ -1,151 +1,35 @@
package ocm
import (
- "sync"
-
- "github.com/mandelsoft/goutils/errors"
- "github.com/mandelsoft/goutils/sliceutils"
"golang.org/x/exp/slices"
"ocm.software/ocm/api/ocm/internal"
- common "ocm.software/ocm/api/utils/misc"
+ "ocm.software/ocm/api/ocm/resolvers"
)
-type DedicatedResolver []ComponentVersionAccess
-
-var (
- _ ComponentVersionResolver = (*DedicatedResolver)(nil)
- _ ComponentResolver = (*DedicatedResolver)(nil)
-)
+// Deprecated: use resolvers.DedicatedResolver.
+type DedicatedResolver = resolvers.DedicatedResolver
+// Deprecated: use resolvers.NewDedicatedResolver.
func NewDedicatedResolver(cv ...ComponentVersionAccess) ComponentVersionResolver {
- return DedicatedResolver(slices.Clone(cv))
-}
-
-func (d DedicatedResolver) Repository() (Repository, error) {
- return nil, nil
-}
-
-func (d DedicatedResolver) LookupComponentVersion(name string, version string) (ComponentVersionAccess, error) {
- for _, cv := range d {
- if cv.GetName() == name && cv.GetVersion() == version {
- return cv.Dup()
- }
- }
- return nil, nil
-}
-
-func (d DedicatedResolver) LookupComponentProviders(name string) []ResolvedComponentProvider {
- for _, c := range d {
- if c.GetName() == name {
- return []ResolvedComponentProvider{d}
- }
- }
- return nil
-}
-
-func (d DedicatedResolver) LookupComponent(name string) (ResolvedComponentVersionProvider, error) {
- return &versionProvider{name, d}, nil
-}
-
-type versionProvider struct {
- name string
- resolver DedicatedResolver
-}
-
-func (p *versionProvider) GetName() string {
- return p.name
-}
-
-func (p *versionProvider) LookupVersion(vers string) (ComponentVersionAccess, error) {
- return p.resolver.LookupComponentVersion(p.name, vers)
+ return resolvers.DedicatedResolver(slices.Clone(cv))
}
-func (p *versionProvider) ListVersions() ([]string, error) {
- var vers []string
- for _, c := range p.resolver {
- if c.GetName() == p.name {
- vers = sliceutils.AppendUnique(vers, c.GetVersion())
- }
- }
- return vers, nil
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-type CompoundResolver struct {
- lock sync.RWMutex
- resolvers []ComponentVersionResolver
-}
-
-var (
- _ ComponentVersionResolver = (*CompoundResolver)(nil)
- _ ComponentResolver = (*CompoundResolver)(nil)
-)
+// Deprecated: use resolvers.CompoundResolver.
+type CompoundResolver = resolvers.CompoundResolver
+// Deprecated: use resolvers.NewCompoundResolver.
func NewCompoundResolver(res ...ComponentVersionResolver) ComponentVersionResolver {
- for i := 0; i < len(res); i++ {
- if res[i] == nil {
- res = append(res[:i], res[i+1:]...)
- i--
- }
- }
- if len(res) == 1 {
- return res[0]
- }
- return &CompoundResolver{resolvers: res}
-}
-
-func (c *CompoundResolver) LookupComponentVersion(name string, version string) (ComponentVersionAccess, error) {
- c.lock.RLock()
- defer c.lock.RUnlock()
- for _, r := range c.resolvers {
- if r == nil {
- continue
- }
- cv, err := r.LookupComponentVersion(name, version)
- if err == nil && cv != nil {
- return cv, nil
- }
- if !errors.IsErrNotFoundKind(err, KIND_COMPONENTVERSION) && !errors.IsErrNotFoundKind(err, KIND_COMPONENT) {
- return nil, err
- }
- }
- return nil, errors.ErrNotFound(KIND_OCM_REFERENCE, common.NewNameVersion(name, version).String())
+ return resolvers.NewCompoundResolver(res...)
}
-func (c *CompoundResolver) LookupComponentProviders(name string) []ResolvedComponentProvider {
- c.lock.RLock()
- defer c.lock.RUnlock()
-
- var result []RepositoryProvider
-
- for _, r := range c.resolvers {
- if cr, ok := r.(ComponentResolver); ok {
- result = append(result, cr.LookupComponentProviders(name)...)
- }
- }
- return result
-}
-
-func (c *CompoundResolver) AddResolver(r ComponentVersionResolver) {
- c.lock.Lock()
- defer c.lock.Unlock()
- c.resolvers = append(c.resolvers, r)
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-type MatchingResolver interface {
- ComponentVersionResolver
- ContextProvider
-
- AddRule(prefix string, spec RepositorySpec, prio ...int)
- Finalize() error
-}
+// Deprecated: use resolvers.MatchingResolver.
+type MatchingResolver = resolvers.MatchingResolver
-func NewMatchingResolver(ctx ContextProvider) MatchingResolver {
+// Deprecated: use resolvers.NewMatchingResolver.
+func NewMatchingResolver(ctx ContextProvider) resolvers.MatchingResolver {
return internal.NewMatchingResolver(ctx.OCMContext())
}
+// Deprecated: use resolvers.ResolverRule.
type ResolverRule = internal.ResolverRule
diff --git a/api/ocm/resolvers/forward.go b/api/ocm/resolvers/forward.go
new file mode 100644
index 0000000000..4bdd370ddd
--- /dev/null
+++ b/api/ocm/resolvers/forward.go
@@ -0,0 +1,23 @@
+package resolvers
+
+import (
+ "ocm.software/ocm/api/ocm/internal"
+)
+
+type (
+ ContextProvider = internal.ContextProvider
+ RepositorySpec = internal.RepositorySpec
+ ComponentVersionAccess = internal.ComponentVersionAccess
+ ComponentVersionResolver = internal.ComponentVersionResolver
+ ComponentResolver = internal.ComponentResolver
+ Repository = internal.Repository
+ ResolvedComponentVersionProvider = internal.ResolvedComponentVersionProvider
+ ResolvedComponentProvider = internal.ResolvedComponentProvider
+ ResolvedRepositoryProvider = internal.ResolvedComponentProvider
+)
+
+const (
+ KIND_COMPONENTVERSION = internal.KIND_COMPONENTVERSION
+ KIND_COMPONENT = internal.KIND_COMPONENT
+ KIND_OCM_REFERENCE = internal.KIND_OCM_REFERENCE
+)
diff --git a/api/ocm/resolvers/resolver.go b/api/ocm/resolvers/resolver.go
new file mode 100644
index 0000000000..a500339b86
--- /dev/null
+++ b/api/ocm/resolvers/resolver.go
@@ -0,0 +1,151 @@
+package resolvers
+
+import (
+ "sync"
+
+ "github.com/mandelsoft/goutils/errors"
+ "github.com/mandelsoft/goutils/sliceutils"
+ "golang.org/x/exp/slices"
+
+ "ocm.software/ocm/api/ocm/internal"
+ common "ocm.software/ocm/api/utils/misc"
+)
+
+type DedicatedResolver []ComponentVersionAccess
+
+var (
+ _ ComponentVersionResolver = (*DedicatedResolver)(nil)
+ _ ComponentResolver = (*DedicatedResolver)(nil)
+)
+
+func NewDedicatedResolver(cv ...ComponentVersionAccess) ComponentVersionResolver {
+ return DedicatedResolver(slices.Clone(cv))
+}
+
+func (d DedicatedResolver) Repository() (Repository, error) {
+ return nil, nil
+}
+
+func (d DedicatedResolver) LookupComponentVersion(name string, version string) (ComponentVersionAccess, error) {
+ for _, cv := range d {
+ if cv.GetName() == name && cv.GetVersion() == version {
+ return cv.Dup()
+ }
+ }
+ return nil, nil
+}
+
+func (d DedicatedResolver) LookupComponentProviders(name string) []ResolvedComponentProvider {
+ for _, c := range d {
+ if c.GetName() == name {
+ return []ResolvedComponentProvider{d}
+ }
+ }
+ return nil
+}
+
+func (d DedicatedResolver) LookupComponent(name string) (ResolvedComponentVersionProvider, error) {
+ return &versionProvider{name, d}, nil
+}
+
+type versionProvider struct {
+ name string
+ resolver DedicatedResolver
+}
+
+func (p *versionProvider) GetName() string {
+ return p.name
+}
+
+func (p *versionProvider) LookupVersion(vers string) (ComponentVersionAccess, error) {
+ return p.resolver.LookupComponentVersion(p.name, vers)
+}
+
+func (p *versionProvider) ListVersions() ([]string, error) {
+ var vers []string
+ for _, c := range p.resolver {
+ if c.GetName() == p.name {
+ vers = sliceutils.AppendUnique(vers, c.GetVersion())
+ }
+ }
+ return vers, nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type CompoundResolver struct {
+ lock sync.RWMutex
+ resolvers []ComponentVersionResolver
+}
+
+var (
+ _ ComponentVersionResolver = (*CompoundResolver)(nil)
+ _ ComponentResolver = (*CompoundResolver)(nil)
+)
+
+func NewCompoundResolver(res ...ComponentVersionResolver) ComponentVersionResolver {
+ for i := 0; i < len(res); i++ {
+ if res[i] == nil {
+ res = append(res[:i], res[i+1:]...)
+ i--
+ }
+ }
+ if len(res) == 1 {
+ return res[0]
+ }
+ return &CompoundResolver{resolvers: res}
+}
+
+func (c *CompoundResolver) LookupComponentVersion(name string, version string) (ComponentVersionAccess, error) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ for _, r := range c.resolvers {
+ if r == nil {
+ continue
+ }
+ cv, err := r.LookupComponentVersion(name, version)
+ if err == nil && cv != nil {
+ return cv, nil
+ }
+ if !errors.IsErrNotFoundKind(err, KIND_COMPONENTVERSION) && !errors.IsErrNotFoundKind(err, KIND_COMPONENT) {
+ return nil, err
+ }
+ }
+ return nil, errors.ErrNotFound(KIND_OCM_REFERENCE, common.NewNameVersion(name, version).String())
+}
+
+func (c *CompoundResolver) LookupComponentProviders(name string) []ResolvedComponentProvider {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+
+ var result []ResolvedRepositoryProvider
+
+ for _, r := range c.resolvers {
+ if cr, ok := r.(ComponentResolver); ok {
+ result = append(result, cr.LookupComponentProviders(name)...)
+ }
+ }
+ return result
+}
+
+func (c *CompoundResolver) AddResolver(r ComponentVersionResolver) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ c.resolvers = append(c.resolvers, r)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type MatchingResolver interface {
+ ComponentVersionResolver
+ ContextProvider
+
+ AddRule(prefix string, spec RepositorySpec, prio ...int)
+ Finalize() error
+}
+
+func NewMatchingResolver(ctx ContextProvider) MatchingResolver {
+ return internal.NewMatchingResolver(ctx.OCMContext())
+}
+
+type ResolverRule = internal.ResolverRule
diff --git a/api/ocm/resolver_test.go b/api/ocm/resolvers/resolver_test.go
similarity index 93%
rename from api/ocm/resolver_test.go
rename to api/ocm/resolvers/resolver_test.go
index 321bd952b3..47a20d0c79 100644
--- a/api/ocm/resolver_test.go
+++ b/api/ocm/resolvers/resolver_test.go
@@ -1,4 +1,4 @@
-package ocm_test
+package resolvers_test
import (
"fmt"
@@ -13,6 +13,7 @@ import (
"ocm.software/ocm/api/ocm/extensions/repositories/ctf"
"ocm.software/ocm/api/ocm/extensions/repositories/ocireg"
"ocm.software/ocm/api/ocm/internal"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/utils/accessio"
"ocm.software/ocm/api/utils/accessobj"
)
@@ -96,13 +97,13 @@ var _ = Describe("resolver", func() {
})
})
-func Check(r ocm.ResolverRule, prefix string, spec ocm.RepositorySpec, prio int) {
+func Check(r resolvers.ResolverRule, prefix string, spec ocm.RepositorySpec, prio int) {
ExpectWithOffset(1, r.GetPrefix()).To(Equal(prefix))
ExpectWithOffset(1, r.GetPriority()).To(Equal(prio))
ExpectWithOffset(1, r.GetSpecification()).To(BeIdenticalTo(spec))
}
-func Print(rules []ocm.ResolverRule) {
+func Print(rules []resolvers.ResolverRule) {
list := []string{}
for _, r := range rules {
list = append(list, fmt.Sprintf("[%d]%s", r.GetPriority(), r.GetPrefix()))
diff --git a/api/ocm/resolvers/suite_test.go b/api/ocm/resolvers/suite_test.go
new file mode 100644
index 0000000000..71105f21c3
--- /dev/null
+++ b/api/ocm/resolvers/suite_test.go
@@ -0,0 +1,13 @@
+package resolvers_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "OCM Resolvers Test Suite")
+}
diff --git a/api/ocm/resourcerefs/resourceref.go b/api/ocm/resourcerefs/resourceref.go
new file mode 100644
index 0000000000..67aa27afd5
--- /dev/null
+++ b/api/ocm/resourcerefs/resourceref.go
@@ -0,0 +1,104 @@
+package resourcerefs
+
+import (
+ "fmt"
+
+ . "github.com/mandelsoft/goutils/finalizer"
+
+ "github.com/mandelsoft/goutils/errors"
+
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ ocm "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/resolvers"
+ common "ocm.software/ocm/api/utils/misc"
+)
+
+func ResolveReferencePath(cv ocm.ComponentVersionAccess, path []metav1.Identity, resolver ocm.ComponentVersionResolver) (ocm.ComponentVersionAccess, error) {
+ if cv == nil {
+ return nil, fmt.Errorf("no component version specified")
+ }
+ eff, err := cv.Dup()
+ if err != nil {
+ return nil, errors.Wrapf(err, "component version already closed")
+ }
+
+ var final Finalizer
+ defer final.Finalize()
+
+ for _, cr := range path {
+ final.Close(eff)
+ cref, err := eff.GetReference(cr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "%s", common.VersionedElementKey(cv))
+ }
+
+ compoundResolver := resolvers.NewCompoundResolver(eff.Repository(), resolver)
+ eff, err = compoundResolver.LookupComponentVersion(cref.GetComponentName(), cref.GetVersion())
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot resolve component version for reference %s", cr.String())
+ }
+ if eff == nil {
+ return nil, errors.ErrNotFound(ocm.KIND_COMPONENTVERSION, cref.String())
+ }
+ final.Finalize()
+ }
+ return eff, nil
+}
+
+func MatchResourceReference(cv ocm.ComponentVersionAccess, typ string, ref metav1.ResourceReference, resolver resolvers.ComponentVersionResolver) (ocm.ResourceAccess, ocm.ComponentVersionAccess, error) {
+ eff, err := ResolveReferencePath(cv, ref.ReferencePath, resolver)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if len(eff.GetDescriptor().Resources) == 0 && len(ref.Resource) == 0 {
+ return nil, nil, errors.ErrNotFound(ocm.KIND_RESOURCE)
+ }
+outer:
+ for i, r := range eff.GetDescriptor().Resources {
+ if r.Type != typ && typ != "" {
+ continue
+ }
+ for k, v := range ref.Resource {
+ switch k {
+ case metav1.SystemIdentityName:
+ if v != r.Name {
+ continue outer
+ }
+ case metav1.SystemIdentityVersion:
+ if v != r.Version {
+ continue outer
+ }
+ default:
+ if r.ExtraIdentity == nil || r.ExtraIdentity[k] != v {
+ continue outer
+ }
+ }
+ }
+ res, err := eff.GetResourceByIndex(i)
+ if err != nil {
+ eff.Close()
+ return nil, nil, err
+ }
+ return res, eff, nil
+ }
+ eff.Close()
+ return nil, nil, errors.ErrNotFound(ocm.KIND_RESOURCE, ref.Resource.String())
+}
+
+func ResolveResourceReference(cv ocm.ComponentVersionAccess, ref metav1.ResourceReference, resolver ocm.ComponentVersionResolver) (ocm.ResourceAccess, ocm.ComponentVersionAccess, error) {
+ if len(ref.Resource) == 0 || len(ref.Resource["name"]) == 0 {
+ return nil, nil, errors.Newf("at least resource name must be specified for resource reference")
+ }
+
+ eff, err := ResolveReferencePath(cv, ref.ReferencePath, resolver)
+ if err != nil {
+ return nil, nil, err
+ }
+ r, err := eff.GetResource(ref.Resource)
+ if err != nil {
+ eff.Close()
+ return nil, nil, err
+ }
+ return r, eff, nil
+}
diff --git a/api/ocm/ocmutils/resourceref_test.go b/api/ocm/resourcerefs/resourceref_test.go
similarity index 95%
rename from api/ocm/ocmutils/resourceref_test.go
rename to api/ocm/resourcerefs/resourceref_test.go
index b85c26fb6b..f8a0488dbc 100644
--- a/api/ocm/ocmutils/resourceref_test.go
+++ b/api/ocm/resourcerefs/resourceref_test.go
@@ -1,4 +1,4 @@
-package ocmutils_test
+package resourcerefs_test
import (
"io"
@@ -8,10 +8,11 @@ import (
. "github.com/onsi/gomega"
. "ocm.software/ocm/api/helper/builder"
- "ocm.software/ocm/api/ocm"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ ocm "ocm.software/ocm/api/ocm/cpi"
"ocm.software/ocm/api/ocm/extensions/repositories/ctf"
utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/tech/signing/handlers/rsa"
"ocm.software/ocm/api/utils/accessio"
"ocm.software/ocm/api/utils/accessobj"
@@ -131,7 +132,7 @@ var _ = Describe("resolving local resource references", func() {
Close(dup)
ref := metav1.NewResourceRef(metav1.NewIdentity("topdata"))
- _, _, err := utils.ResolveResourceReference(dup, ref, nil)
+ _, _, err := resourcerefs.ResolveResourceReference(dup, ref, nil)
MustFailWithMessage(err, "component version already closed: closed")
})
})
diff --git a/api/ocm/resourcerefs/suite_test.go b/api/ocm/resourcerefs/suite_test.go
new file mode 100644
index 0000000000..297380802e
--- /dev/null
+++ b/api/ocm/resourcerefs/suite_test.go
@@ -0,0 +1,13 @@
+package resourcerefs_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "OCM ResourceRefs Test Suite")
+}
diff --git a/api/ocm/tools/signing/options.go b/api/ocm/tools/signing/options.go
index ba735785ee..b7fe9a72e7 100644
--- a/api/ocm/tools/signing/options.go
+++ b/api/ocm/tools/signing/options.go
@@ -12,6 +12,7 @@ import (
"ocm.software/ocm/api/ocm"
"ocm.software/ocm/api/ocm/compdesc"
"ocm.software/ocm/api/ocm/extensions/attrs/signingattr"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/tech/signing"
"ocm.software/ocm/api/tech/signing/hasher/sha256"
"ocm.software/ocm/api/tech/signing/signutils"
@@ -221,7 +222,7 @@ func Resolver(h ...ocm.ComponentVersionResolver) Option {
}
func (o *resolver) ApplySigningOption(opts *Options) {
- opts.Resolver = ocm.NewCompoundResolver(append([]ocm.ComponentVersionResolver{opts.Resolver}, o.resolver...)...)
+ opts.Resolver = resolvers.NewCompoundResolver(append([]ocm.ComponentVersionResolver{opts.Resolver}, o.resolver...)...)
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/api/ocm/tools/signing/signing_test.go b/api/ocm/tools/signing/signing_test.go
index 1fcb2a9013..32c988daaf 100644
--- a/api/ocm/tools/signing/signing_test.go
+++ b/api/ocm/tools/signing/signing_test.go
@@ -26,6 +26,7 @@ import (
"ocm.software/ocm/api/ocm/extensions/attrs/signingattr"
"ocm.software/ocm/api/ocm/extensions/repositories/composition"
"ocm.software/ocm/api/ocm/extensions/repositories/ctf"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/ocm/tools/signing/signingtest"
"ocm.software/ocm/api/tech/signing"
"ocm.software/ocm/api/tech/signing/handlers/rsa"
@@ -137,7 +138,7 @@ var _ = Describe("access method", func() {
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
archcloser := session.AddCloser(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv := Must(resolver.LookupComponentVersion(COMPONENTA, VERSION))
closer := session.AddCloser(cv)
@@ -226,7 +227,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v
src, err := ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env)
Expect(err).To(Succeed())
archcloser := session.AddCloser(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv, err := resolver.LookupComponentVersion(COMPONENTA, VERSION)
Expect(err).To(Succeed())
@@ -305,7 +306,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v
src, err := ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env)
Expect(err).To(Succeed())
archcloser := session.AddCloser(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv, err := resolver.LookupComponentVersion(COMPONENTA, VERSION)
Expect(err).To(Succeed())
@@ -356,7 +357,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v
src, err := ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env)
Expect(err).To(Succeed())
archcloser := session.AddCloser(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv, err := resolver.LookupComponentVersion(COMPONENTB, VERSION)
Expect(err).To(Succeed())
@@ -435,7 +436,7 @@ applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1]
src, err := ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env)
Expect(err).To(Succeed())
session.AddCloser(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv, err := resolver.LookupComponentVersion(COMPONENTA, VERSION)
Expect(err).To(Succeed())
@@ -593,7 +594,7 @@ applying to version "github.com/mandelsoft/ref:v1"[github.com/mandelsoft/ref:v1]
Expect(err).To(Succeed())
arch.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
log := HashComponent(resolver, COMPONENTD, D_COMPD, DigestMode(c.Mode()))
@@ -677,7 +678,7 @@ applying to version "github.com/mandelsoft/top:v1"[github.com/mandelsoft/top:v1]
Expect(err).To(Succeed())
arch.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
log := SignComponent(resolver, SIGNATURE, COMPONENTD, D_COMPD, DigestMode(c.Mode()))
@@ -755,7 +756,7 @@ applying to version "github.com/mandelsoft/top:v1"[github.com/mandelsoft/top:v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
arch.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
log := SignComponent(resolver, SIGNATURE, COMPONENTB, subst["D_COMPB_X"], DigestMode(DIGESTMODE_TOP), HashByAlgo(sha512.Algorithm))
Expect(log).To(StringEqualTrimmedWithContext(`
@@ -847,7 +848,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
arch.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
fmt.Printf("SIGN D\n")
log := SignComponent(resolver, SIGNATURE, COMPONENTD, digestD, DigestMode(DIGESTMODE_TOP))
@@ -922,7 +923,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
finalizer.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
fmt.Printf("SIGN B\n")
_ = SignComponent(resolver, SIGNATURE, COMPONENTB, D_COMPB, DigestMode(DIGESTMODE_LOCAL))
@@ -944,7 +945,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
finalizer.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
fmt.Printf("RESIGN B\n")
_ = SignComponent(resolver, SIGNATURE, COMPONENTB, D_COMPB, DigestMode(DIGESTMODE_TOP), SignatureName(SIGNATURE2, true))
@@ -982,7 +983,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
finalizer.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
fmt.Printf("SIGN B\n")
_ = SignComponent(resolver, SIGNATURE, COMPONENTB, D_COMPB, DigestMode(DIGESTMODE_TOP), SignatureName(SIGNATURE2, true))
@@ -1004,7 +1005,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
finalizer.Close(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
fmt.Printf("SIGN D\n")
_ = SignComponent(resolver, SIGNATURE, COMPONENTD, D_COMPD, Recursive(), DigestMode(DIGESTMODE_LOCAL))
@@ -1066,7 +1067,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env))
defer Close(src, "ctf")
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv := Must(resolver.LookupComponentVersion(COMPONENTC, VERSION))
defer cv.Close()
@@ -1123,7 +1124,7 @@ github.com/mandelsoft/test:v1: SHA-256:${D_COMPA}[jsonNormalisation/v1]
It("signs with certificate and default issuer", func() {
digest := "9cf14695c864411cad03071a8766e6769bb00373bdd8c65887e4644cc285dc78"
- res := ocm.NewDedicatedResolver(cv)
+ res := resolvers.NewDedicatedResolver(cv)
buf := SignComponent(res, PROVIDER, COMPONENTA, digest, PrivateKey(PROVIDER, priv), PublicKey(PROVIDER, pemBytes), RootCertificates(ca))
Expect(buf).To(StringEqualTrimmedWithContext(`
@@ -1143,7 +1144,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v
It("signs with certificate and explicit CN issuer", func() {
digest := "9cf14695c864411cad03071a8766e6769bb00373bdd8c65887e4644cc285dc78"
- res := ocm.NewDedicatedResolver(cv)
+ res := resolvers.NewDedicatedResolver(cv)
buf := SignComponent(res, SIGNATURE, COMPONENTA, digest, PrivateKey(SIGNATURE, priv), PublicKey(SIGNATURE, pemBytes), RootCertificates(ca), Issuer(PROVIDER))
Expect(buf).To(StringEqualTrimmedWithContext(`
@@ -1167,7 +1168,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v
It("signs with certificate and issuer", func() {
digest := "9cf14695c864411cad03071a8766e6769bb00373bdd8c65887e4644cc285dc78"
- res := ocm.NewDedicatedResolver(cv)
+ res := resolvers.NewDedicatedResolver(cv)
issuer := &pkix.Name{
CommonName: PROVIDER,
Country: []string{"DE"},
@@ -1198,7 +1199,7 @@ applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v
It("signs with certificate, issuer and tsa", func() {
digest := "9cf14695c864411cad03071a8766e6769bb00373bdd8c65887e4644cc285dc78"
- res := ocm.NewDedicatedResolver(cv)
+ res := resolvers.NewDedicatedResolver(cv)
issuer := &pkix.Name{
CommonName: "mandelsoft",
Country: []string{"DE"},
diff --git a/api/ocm/tools/toi/install/action.go b/api/ocm/tools/toi/install/action.go
index e901b97e26..20076c8842 100644
--- a/api/ocm/tools/toi/install/action.go
+++ b/api/ocm/tools/toi/install/action.go
@@ -22,6 +22,7 @@ import (
resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
"ocm.software/ocm/api/ocm/extensions/repositories/ctf"
utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/ocm/tools/toi"
"ocm.software/ocm/api/ocm/tools/transfer"
"ocm.software/ocm/api/ocm/tools/transfer/transferhandler/standard"
@@ -96,7 +97,7 @@ func DetermineExecutor(executor *toi.Executor, octx ocm.Context, cv ocm.Componen
if executor.ResourceRef == nil {
return nil, errors.Newf("executor resource reference required for toi package executor")
}
- res, eff, err := utils.ResolveResourceReference(cv, *executor.ResourceRef, resolver)
+ res, eff, err := resourcerefs.ResolveResourceReference(cv, *executor.ResourceRef, resolver)
if err != nil {
return nil, errors.ErrNotFoundWrap(err, "executor resource", executor.ResourceRef.String())
}
@@ -121,7 +122,7 @@ func DetermineExecutor(executor *toi.Executor, octx ocm.Context, cv ocm.Componen
return nil, errors.Newf("executor image reference required for toi executor")
}
var eff2 ocm.ComponentVersionAccess
- res, eff2, err = utils.ResolveResourceReference(eff, *espec.Spec.ImageRef, resolver)
+ res, eff2, err = resourcerefs.ResolveResourceReference(eff, *espec.Spec.ImageRef, resolver)
if err != nil {
return nil, errors.ErrNotFoundWrap(err, "executor resource", executor.ResourceRef.String())
}
@@ -240,7 +241,7 @@ func ProcessConfig(name string, octx ocm.Context, cv ocm.ComponentVersionAccess,
stubs := spiff.Options{}
for i, lib := range libraries {
- res, eff, err := utils.ResolveResourceReference(cv, lib, resolver)
+ res, eff, err := resourcerefs.ResolveResourceReference(cv, lib, resolver)
if err != nil {
return nil, errors.ErrNotFound("library resource %s not found", lib.String())
}
diff --git a/api/ocm/tools/toi/install/execute.go b/api/ocm/tools/toi/install/execute.go
index 31405ae9a8..25b466400a 100644
--- a/api/ocm/tools/toi/install/execute.go
+++ b/api/ocm/tools/toi/install/execute.go
@@ -5,7 +5,7 @@ import (
"ocm.software/ocm/api/ocm"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
- utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/ocm/tools/toi"
"ocm.software/ocm/api/utils/blobaccess/blobaccess"
common "ocm.software/ocm/api/utils/misc"
@@ -33,7 +33,7 @@ func Execute(p common.Printer, d Driver, name string, rid metav1.Identity, creds
}
}
- ires, _, err := utils.MatchResourceReference(cv, toi.TypeTOIPackage, metav1.NewResourceRef(rid), nil)
+ ires, _, err := resourcerefs.MatchResourceReference(cv, toi.TypeTOIPackage, metav1.NewResourceRef(rid), nil)
if err != nil {
return nil, errors.Wrapf(err, "package resource in %s", common.VersionedElementKey(cv).String())
}
diff --git a/api/ocm/tools/transfer/transferhandler/standard/handler.go b/api/ocm/tools/transfer/transferhandler/standard/handler.go
index 3b871bfcab..06020464e3 100644
--- a/api/ocm/tools/transfer/transferhandler/standard/handler.go
+++ b/api/ocm/tools/transfer/transferhandler/standard/handler.go
@@ -10,6 +10,7 @@ import (
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/ocm/cpi"
"ocm.software/ocm/api/ocm/cpi/accspeccpi"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/ocm/tools/transfer/transferhandler"
"ocm.software/ocm/api/utils/accessio"
)
@@ -53,7 +54,7 @@ func (h *Handler) TransferVersion(repo ocm.Repository, src ocm.ComponentVersionA
return nil, nil, errors.Wrapf(err, "failed looking up in target")
}
}
- compoundResolver := ocm.NewCompoundResolver(repo, h.opts.GetResolver())
+ compoundResolver := resolvers.NewCompoundResolver(repo, h.opts.GetResolver())
cv, err := compoundResolver.LookupComponentVersion(meta.GetComponentName(), meta.Version)
return cv, h, err
}
diff --git a/api/ocm/tools/transfer/transferhandler/standard/handler_test.go b/api/ocm/tools/transfer/transferhandler/standard/handler_test.go
index 2a4a7936c9..6ee3d032a4 100644
--- a/api/ocm/tools/transfer/transferhandler/standard/handler_test.go
+++ b/api/ocm/tools/transfer/transferhandler/standard/handler_test.go
@@ -25,6 +25,7 @@ import (
"ocm.software/ocm/api/ocm/extensions/attrs/compositionmodeattr"
"ocm.software/ocm/api/ocm/extensions/attrs/signingattr"
"ocm.software/ocm/api/ocm/extensions/repositories/ctf"
+ "ocm.software/ocm/api/ocm/resolvers"
ocmsign "ocm.software/ocm/api/ocm/tools/signing"
"ocm.software/ocm/api/ocm/tools/transfer"
"ocm.software/ocm/api/ocm/tools/transfer/transferhandler"
@@ -488,7 +489,7 @@ warning: version "github.com/mandelsoft/test:v1" already present, but differs
cv, err := src.LookupComponentVersion(COMPONENT, VERSION)
Expect(err).To(Succeed())
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
opts := ocmsign.NewOptions(
ocmsign.Sign(signingattr.Get(env.OCMContext()).GetSigner(SIGN_ALGO), SIGNATURE),
@@ -513,7 +514,7 @@ warning: version "github.com/mandelsoft/test:v1" already present, but differs
Expect(err).To(Succeed())
Expect(env.DirExists(OUT)).To(BeTrue())
- resolver = ocm.NewCompoundResolver(tgt)
+ resolver = resolvers.NewCompoundResolver(tgt)
opts = ocmsign.NewOptions(
ocmsign.Resolver(resolver),
diff --git a/api/utils/blobaccess/ocm/access.go b/api/utils/blobaccess/ocm/access.go
new file mode 100644
index 0000000000..394218f06b
--- /dev/null
+++ b/api/utils/blobaccess/ocm/access.go
@@ -0,0 +1,38 @@
+package ocm
+
+import (
+ "github.com/mandelsoft/goutils/finalizer"
+
+ "ocm.software/ocm/api/utils/blobaccess/bpi"
+ "ocm.software/ocm/api/utils/refmgmt"
+)
+
+func DataAccess(cvp ComponentVersionProvider, res ResourceProvider) (bpi.DataAccess, error) {
+ return BlobAccess(cvp, res)
+}
+
+func BlobAccess(cvp ComponentVersionProvider, res ResourceProvider) (blob bpi.BlobAccess, rerr error) {
+ var finalize finalizer.Finalizer
+ defer finalize.FinalizeWithErrorPropagation(&rerr)
+
+ cv, err := refmgmt.ToLazy(cvp.GetComponentVersionAccess())
+ if err != nil {
+ return nil, err
+ }
+ finalize.Close(cv)
+
+ r, eff, err := res.GetResource(cv)
+ if eff != nil {
+ finalize.Close(refmgmt.AsLazy(eff))
+ }
+ if err != nil {
+ return nil, err
+ }
+ return r.BlobAccess()
+}
+
+func Provider(cvp ComponentVersionProvider, res ResourceProvider) bpi.BlobAccessProvider {
+ return bpi.BlobAccessProviderFunction(func() (bpi.BlobAccess, error) {
+ return BlobAccess(cvp, res)
+ })
+}
diff --git a/api/utils/blobaccess/ocm/access_test.go b/api/utils/blobaccess/ocm/access_test.go
new file mode 100644
index 0000000000..8680f04fd0
--- /dev/null
+++ b/api/utils/blobaccess/ocm/access_test.go
@@ -0,0 +1,69 @@
+package ocm_test
+
+import (
+ . "github.com/mandelsoft/goutils/testutils"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ . "ocm.software/ocm/api/helper/builder"
+
+ v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
+ "ocm.software/ocm/api/ocm/extensions/repositories/ctf"
+ "ocm.software/ocm/api/ocm/selectors/rscsel"
+ "ocm.software/ocm/api/utils/accessio"
+ "ocm.software/ocm/api/utils/accessobj"
+ "ocm.software/ocm/api/utils/blobaccess/ocm"
+ me "ocm.software/ocm/api/utils/blobaccess/ocm"
+ "ocm.software/ocm/api/utils/mime"
+)
+
+const (
+ ARCH = "/arch.ctf"
+ COMP1 = "acme.org/test1"
+ COMP2 = "acme.org/test2"
+ VERS = "v1"
+)
+
+var _ = Describe("blobaccess for ocm", func() {
+ Context("maven filesystem repository", func() {
+ var env *Builder
+
+ BeforeEach(func() {
+ env = NewBuilder()
+
+ env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() {
+ env.ComponentVersion(COMP1, VERS, func() {
+ env.Resource("test", VERS, resourcetypes.PLAIN_TEXT, v1.LocalRelation, func() {
+ env.BlobStringData(mime.MIME_TEXT, "test data")
+ })
+ })
+
+ env.ComponentVersion(COMP2, VERS, func() {
+ env.Reference("ref", COMP1, VERS)
+ })
+ })
+ })
+
+ AfterEach(func() {
+ MustBeSuccessful(env.Cleanup())
+ })
+
+ It("blobaccess for selector", func() {
+ b := Must(me.BlobAccess(ocm.ByRepositorySpecAndName(env.OCMContext(), Must(ctf.NewRepositorySpec(accessobj.ACC_READONLY, ARCH, accessio.PathFileSystem(env.FileSystem()))), COMP1, VERS),
+ ocm.ByResourceSelector(rscsel.Name("test")),
+ ))
+ defer Close(b, "blobaccess")
+
+ Expect(string(Must(b.Get()))).To(Equal("test data"))
+ })
+
+ It("blobaccess for ref path", func() {
+ b := Must(me.BlobAccess(ocm.ByRepositorySpecAndName(env.OCMContext(), Must(ctf.NewRepositorySpec(accessobj.ACC_READONLY, ARCH, accessio.PathFileSystem(env.FileSystem()))), COMP2, VERS),
+ ocm.ByResourcePath(v1.NewIdentity("test"), v1.NewIdentity("ref")),
+ ))
+ defer Close(b, "blobaccess")
+
+ Expect(string(Must(b.Get()))).To(Equal("test data"))
+ })
+ })
+})
diff --git a/api/utils/blobaccess/ocm/compvers.go b/api/utils/blobaccess/ocm/compvers.go
new file mode 100644
index 0000000000..39c1d6dc69
--- /dev/null
+++ b/api/utils/blobaccess/ocm/compvers.go
@@ -0,0 +1,77 @@
+package ocm
+
+import (
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/utils/refmgmt"
+)
+
+// ComponentVersionProvider is a factory for component versions.
+// Every call provides a separately closeable component versuion access.
+// An implementation should not hold private views of objects,
+// The life cycle of all those objects should be left to the
+// creator of ComponentVersionProvider implementation. Therefore, it does not
+// have a close method.
+type ComponentVersionProvider interface {
+ GetComponentVersionAccess() (cpi.ComponentVersionAccess, error)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type bycv struct {
+ cv cpi.ComponentVersionAccess
+}
+
+var _ ComponentVersionProvider = (*bycv)(nil)
+
+func ByComponentVersion(cv cpi.ComponentVersionAccess) ComponentVersionProvider {
+ return &bycv{cv}
+}
+
+func (c *bycv) GetComponentVersionAccess() (cpi.ComponentVersionAccess, error) {
+ return c.cv.Dup()
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type byresolver struct {
+ resolver cpi.ComponentVersionResolver
+ comp string
+ vers string
+}
+
+var _ ComponentVersionProvider = (*byresolver)(nil)
+
+func ByResolverAndName(resolver cpi.ComponentVersionResolver, comp, vers string) ComponentVersionProvider {
+ return &byresolver{resolver, comp, vers}
+}
+
+func (c *byresolver) GetComponentVersionAccess() (cpi.ComponentVersionAccess, error) {
+ return c.resolver.LookupComponentVersion(c.comp, c.vers)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type byrepospec struct {
+ ctx cpi.Context
+ spec cpi.RepositorySpec
+ comp string
+ vers string
+}
+
+var _ ComponentVersionProvider = (*byrepospec)(nil)
+
+func ByRepositorySpecAndName(ctx cpi.ContextProvider, spec cpi.RepositorySpec, comp, vers string) ComponentVersionProvider {
+ if ctx == nil {
+ ctx = cpi.DefaultContext()
+ }
+ return &byrepospec{ctx.OCMContext(), spec, comp, vers}
+}
+
+func (c *byrepospec) GetComponentVersionAccess() (cpi.ComponentVersionAccess, error) {
+ repo, err := refmgmt.ToLazy(c.ctx.RepositoryForSpec(c.spec))
+ if err != nil {
+ return nil, err
+ }
+ defer repo.Close()
+ return repo.LookupComponentVersion(c.comp, c.vers)
+}
diff --git a/api/utils/blobaccess/ocm/ref.go b/api/utils/blobaccess/ocm/ref.go
new file mode 100644
index 0000000000..e037e1c800
--- /dev/null
+++ b/api/utils/blobaccess/ocm/ref.go
@@ -0,0 +1,93 @@
+package ocm
+
+import (
+ "github.com/mandelsoft/goutils/errors"
+
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/resourcerefs"
+ "ocm.software/ocm/api/ocm/selectors/rscsel"
+ "ocm.software/ocm/api/utils"
+)
+
+// ResourceProvider selects a resource from a component version.
+// It should not hold any separately closeabvle view on
+// an object. The lifecycle of those objects should be left
+// to the creator of the implementation of this interface.
+type ResourceProvider interface {
+ GetResource(cv cpi.ComponentVersionAccess) (cpi.ResourceAccess, cpi.ComponentVersionAccess, error)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type byid struct {
+ id metav1.Identity
+}
+
+var _ ResourceProvider = (*byid)(nil)
+
+func ByResourceId(id metav1.Identity) ResourceProvider {
+ return &byid{id}
+}
+
+func (r *byid) GetResource(cv cpi.ComponentVersionAccess) (cpi.ResourceAccess, cpi.ComponentVersionAccess, error) {
+ cv, err := cv.Dup()
+ if err != nil {
+ return nil, nil, err
+ }
+ res, err := cv.GetResource(r.id)
+ if err != nil {
+ cv.Close()
+ return nil, nil, err
+ }
+ return res, cv, nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type byref struct {
+ resolver cpi.ComponentVersionResolver
+ ref metav1.ResourceReference
+}
+
+var _ ResourceProvider = (*byref)(nil)
+
+func ByResourcePath(id metav1.Identity, path ...metav1.Identity) ResourceProvider {
+ return &byref{nil, metav1.NewNestedResourceRef(id, path)}
+}
+
+func ByResourceRef(ref metav1.ResourceReference, res ...cpi.ComponentVersionResolver) ResourceProvider {
+ return &byref{utils.Optional(res...), ref}
+}
+
+func (r *byref) GetResource(cv cpi.ComponentVersionAccess) (cpi.ResourceAccess, cpi.ComponentVersionAccess, error) {
+ return resourcerefs.ResolveResourceReference(cv, r.ref, r.resolver)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type bysel struct {
+ sel []rscsel.Selector
+}
+
+var _ ResourceProvider = (*bysel)(nil)
+
+func ByResourceSelector(sel ...rscsel.Selector) ResourceProvider {
+ return &bysel{sel}
+}
+
+func (r *bysel) GetResource(cv cpi.ComponentVersionAccess) (cpi.ResourceAccess, cpi.ComponentVersionAccess, error) {
+ res, err := cv.SelectResources(r.sel...)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(res) == 0 {
+ return nil, nil, errors.ErrNotFound(cpi.KIND_RESOURCE)
+ }
+
+ cv, err = cv.Dup()
+ if err != nil {
+ return nil, nil, err
+ }
+ return res[0], cv, nil
+}
diff --git a/api/utils/blobaccess/ocm/suite_test.go b/api/utils/blobaccess/ocm/suite_test.go
new file mode 100644
index 0000000000..cc788df8ec
--- /dev/null
+++ b/api/utils/blobaccess/ocm/suite_test.go
@@ -0,0 +1,13 @@
+package ocm_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "OCM Access Test Suite")
+}
diff --git a/api/utils/clisupport/identity.go b/api/utils/clisupport/identity.go
new file mode 100644
index 0000000000..977ddb6260
--- /dev/null
+++ b/api/utils/clisupport/identity.go
@@ -0,0 +1,50 @@
+package clisupport
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/mandelsoft/goutils/errors"
+
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+)
+
+func ParseIdentityPath(ids ...string) ([]metav1.Identity, error) {
+ var err error
+ result := []metav1.Identity{}
+
+ var id metav1.Identity
+ for _, l := range ids {
+ name, value, err := ParseIdentityAttribute(l)
+ if err != nil {
+ return nil, err
+ }
+ if name != "name" {
+ if id == nil {
+ return nil, fmt.Errorf("first attribute must be the name attribute")
+ }
+ if id[name] != "" {
+ return nil, fmt.Errorf("attribute %q already set", name)
+ }
+ id[name] = value
+ } else {
+ if id != nil {
+ result = append(result, id)
+ }
+ id = metav1.Identity{name: value}
+ }
+ }
+ result = append(result, id)
+ return result, err
+}
+
+func ParseIdentityAttribute(a string) (string, string, error) {
+ i := strings.Index(a, "=")
+ if i < 0 {
+ return "", "", errors.ErrInvalid("identity attribute", a)
+ }
+ name := a[:i]
+ value := a[i+1:]
+
+ return name, value, nil
+}
diff --git a/api/utils/clisupport/identity_test.go b/api/utils/clisupport/identity_test.go
new file mode 100644
index 0000000000..eb50748f39
--- /dev/null
+++ b/api/utils/clisupport/identity_test.go
@@ -0,0 +1,47 @@
+package clisupport_test
+
+import (
+ . "github.com/mandelsoft/goutils/testutils"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/utils/clisupport"
+)
+
+var _ = Describe("IdentityPath Parsing", func() {
+ Context("", func() {
+ It("handles simple identity", func() {
+ value := `name=alice`
+ flag := Must(clisupport.ParseIdentityPath(value))
+ Expect(flag).To(Equal([]v1.Identity{{"name": "alice"}}))
+ })
+
+ It("handles simple path", func() {
+ value1 := `name=alice`
+ value2 := `husband=bob`
+ flag := Must(clisupport.ParseIdentityPath(value1, value2))
+ Expect(flag).To(Equal([]v1.Identity{{"name": "alice", "husband": "bob"}}))
+ })
+
+ It("handles mulki path", func() {
+ value1 := `name=alice`
+ value2 := `husband=bob`
+ value3 := "name=bob"
+ value4 := "wife=alice"
+ value5 := "name=other"
+ flag := Must(clisupport.ParseIdentityPath(value1, value2, value3, value4, value5))
+ Expect(flag).To(Equal([]v1.Identity{{"name": "alice", "husband": "bob"}, {"name": "bob", "wife": "alice"}, {"name": "other"}}))
+ })
+
+ It("rejects invalid value", func() {
+ value := `a=b`
+ ExpectError(clisupport.ParseIdentityPath(value)).To(MatchError("first attribute must be the name attribute"))
+ })
+
+ It("rejects invalid assignment", func() {
+ value := `a`
+ ExpectError(clisupport.ParseIdentityPath(value)).To(MatchError("identity attribute \"a\" is invalid"))
+ })
+ })
+})
diff --git a/api/utils/clisupport/suite_test.go b/api/utils/clisupport/suite_test.go
new file mode 100644
index 0000000000..f8092d2971
--- /dev/null
+++ b/api/utils/clisupport/suite_test.go
@@ -0,0 +1,13 @@
+package clisupport_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "clisupport Test Suite")
+}
diff --git a/api/utils/cobrautils/flag/identity_path.go b/api/utils/cobrautils/flag/identity_path.go
new file mode 100644
index 0000000000..ddf823c55b
--- /dev/null
+++ b/api/utils/cobrautils/flag/identity_path.go
@@ -0,0 +1,87 @@
+package flag
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/spf13/pflag"
+)
+
+type identityPath struct {
+ value *[]map[string]string
+ changed bool
+}
+
+func newIdentityPathValue(val []map[string]string, p *[]map[string]string) *identityPath {
+ ssv := new(identityPath)
+ ssv.value = p
+ *ssv.value = val
+ return ssv
+}
+
+func (s *identityPath) Set(val string) error {
+ k, v, err := parseAssignment(val)
+ if err != nil {
+ return err
+ }
+ if !s.changed {
+ if k != "name" {
+ return fmt.Errorf("first identity attribute must be the name attribute")
+ }
+ *s.value = []map[string]string{{k: v}}
+ } else {
+ if k == "name" {
+ *s.value = append(*s.value, map[string]string{k: v})
+ } else {
+ (*s.value)[len(*s.value)-1][k] = v
+ }
+ }
+ s.changed = true
+ return nil
+}
+
+func (s *identityPath) Type() string {
+ return "{=}"
+}
+
+func (s *identityPath) String() string {
+ if *s.value == nil {
+ return ""
+ }
+ var list []string
+ for _, v := range *s.value {
+ //nolint: errchkjson // initialized by unmarshal
+ s, _ := json.Marshal(v)
+ list = append(list, string(s))
+ }
+ return "[" + strings.Join(list, ", ") + "]"
+}
+
+func (s *identityPath) GetPath() []map[string]string {
+ return *s.value
+}
+
+func IdentityPathVar(f *pflag.FlagSet, p *[]map[string]string, name string, value []map[string]string, usage string) {
+ f.VarP(newIdentityPathValue(value, p), name, "", usage)
+}
+
+func IdentityPathVarP(f *pflag.FlagSet, p *[]map[string]string, name, shorthand string, value []map[string]string, usage string) {
+ f.VarP(newIdentityPathValue(value, p), name, shorthand, usage)
+}
+
+func IdentityPathVarPF(f *pflag.FlagSet, p *[]map[string]string, name, shorthand string, value []map[string]string, usage string) *pflag.Flag {
+ return f.VarPF(newIdentityPathValue(value, p), name, shorthand, usage)
+}
+
+func IdentityPath(f *pflag.FlagSet, name string, value []map[string]string, usage string) *[]map[string]string {
+ p := []map[string]string{}
+ IdentityPathVarP(f, &p, name, "", value, usage)
+ return &p
+}
+
+func IdentityPathP(f *pflag.FlagSet, name, shorthand string, value []map[string]string, usage string) *[]map[string]string {
+ p := []map[string]string{}
+ IdentityPathVarP(f, &p, name, shorthand, value, usage)
+ return &p
+}
diff --git a/api/utils/cobrautils/flag/identity_path_test.go b/api/utils/cobrautils/flag/identity_path_test.go
new file mode 100644
index 0000000000..4d96582aac
--- /dev/null
+++ b/api/utils/cobrautils/flag/identity_path_test.go
@@ -0,0 +1,91 @@
+package flag
+
+import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/mandelsoft/goutils/testutils"
+ "github.com/spf13/pflag"
+)
+
+var _ = Describe("identity path", func() {
+ var flags *pflag.FlagSet
+
+ BeforeEach(func() {
+ flags = pflag.NewFlagSet("test", pflag.ContinueOnError)
+ })
+
+ It("handles simple identity", func() {
+ var flag []map[string]string
+ IdentityPathVarP(flags, &flag, "flag", "", nil, "test flag")
+
+ value := `name=alice`
+
+ Expect(flags.Parse([]string{"--flag", value})).To(Succeed())
+ Expect(flag).To(Equal([]map[string]string{{"name": "alice"}}))
+ })
+
+ It("handles simple path", func() {
+ var flag []map[string]string
+ IdentityPathVarP(flags, &flag, "flag", "", nil, "test flag")
+
+ value1 := `name=alice`
+ value2 := `husband=bob`
+
+ Expect(flags.Parse([]string{"--flag", value1, "--flag", value2})).To(Succeed())
+ Expect(flag).To(Equal([]map[string]string{{"name": "alice", "husband": "bob"}}))
+ })
+
+ It("handles multi path", func() {
+ var flag []map[string]string
+ IdentityPathVarP(flags, &flag, "flag", "", nil, "test flag")
+
+ value1 := `name=alice`
+ value2 := `husband=bob`
+ value3 := "name=bob"
+ value4 := "wife=alice"
+ value5 := "name=other"
+ Expect(flags.Parse([]string{"--flag", value1, "--flag", value2, "--flag", value3, "--flag", value4, "--flag", value5})).To(Succeed())
+ Expect(flag).To(Equal([]map[string]string{{"name": "alice", "husband": "bob"}, {"name": "bob", "wife": "alice"}, {"name": "other"}}))
+ })
+
+ It("shows default", func() {
+ var flag []map[string]string
+ IdentityPathVarP(flags, &flag, "flag", "", []map[string]string{{"name": "alice"}}, "test flag")
+
+ Expect(flags.FlagUsages()).To(testutils.StringEqualTrimmedWithContext(`--flag {=} test flag (default [{"name":"alice"}])`))
+ })
+
+ It("handles replaces default content", func() {
+ var flag []map[string]string
+ IdentityPathVarP(flags, &flag, "flag", "", []map[string]string{{"name": "other"}}, "test flag")
+
+ value1 := `name=alice`
+ value2 := `husband=bob`
+
+ Expect(flags.Parse([]string{"--flag", value1, "--flag", value2})).To(Succeed())
+ Expect(flag).To(Equal([]map[string]string{{"name": "alice", "husband": "bob"}}))
+ })
+
+ It("rejects invalid value", func() {
+ var flag []map[string]string
+ IdentityPathVarP(flags, &flag, "flag", "", nil, "test flag")
+
+ value := `a=b`
+
+ err := flags.Parse([]string{"--flag", value})
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("invalid argument \"a=b\" for \"--flag\" flag: first identity attribute must be the name attribute"))
+ })
+
+ It("rejects invalid assignment", func() {
+ var flag map[string]interface{}
+ StringToValueVarP(flags, &flag, "flag", "", nil, "test flag")
+
+ value := `a`
+
+ err := flags.Parse([]string{"--flag", value})
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("invalid argument \"a\" for \"--flag\" flag: expected ="))
+ })
+})
diff --git a/api/utils/cobrautils/flagsets/types.go b/api/utils/cobrautils/flagsets/types.go
index 5863a8e371..b3aaacf097 100644
--- a/api/utils/cobrautils/flagsets/types.go
+++ b/api/utils/cobrautils/flagsets/types.go
@@ -5,6 +5,7 @@ import (
"github.com/spf13/pflag"
+ v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/utils/cobrautils/flag"
"ocm.software/ocm/api/utils/cobrautils/groups"
)
@@ -551,3 +552,44 @@ func (o *StringSliceMapColonOption) AddFlags(fs *pflag.FlagSet) {
func (o *StringSliceMapColonOption) Value() interface{} {
return o.value
}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+type IdentityPathOptionType struct {
+ TypeOptionBase
+}
+
+func NewIdentityPathOptionType(name string, description string) ConfigOptionType {
+ return &IdentityPathOptionType{
+ TypeOptionBase: TypeOptionBase{name, description},
+ }
+}
+
+func (s *IdentityPathOptionType) Equal(optionType ConfigOptionType) bool {
+ return reflect.DeepEqual(s, optionType)
+}
+
+func (s *IdentityPathOptionType) Create() Option {
+ return &IdentityPathOption{
+ OptionBase: NewOptionBase(s),
+ }
+}
+
+type IdentityPathOption struct {
+ OptionBase
+ value []map[string]string
+}
+
+var _ Option = (*IdentityPathOption)(nil)
+
+func (o *IdentityPathOption) AddFlags(fs *pflag.FlagSet) {
+ o.TweakFlag(flag.IdentityPathVarPF(fs, &o.value, o.otyp.GetName(), "", nil, o.otyp.GetDescription()))
+}
+
+func (o *IdentityPathOption) Value() interface{} {
+ var result []v1.Identity
+ for _, v := range o.value {
+ result = append(result, v1.Identity(v))
+ }
+ return result
+}
diff --git a/api/utils/testutils/doc.go b/api/utils/testutils/doc.go
deleted file mode 100644
index 546124a922..0000000000
--- a/api/utils/testutils/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Deprecated: This package is deprecated and will be removed in a future release. Please use the testutils package
-// provided by github.com/mandelsoft/goutils.
-package testutils
diff --git a/api/utils/testutils/object.go b/api/utils/testutils/object.go
deleted file mode 100644
index 1bdbbe94a9..0000000000
--- a/api/utils/testutils/object.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package testutils
-
-import (
- "fmt"
- "strings"
-
- "github.com/go-test/deep"
- "github.com/onsi/gomega/format"
- "github.com/onsi/gomega/types"
-)
-
-// DeepEqual compares two objects and shows diff on failure.
-func DeepEqual(expected interface{}) types.GomegaMatcher {
- return &DeepEqualMatcher{
- Expected: expected,
- }
-}
-
-type DeepEqualMatcher struct {
- Expected interface{}
-}
-
-func (matcher *DeepEqualMatcher) Match(actual interface{}) (success bool, err error) {
- return len(deep.Equal(actual, matcher.Expected)) == 0, nil
-}
-
-func (matcher *DeepEqualMatcher) FailureMessage(actual interface{}) (message string) {
- diff := deep.Equal(actual, matcher.Expected)
- if len(diff) > 0 {
- return fmt.Sprintf("unexpected diff in deep equal: \n %s\n", strings.Join(diff, "\n "))
- }
- return format.Message(actual, "to equal", matcher.Expected)
-}
-
-func (matcher *DeepEqualMatcher) NegatedFailureMessage(actual interface{}) (message string) {
- return format.Message(actual, "not to equal", matcher.Expected)
-}
diff --git a/api/utils/testutils/package.go b/api/utils/testutils/package.go
deleted file mode 100644
index a3ec75d19d..0000000000
--- a/api/utils/testutils/package.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package testutils
-
-import (
- "fmt"
- "strings"
-
- "github.com/mandelsoft/filepath/pkg/filepath"
- "github.com/mandelsoft/goutils/general"
- "github.com/mandelsoft/goutils/pkgutils"
- "github.com/mandelsoft/vfs/pkg/osfs"
- "github.com/mandelsoft/vfs/pkg/vfs"
- "golang.org/x/mod/modfile"
-)
-
-const GO_MOD = "go.mod"
-
-func GetPackagePathFromProjectRoot(i ...interface{}) (string, error) {
- pkg, err := pkgutils.GetPackageName(i...)
- if err != nil {
- return "", err
- }
- mod, err := GetModuleName()
- if err != nil {
- return "", err
- }
- path, ok := strings.CutPrefix(pkg, mod+"/")
- if !ok {
- return "", fmt.Errorf("prefix %q not found in %q", mod, pkg)
- }
- return path, nil
-}
-
-// GetModuleName returns a go modules module name by finding and parsing the go.mod file.
-func GetModuleName() (string, error) {
- pathToRoot, err := GetRelativePathToProjectRoot()
- if err != nil {
- return "", err
- }
- pathToGoMod := filepath.Join(pathToRoot, GO_MOD)
- // Read the content of the go.mod file
- data, err := vfs.ReadFile(osfs.OsFs, pathToGoMod)
- if err != nil {
- return "", err
- }
-
- // Parse the go.mod file
- modFile, err := modfile.Parse(GO_MOD, data, nil)
- if err != nil {
- return "", fmt.Errorf("error parsing %s file: %w", GO_MOD, err)
- }
-
- // Print the module path
- return modFile.Module.Mod.Path, nil
-}
-
-// GetRelativePathToProjectRoot calculates the relative path to a go projects root directory.
-// It therefore assumes that the project root is the directory containing the go.mod file.
-// The optional parameter i determines how many directories the function will step up through, attempting to find a
-// go.mod file. If it cannot find a directory with a go.mod file within i iterations, the function throws an error.
-func GetRelativePathToProjectRoot(i ...int) (string, error) {
- iterations := general.OptionalDefaulted(20, i...)
-
- path := "."
- for count := 0; count < iterations; count++ {
- if ok, err := vfs.FileExists(osfs.OsFs, filepath.Join(path, GO_MOD)); err != nil || ok {
- if err != nil {
- return "", fmt.Errorf("failed to check if %s exists: %w", GO_MOD, err)
- }
- return path, nil
- }
- if count == iterations {
- return "", fmt.Errorf("could not find %s (within %d steps)", GO_MOD, iterations)
- }
- path = filepath.Join(path, "..")
- }
- return "", nil
-}
diff --git a/api/utils/testutils/package_test.go b/api/utils/testutils/package_test.go
deleted file mode 100644
index 13cd3a28bd..0000000000
--- a/api/utils/testutils/package_test.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package testutils_test
-
-import (
- . "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
-
- me "ocm.software/ocm/api/utils/testutils"
-)
-
-var _ = Describe("package tests", func() {
- It("go module name", func() {
- mod := me.Must(me.GetModuleName())
- Expect(mod).To(Equal("ocm.software/ocm"))
- })
-})
diff --git a/api/utils/testutils/signing_test.go b/api/utils/testutils/signing_test.go
deleted file mode 100644
index ad88a136b3..0000000000
--- a/api/utils/testutils/signing_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package testutils_test
-
-import (
- . "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
-
- common "ocm.software/ocm/api/utils/misc"
- "ocm.software/ocm/api/utils/testutils"
-)
-
-var _ = Describe("normalization", func() {
- It("compares with substitution variables", func() {
- exp := "A ${TEST}."
- res := "A testcase."
- vars := common.Properties{
- "TEST": "testcase",
- }
- Expect(res).To(testutils.StringEqualTrimmedWithContext(exp, common.Properties{}, vars))
- Expect(res).To(testutils.StringEqualTrimmedWithContext(exp, vars, common.Properties{}))
- })
-})
diff --git a/api/utils/testutils/string.go b/api/utils/testutils/string.go
deleted file mode 100644
index b8af087548..0000000000
--- a/api/utils/testutils/string.go
+++ /dev/null
@@ -1,213 +0,0 @@
-package testutils
-
-import (
- "encoding/json"
- "fmt"
- "regexp"
- "strings"
-
- "github.com/drone/envsubst"
- "github.com/mandelsoft/goutils/errors"
- "github.com/onsi/gomega/format"
- "github.com/onsi/gomega/types"
-)
-
-type Substitutions = map[string]string
-
-func SubstList(values ...string) map[string]string {
- r := map[string]string{}
- for i := 0; i+1 < len(values); i += 2 {
- r[values[i]] = values[i+1]
- }
- return r
-}
-
-func SubstFrom(v interface{}, prefix ...string) map[string]string {
- data, err := json.Marshal(v)
- if err != nil {
- panic(err)
- }
- var values map[string]string
- err = json.Unmarshal(data, &values)
- if err != nil {
- panic(err)
- }
- if len(prefix) > 0 {
- p := strings.Join(prefix, "")
- n := map[string]string{}
- for k, v := range values {
- n[p+k] = v
- }
- values = n
- }
- return values
-}
-
-func MergeSubst(subst ...map[string]string) map[string]string {
- r := map[string]string{}
- for _, s := range subst {
- for k, v := range s {
- r[k] = v
- }
- }
- return r
-}
-
-// StringEqualTrimmedWithContext compares two trimmed strings and provides the complete actual value
-// as error context.
-// If value mappings are given, the expected string is evaluated by envsubst, first.
-// It is an error for actual to be nil. Use BeNil() instead.
-func StringEqualTrimmedWithContext(expected string, subst ...Substitutions) types.GomegaMatcher {
- var err error
- expected, err = eval(expected, subst...)
- if err != nil {
- return &reportError{err}
- }
- return &StringEqualMatcher{
- Expected: expected,
- Trim: true,
- }
-}
-
-// StringMatchTrimmedWithContext matches a trimmed string by a regular
-// expression and provides the complete actual value as error context.
-// If value mappings are given, the expected string is evaluated by envsubst, first.
-// It is an error for actual to be nil. Use BeNil() instead.
-func StringMatchTrimmedWithContext(expected string, subst ...Substitutions) types.GomegaMatcher {
- var err error
- expected, err = eval(expected, subst...)
- if err != nil {
- return &reportError{err}
- }
- return &StringEqualMatcher{
- Expected: expected,
- Trim: true,
- Regex: true,
- }
-}
-
-// StringEqualWithContext compares two strings and provides the complete actual value
-// as error context.
-// If value mappings are given, the expected string is evaluated by envsubst, first.
-// It is an error for actual to be nil. Use BeNil() instead.
-func StringEqualWithContext(expected string, subst ...Substitutions) types.GomegaMatcher {
- var err error
- expected, err = eval(expected, subst...)
- if err != nil {
- return &reportError{err}
- }
- return &StringEqualMatcher{
- Expected: expected,
- }
-}
-
-// StringMatchWithContext matches a string by a regular expression and provides
-// the complete actual value as error context.
-// If value mappings are given, the expected string is evaluated by envsubst, first.
-// It is an error for actual to be nil. Use BeNil() instead.
-func StringMatchWithContext(expected string, subst ...Substitutions) types.GomegaMatcher {
- var err error
- expected, err = eval(expected, subst...)
- if err != nil {
- return &reportError{err}
- }
- return &StringEqualMatcher{
- Expected: expected,
- Regex: true,
- }
-}
-
-type StringEqualMatcher struct {
- Expected string
- Trim bool
- Regex bool
-}
-
-func (matcher *StringEqualMatcher) Match(actual interface{}) (success bool, err error) {
- if actual == nil {
- return false, fmt.Errorf("Refusing to compare to .")
- }
-
- s, err := AsString(actual)
- if err != nil {
- return false, err
- }
- if matcher.Regex {
- expected := matcher.Expected
- if matcher.Trim {
- expected = strings.TrimSpace(expected)
- }
- r, err := regexp.Compile(expected)
- if err != nil {
- return false, errors.Wrapf(err, "Invalid regular expression %q", matcher.Regex)
- }
- if matcher.Trim {
- return r.MatchString(strings.TrimSpace(s)), nil
- }
- return r.MatchString(s), nil
- } else {
- if matcher.Trim {
- return strings.TrimSpace(s) == strings.TrimSpace(matcher.Expected), nil
- }
- return s == matcher.Expected, nil
- }
-}
-
-func (matcher *StringEqualMatcher) FailureMessage(actual interface{}) (message string) {
- actualString, err := AsString(actual)
- if err == nil {
- compare, expected := actualString, matcher.Expected
- if matcher.Trim {
- compare = strings.TrimSpace(actualString)
- expected = strings.TrimSpace(matcher.Expected)
- }
- return fmt.Sprintf(
- "Found\n%s\n%s",
- actualString,
- format.MessageWithDiff(compare, "to equal", expected),
- )
- }
- return format.Message(actual, "to equal", matcher.Expected)
-}
-
-func (matcher *StringEqualMatcher) NegatedFailureMessage(actual interface{}) (message string) {
- actualString, err := AsString(actual)
- if err == nil {
- return format.Message(actualString, "not to equal", matcher.Expected)
- }
- return format.Message(actual, "not to equal", matcher.Expected)
-}
-
-func eval(expected string, subst ...Substitutions) (string, error) {
- if len(subst) > 0 {
- return envsubst.Eval(expected, stringmapping(subst...))
- }
- return expected, nil
-}
-
-func stringmapping(values ...Substitutions) func(variable string) string {
- return func(variable string) string {
- for _, m := range values {
- if v, ok := m[variable]; ok {
- return v
- }
- }
- return "${" + variable + "}"
- }
-}
-
-type reportError struct {
- err error
-}
-
-func (r *reportError) Match(actual interface{}) (success bool, err error) {
- return false, err
-}
-
-func (r *reportError) FailureMessage(actual interface{}) (message string) {
- return r.err.Error()
-}
-
-func (r *reportError) NegatedFailureMessage(actual interface{}) (message string) {
- return r.err.Error()
-}
diff --git a/api/utils/testutils/tcp.go b/api/utils/testutils/tcp.go
deleted file mode 100644
index 05286141e0..0000000000
--- a/api/utils/testutils/tcp.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package testutils
-
-import (
- "context"
- "net"
- "time"
-
- "github.com/mandelsoft/goutils/errors"
-)
-
-func PingTCPServer(address string, dur time.Duration) error {
- var conn net.Conn
- var d net.Dialer
-
- ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
- defer cancel()
-
- end := time.Now().Add(dur)
- err := errors.New("timed out waiting for server to start")
- for time.Now().Before(end) {
- conn, err = d.DialContext(ctx, "tcp", address)
- if err != nil {
- time.Sleep(time.Second)
- continue
- }
- conn.Close()
- break
- }
- return err
-}
diff --git a/api/utils/testutils/utils.go b/api/utils/testutils/utils.go
deleted file mode 100644
index 5fab786702..0000000000
--- a/api/utils/testutils/utils.go
+++ /dev/null
@@ -1,157 +0,0 @@
-package testutils
-
-import (
- "encoding/json"
- "fmt"
- "io"
-
- . "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
-
- "github.com/onsi/gomega/types"
-
- "ocm.software/ocm/api/utils"
- "ocm.software/ocm/api/utils/runtime"
-)
-
-func Close(c io.Closer, msg ...interface{}) {
- DeferWithOffset(1, c.Close, msg...)
-}
-
-func Defer(f func() error, msg ...interface{}) {
- DeferWithOffset(1, f, msg...)
-}
-
-func DeferWithOffset(o int, f func() error, msg ...interface{}) {
- err := f()
- if err != nil {
- switch len(msg) {
- case 0:
- ExpectWithOffset(1+o, err).To(Succeed())
- case 1:
- Fail(fmt.Sprintf("%s: %s", msg[0], err), 1+o)
- default:
- Fail(fmt.Sprintf("%s: %s", fmt.Sprintf(msg[0].(string), msg[1:]...), err), 1+o)
- }
- }
-}
-
-func NotNil[T any](o T, extra ...interface{}) T {
- ExpectWithOffset(1, o, extra...).NotTo(BeNil())
- return o
-}
-
-func Must[T any](o T, err error) T {
- ExpectWithOffset(1, err).To(Succeed())
- return o
-}
-
-func Must2[T any, V any](a T, b V, err error) (T, V) {
- ExpectWithOffset(1, err).To(Succeed())
- return a, b
-}
-
-func Must3[T, U, V any](a T, b U, c V, err error) (T, U, V) {
- ExpectWithOffset(1, err).To(Succeed())
- return a, b, c
-}
-
-type result[T any] struct {
- res T
- err error
-}
-
-func (r result[T]) Must(offset ...int) T {
- ExpectWithOffset(utils.Optional(offset...)+1, r.err).To(Succeed())
- return r.res
-}
-
-func R[T any](o T, err error) result[T] {
- return Calling(o, err)
-}
-
-func Calling[T any](o T, err error) result[T] {
- return result[T]{o, err}
-}
-
-func MustWithOffset[T any](offset int, res result[T]) T {
- ExpectWithOffset(offset+1, res.err).To(Succeed())
- return res.res
-}
-
-func MustBeNonNil[T any](o T) T {
- ExpectWithOffset(1, o).NotTo(BeNil())
- return o
-}
-
-func MustBeSuccessful(actual ...interface{}) {
- if actual[len(actual)-1] == nil {
- return
- }
- err, ok := actual[len(actual)-1].(error)
- if !ok {
- Fail("no errors return", 1)
- }
- ExpectWithOffset(1, err).To(Succeed())
-}
-
-func MustBeSuccessfulWithOffset(offset int, err error) {
- ExpectWithOffset(offset+1, err).To(Succeed())
-}
-
-func MustFailWithMessage(err error, msg string) {
- ExpectWithOffset(1, err).To(HaveOccurred())
- ExpectWithOffset(1, err.Error()).To(Equal(msg))
-}
-
-func ErrorFrom(args ...interface{}) error {
- e, ok := args[len(args)-1].(error)
- if !ok {
- Fail("no errors return", 1)
- }
- return e
-}
-
-func ExpectError(values ...interface{}) types.Assertion {
- return Expect(values[len(values)-1])
-}
-
-func AsString(actual interface{}) (string, error) {
- s, ok := actual.(string)
- if !ok {
- b, ok := actual.([]byte)
- if !ok {
- return "", fmt.Errorf("Actual value is no string (or byte array), but a %T.", actual)
- }
- s = string(b)
- }
- return s, nil
-}
-
-func AsStructure(actual interface{}, substs ...Substitutions) (interface{}, error) {
- var err error
-
- s, ok := actual.(string)
- if !ok {
- b, ok := actual.([]byte)
- if !ok {
- b, err = json.Marshal(actual)
- if err != nil {
- return "", fmt.Errorf("Actual value (%T) is no string, byte array, or serializable object.", actual)
- }
- }
- s = string(b)
- }
- if subst := MergeSubst(substs...); len(subst) != 0 {
- s, err = eval(s, subst)
- if err != nil {
- return nil, err
- }
- }
- var value interface{}
- err = runtime.DefaultYAMLEncoding.Unmarshal([]byte(s), &value)
- if err != nil {
- return nil, err
- }
- return value, nil
-}
diff --git a/api/utils/testutils/yaml.go b/api/utils/testutils/yaml.go
deleted file mode 100644
index f4ab69bd2e..0000000000
--- a/api/utils/testutils/yaml.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package testutils
-
-import (
- "fmt"
- "reflect"
- "strings"
-
- "github.com/go-test/deep"
- "github.com/onsi/gomega/format"
- "github.com/onsi/gomega/types"
-
- "ocm.software/ocm/api/utils/runtime"
-)
-
-// YAMLEqual compares two yaml structures.
-// If value mappings are given, the expected string is evaluated by envsubst, first.
-// It is an error for actual to be nil. Use BeNil() instead.
-func YAMLEqual(expected interface{}, subst ...Substitutions) types.GomegaMatcher {
- data, err := AsStructure(expected, subst...)
- if err != nil {
- return &reportError{err}
- }
-
- return &YAMLEqualMatcher{
- Expected: data,
- }
-}
-
-type YAMLEqualMatcher struct {
- Expected interface{}
-}
-
-func (matcher *YAMLEqualMatcher) Match(actual interface{}) (success bool, err error) {
- if actual == nil {
- return false, fmt.Errorf("Refusing to compare to .")
- }
-
- data, err := AsStructure(actual)
- if err != nil {
- return false, err
- }
- return reflect.DeepEqual(data, matcher.Expected), nil
-}
-
-func (matcher *YAMLEqualMatcher) FailureMessage(actual interface{}) (message string) {
- data, err := AsStructure(actual)
- if err == nil {
- diff := deep.Equal(data, matcher.Expected)
- if len(diff) > 0 {
- eff, _ := runtime.DefaultYAMLEncoding.Marshal(data)
- return fmt.Sprintf(
- "Found\n%s\n%s",
- string(eff),
- fmt.Sprintf("unexpected diff in YAML: \n %s\n", strings.Join(diff, "\n ")))
- }
- return "identical"
- }
- return format.Message(actual, "to equal", matcher.Expected, err.Error())
-}
-
-func (matcher *YAMLEqualMatcher) NegatedFailureMessage(actual interface{}) (message string) {
- return format.Message(actual, "not to equal", matcher.Expected)
-}
diff --git a/cmds/helminstaller/app/execute.go b/cmds/helminstaller/app/execute.go
index 0e9e4748b2..786515be1c 100644
--- a/cmds/helminstaller/app/execute.go
+++ b/cmds/helminstaller/app/execute.go
@@ -17,6 +17,7 @@ import (
resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
"ocm.software/ocm/api/ocm/extensions/download"
utils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/ocm/tools/toi/support"
"ocm.software/ocm/api/tech/helm/loader"
"ocm.software/ocm/api/utils/compression"
@@ -99,7 +100,7 @@ func (e *Execution) addSubCharts(finalize *Finalizer, subCharts map[string]v1.Re
e.outf("Loading %d sub charts into %s...\n", len(subCharts), charts)
for n, r := range subCharts {
e.outf(" Loading sub chart %q from resource %s@%s\n", n, r, common.VersionedElementKey(e.ComponentVersion))
- acc, rcv := Must2f(R2(utils.ResolveResourceReference(e.ComponentVersion, r, nil)), "chart reference", r.String())
+ acc, rcv := Must2f(R2(resourcerefs.ResolveResourceReference(e.ComponentVersion, r, nil)), "chart reference", r.String())
loop.Close(rcv)
if acc.Meta().Type != resourcetypes.HELM_CHART {
@@ -158,7 +159,7 @@ func (e *Execution) Execute(cfg *Config, values map[string]interface{}, kubeconf
values = Merge(Must1(cfg.GetValues()), values)
e.outf("Loading helm chart from resource %s@%s\n", cfg.Chart, common.VersionedElementKey(e.ComponentVersion))
- acc, rcv := Must2f(R2(utils.ResolveResourceReference(e.ComponentVersion, cfg.Chart, nil)), "chart reference", cfg.Chart.String())
+ acc, rcv := Must2f(R2(resourcerefs.ResolveResourceReference(e.ComponentVersion, cfg.Chart, nil)), "chart reference", cfg.Chart.String())
finalize.Close(rcv)
if acc.Meta().Type != resourcetypes.HELM_CHART {
@@ -189,7 +190,7 @@ func (e *Execution) Execute(cfg *Config, values map[string]interface{}, kubeconf
e.outf("Localizing helm chart...\n")
e.Logger.Debug("Localizing helm chart")
for i, v := range cfg.ImageMapping {
- acc, rcv := Must2f(R2(utils.ResolveResourceReference(e.ComponentVersion, v.ResourceReference, nil)), "mapping", fmt.Sprintf("%d (%s)", i+1, &v.ResourceReference))
+ acc, rcv := Must2f(R2(resourcerefs.ResolveResourceReference(e.ComponentVersion, v.ResourceReference, nil)), "mapping", fmt.Sprintf("%d (%s)", i+1, &v.ResourceReference))
rcv.Close()
ref := Must1f(R1(utils.GetOCIArtifactRef(e.Context, acc)), "mapping %d: cannot resolve resource %s to an OCI Reference", i+1, v)
ix := strings.Index(ref, ":")
diff --git a/cmds/ocm/commands/ocmcmds/common/cmds/signing/cmd.go b/cmds/ocm/commands/ocmcmds/common/cmds/signing/cmd.go
index 160b2ee849..3cbfafc31b 100644
--- a/cmds/ocm/commands/ocmcmds/common/cmds/signing/cmd.go
+++ b/cmds/ocm/commands/ocmcmds/common/cmds/signing/cmd.go
@@ -10,6 +10,7 @@ import (
"ocm.software/ocm/api/ocm"
"ocm.software/ocm/api/ocm/compdesc"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/ocm/tools/signing"
common "ocm.software/ocm/api/utils/misc"
ocmcommon "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common"
@@ -130,7 +131,7 @@ func NewAction(desc []string, ctx ocm.Context, p common.Printer, sopts *signing.
func (a *action) Digest(o *comphdlr.Object) (*metav1.DigestSpec, *compdesc.ComponentDescriptor, error) {
sopts := *a.sopts
- sopts.Resolver = ocm.NewCompoundResolver(o.Repository, a.sopts.Resolver)
+ sopts.Resolver = resolvers.NewCompoundResolver(o.Repository, a.sopts.Resolver)
d, err := signing.Apply(a.printer, &a.state, o.ComponentVersion, &sopts)
var cd *compdesc.ComponentDescriptor
nv := common.VersionedElementKey(o.ComponentVersion)
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go b/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go
index 6efae4f9dd..40085d4af5 100644
--- a/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/options/standard.go
@@ -24,6 +24,8 @@ var (
RegistryOption = options.NPMRegistryOption
PackageOption = options.NPMPackageOption
PackageVersionOption = options.NPMVersionOption
+
+ IdentityPathOption = options.IdentityPathOption
)
// string options.
@@ -68,4 +70,7 @@ var (
FormattedJSONOption = flagsets.NewYAMLOptionType("inputFormattedJson", "JSON formatted text")
)
-var ValuesOption = flagsets.NewValueMapYAMLOptionType("inputValues", "YAML based generic values for inputs")
+var (
+ ValuesOption = flagsets.NewValueMapYAMLOptionType("inputValues", "YAML based generic values for inputs")
+ ComponentOption = flagsets.NewStringOptionType("inputComponent", "component name")
+)
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/init.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/init.go
index 2368661be0..a692cd100f 100644
--- a/cmds/ocm/commands/ocmcmds/common/inputs/types/init.go
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/init.go
@@ -9,6 +9,7 @@ import (
_ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/helm"
_ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/maven"
_ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/ociartifact"
+ _ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm"
_ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/spiff"
_ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/utf8"
_ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/wget"
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/maven/spec.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/maven/spec.go
index 4ffb28085c..1e443f514e 100644
--- a/cmds/ocm/commands/ocmcmds/common/inputs/types/maven/spec.go
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/maven/spec.go
@@ -63,7 +63,7 @@ func (s *Spec) Validate(fldPath *field.Path, ctx inputs.Context, inputFilePath s
}
if s.Version == "" {
pathField := fldPath.Child("version")
- allErrs = append(allErrs, field.Invalid(pathField, s.GroupId, "no group id"))
+ allErrs = append(allErrs, field.Invalid(pathField, s.GroupId, "no version"))
}
return allErrs
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/cli.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/cli.go
new file mode 100644
index 0000000000..1ad97c5fc9
--- /dev/null
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/cli.go
@@ -0,0 +1,25 @@
+package ocm
+
+import (
+ acc "ocm.software/ocm/api/ocm/extensions/accessmethods/options"
+ "ocm.software/ocm/api/utils/cobrautils/flagsets"
+ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/options"
+)
+
+func ConfigHandler() flagsets.ConfigOptionTypeSetHandler {
+ return flagsets.NewConfigOptionTypeSetHandler(
+ TYPE, AddConfig,
+ options.RepositoryOption,
+ options.ComponentOption,
+ options.VersionOption,
+ options.IdentityPathOption,
+ )
+}
+
+func AddConfig(opts flagsets.ConfigOptions, config flagsets.Config) error {
+ flagsets.AddFieldByMappedOptionP(opts, options.RepositoryOption, config, acc.MapRepository, "ocmRepository")
+ flagsets.AddFieldByOptionP(opts, options.ComponentOption, config, "component")
+ flagsets.AddFieldByOptionP(opts, options.VersionOption, config, "version")
+ flagsets.AddFieldByMappedOptionP(opts, options.IdentityPathOption, config, acc.MapResourceRef, "resourceRef")
+ return nil
+}
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/input_test.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/input_test.go
new file mode 100644
index 0000000000..10bbca8a9c
--- /dev/null
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/input_test.go
@@ -0,0 +1,93 @@
+package ocm_test
+
+import (
+ . "github.com/mandelsoft/goutils/testutils"
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ . "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/testutils"
+ . "ocm.software/ocm/cmds/ocm/testhelper"
+
+ "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/ocm/extensions/accessmethods/localblob"
+ resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
+ "ocm.software/ocm/api/ocm/extensions/repositories/comparch"
+ "ocm.software/ocm/api/ocm/extensions/repositories/ocireg"
+ "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/utils/accessio"
+ "ocm.software/ocm/api/utils/accessobj"
+ "ocm.software/ocm/api/utils/mime"
+
+ v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs"
+ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/options"
+ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm"
+)
+
+const (
+ COMP = "acme.org/test"
+ VERS = "v1"
+ ARCH = "/ca"
+
+ REPO = "/ctf"
+ REFCOMP = "acme.org/ref"
+ REFRSC = "res"
+)
+
+var _ = Describe("Input Type", func() {
+ Context("spec", func() {
+ var env *InputTest
+
+ BeforeEach(func() {
+ env = NewInputTest(ocm.TYPE)
+ })
+
+ It("simple spec", func() {
+ env.Set(options.RepositoryOption, "ghcr.io/open-component-model")
+ env.Set(options.ComponentOption, COMP)
+ env.Set(options.VersionOption, VERS)
+ env.Set(options.IdentityPathOption, "name=test")
+ env.Check(&ocm.Spec{
+ InputSpecBase: inputs.InputSpecBase{},
+ OCMRepository: Must(cpi.ToGenericRepositorySpec(ocireg.NewRepositorySpec("ghcr.io/open-component-model"))),
+ Component: COMP,
+ Version: VERS,
+ ResourceRef: v1.NewResourceRef(v1.NewIdentity("test")),
+ })
+ })
+ })
+
+ Context("compose", func() {
+ var env *TestEnv
+
+ BeforeEach(func() {
+ env = NewTestEnv(TestData())
+
+ env.OCMCommonTransport(REPO, accessio.FormatDirectory, func() {
+ env.ComponentVersion(REFCOMP, VERS, func() {
+ env.Resource(REFRSC, VERS, resourcetypes.PLAIN_TEXT, v1.LocalRelation, func() {
+ env.BlobStringData(mime.MIME_TEXT, "test data")
+ })
+ })
+ })
+
+ Expect(env.Execute("create", "ca", "-ft", "directory", COMP, VERS, "--provider", "acme.org", "--file", ARCH)).To(Succeed())
+ })
+
+ AfterEach(func() {
+ env.Cleanup()
+ })
+
+ It("adds ocm resource", func() {
+ Expect(env.Execute("add", "resources", "--file", ARCH, "/testdata/resources1.yaml")).To(Succeed())
+
+ ca := Must(comparch.Open(env, accessobj.ACC_READONLY, ARCH, 0, env))
+ defer Close(ca)
+
+ r := Must(ca.GetResourceByIndex(0))
+ Expect(Must(r.Access()).GetKind()).To(Equal(localblob.Type))
+
+ data := Must(ocmutils.GetResourceData(r))
+ Expect(string(data)).To(Equal("test data"))
+ })
+ })
+})
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/spec.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/spec.go
new file mode 100644
index 0000000000..1876bae5bc
--- /dev/null
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/spec.go
@@ -0,0 +1,74 @@
+package ocm
+
+import (
+ "k8s.io/apimachinery/pkg/util/validation/field"
+
+ "ocm.software/ocm/api/credentials"
+ metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
+ cpi2 "ocm.software/ocm/api/ocm/cpi"
+ "ocm.software/ocm/api/utils/blobaccess"
+ "ocm.software/ocm/api/utils/blobaccess/ocm"
+ "ocm.software/ocm/api/utils/runtime"
+ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs"
+)
+
+type Spec struct {
+ inputs.InputSpecBase `json:",inline"`
+
+ // OCMRepository is the URL of the OCM repository to load the chart from.
+ OCMRepository *cpi2.GenericRepositorySpec `json:"ocmRepository,omitempty"`
+
+ // Component if the name of the root component used to lookup the resource.
+ Component string `json:"component,omitempty"`
+
+ // Version is the version og the root component.
+ Version string `json:"version,omitempty,"`
+
+ ResourceRef metav1.ResourceReference `json:"resourceRef"`
+}
+
+var _ inputs.InputSpec = (*Spec)(nil)
+
+func New(comp, vers string, repo cpi2.RepositorySpec, id metav1.Identity, path ...metav1.Identity) (inputs.InputSpec, error) {
+ spec, err := cpi2.ToGenericRepositorySpec(repo)
+ if err != nil {
+ return nil, err
+ }
+ return &Spec{
+ InputSpecBase: inputs.InputSpecBase{
+ ObjectVersionedType: runtime.ObjectVersionedType{
+ Type: TYPE,
+ },
+ },
+ OCMRepository: spec,
+ Component: comp,
+ Version: vers,
+ ResourceRef: metav1.NewNestedResourceRef(id, path),
+ }, nil
+}
+
+func (s *Spec) Validate(fldPath *field.Path, ctx inputs.Context, inputFilePath string) field.ErrorList {
+ var allErrs field.ErrorList
+
+ err := s.OCMRepository.Validate(ctx.OCMContext(), nil, credentials.StringUsageContext(s.Component))
+ if err != nil {
+ data, _ := s.OCMRepository.MarshalJSON()
+ pathField := fldPath.Child("ocmRepository")
+ allErrs = append(allErrs, field.Invalid(pathField, string(data), err.Error()))
+ }
+ if s.Component == "" {
+ pathField := fldPath.Child("component")
+ allErrs = append(allErrs, field.Invalid(pathField, s.Component, "no component name"))
+ }
+ if s.Version == "" {
+ pathField := fldPath.Child("version")
+ allErrs = append(allErrs, field.Invalid(pathField, s.Version, "no component version"))
+ }
+
+ return allErrs
+}
+
+func (s *Spec) GetBlob(ctx inputs.Context, info inputs.InputResourceInfo) (blobaccess.BlobAccess, string, error) {
+ b, err := ocm.BlobAccess(ocm.ByRepositorySpecAndName(ctx.OCMContext(), s.OCMRepository, s.Component, s.Version), ocm.ByResourceRef(s.ResourceRef))
+ return b, "", err
+}
diff --git a/api/utils/testutils/suite_test.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/suite_test.go
similarity index 74%
rename from api/utils/testutils/suite_test.go
rename to cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/suite_test.go
index c47f9dd5f9..450f29ace8 100644
--- a/api/utils/testutils/suite_test.go
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/suite_test.go
@@ -1,4 +1,4 @@
-package testutils_test
+package ocm_test
import (
"testing"
@@ -9,5 +9,5 @@ import (
func TestConfig(t *testing.T) {
RegisterFailHandler(Fail)
- RunSpecs(t, "Testutils")
+ RunSpecs(t, "Input Type ocm")
}
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/testdata/resources1.yaml b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/testdata/resources1.yaml
new file mode 100644
index 0000000000..8518822909
--- /dev/null
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/testdata/resources1.yaml
@@ -0,0 +1,12 @@
+name: myblob
+type: plainText
+input:
+ type: ocm
+ ocmRepository:
+ type: CommonTransportFormat
+ filePath: /ctf
+ component: acme.org/ref
+ version: v1
+ resourceRef:
+ resource:
+ name: res
\ No newline at end of file
diff --git a/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/type.go b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/type.go
new file mode 100644
index 0000000000..fb50a4a826
--- /dev/null
+++ b/cmds/ocm/commands/ocmcmds/common/inputs/types/ocm/type.go
@@ -0,0 +1,33 @@
+package ocm
+
+import (
+ "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/inputs"
+)
+
+const TYPE = "ocm"
+
+func init() {
+ inputs.DefaultInputTypeScheme.Register(inputs.NewInputType(TYPE, &Spec{}, usage, ConfigHandler()))
+}
+
+const usage = `
+This input type allows to get a resource artifact from an OCM repository.
+
+This blob type specification supports the following fields:
+- **ocmRepository
** *repository specification*
+
+ This REQUIRED property describes the OCM repository specification
+
+- **component
** *string*
+
+ This REQUIRED property describes the component na,e
+
+- **version
** *string*
+
+ This REQUIRED property describes the version of a maven artifact.
+
+- **resourceRef
** *relative resource reference*
+
+ This REQUIRED property describes the resource reference for the desired
+ resource relative to the given component version .
+`
diff --git a/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go
index 02afe53871..8b3d00006a 100644
--- a/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go
+++ b/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go
@@ -5,6 +5,7 @@ import (
clictx "ocm.software/ocm/api/cli"
"ocm.software/ocm/api/ocm"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/ocm/tools/transfer/transferhandler"
"ocm.software/ocm/api/ocm/tools/transfer/transferhandler/standard"
"ocm.software/ocm/cmds/ocm/common/options"
@@ -49,15 +50,15 @@ func (o *Option) CompleteWithSession(octx clictx.OCM, session ocm.Session) error
func (o *Option) getResolver(ctx clictx.OCM, session ocm.Session) (ocm.ComponentVersionResolver, error) {
if len(o.RepoSpecs) != 0 {
- resolvers := []ocm.ComponentVersionResolver{}
+ resolver := []ocm.ComponentVersionResolver{}
for _, s := range o.RepoSpecs {
r, _, err := session.DetermineRepository(ctx.Context(), s)
if err != nil {
return nil, err
}
- resolvers = append(resolvers, r)
+ resolver = append(resolver, r)
}
- return ocm.NewCompoundResolver(append(resolvers, ctx.Context().GetResolver())...), nil
+ return resolvers.NewCompoundResolver(append(resolver, ctx.Context().GetResolver())...), nil
}
return ctx.Context().GetResolver(), nil
}
diff --git a/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go
index 14e1dd4091..2633d17229 100644
--- a/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go
+++ b/cmds/ocm/commands/ocmcmds/components/verify/cmd_test.go
@@ -22,6 +22,7 @@ import (
resourcetypes "ocm.software/ocm/api/ocm/extensions/artifacttypes"
"ocm.software/ocm/api/ocm/extensions/attrs/signingattr"
"ocm.software/ocm/api/ocm/extensions/repositories/ctf"
+ "ocm.software/ocm/api/ocm/resolvers"
"ocm.software/ocm/api/tech/signing/handlers/rsa"
"ocm.software/ocm/api/utils/accessio"
"ocm.software/ocm/api/utils/accessobj"
@@ -123,7 +124,7 @@ var _ = Describe("access method", func() {
src, err := ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env)
Expect(err).To(Succeed())
archcloser := session.AddCloser(src)
- resolver := ocm.NewCompoundResolver(src)
+ resolver := resolvers.NewCompoundResolver(src)
cv, err := resolver.LookupComponentVersion(COMPONENTB, VERSION)
Expect(err).To(Succeed())
diff --git a/cmds/ocm/commands/toicmds/config/bootstrap/cmd.go b/cmds/ocm/commands/toicmds/config/bootstrap/cmd.go
index 8f2082d02e..17dd8d042a 100644
--- a/cmds/ocm/commands/toicmds/config/bootstrap/cmd.go
+++ b/cmds/ocm/commands/toicmds/config/bootstrap/cmd.go
@@ -16,7 +16,7 @@ import (
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/ocm/extensions/attrs/ociuploadattr"
"ocm.software/ocm/api/ocm/extensions/download"
- utils2 "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/ocm/tools/toi"
"ocm.software/ocm/api/ocm/tools/toi/install"
utils3 "ocm.software/ocm/api/utils"
@@ -161,7 +161,7 @@ func (a *action) Out() error {
rid := metav1.NewResourceRef(a.cmd.Id)
resolver := lookupoption.From(a.cmd)
- ires, eff, err := utils2.MatchResourceReference(cv, toi.TypeTOIPackage, rid, resolver)
+ ires, eff, err := resourcerefs.MatchResourceReference(cv, toi.TypeTOIPackage, rid, resolver)
if err != nil {
return errors.Wrapf(err, "package resource in %s", nv)
}
@@ -243,7 +243,7 @@ func (a *action) handle(kind, path string, cv ocm.ComponentVersionAccess, spec *
}
func (a *action) download(kind, path string, cv ocm.ComponentVersionAccess, spec *metav1.ResourceReference, resolver ocm.ComponentVersionResolver) error {
- res, _, err := utils2.MatchResourceReference(cv, toi.TypeYAML, *spec, resolver)
+ res, _, err := resourcerefs.MatchResourceReference(cv, toi.TypeYAML, *spec, resolver)
if err != nil {
return errors.Wrapf(err, "%s resource", kind)
}
diff --git a/cmds/ocm/commands/toicmds/package/describe/cmd.go b/cmds/ocm/commands/toicmds/package/describe/cmd.go
index 98a3e853b7..73ceb1f60b 100644
--- a/cmds/ocm/commands/toicmds/package/describe/cmd.go
+++ b/cmds/ocm/commands/toicmds/package/describe/cmd.go
@@ -12,7 +12,7 @@ import (
"ocm.software/ocm/api/ocm"
metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/ocm/extensions/attrs/ociuploadattr"
- utils2 "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/ocm/tools/toi"
"ocm.software/ocm/api/ocm/tools/toi/install"
utils3 "ocm.software/ocm/api/utils"
@@ -164,7 +164,7 @@ func (a *action) describe(cv ocm.ComponentVersionAccess) error {
rid := metav1.NewResourceRef(a.cmd.Id)
resolver := lookupoption.From(a.cmd)
- ires, eff, err := utils2.MatchResourceReference(cv, toi.TypeTOIPackage, rid, resolver)
+ ires, eff, err := resourcerefs.MatchResourceReference(cv, toi.TypeTOIPackage, rid, resolver)
if err != nil {
return errors.Wrapf(err, "package resource in %s", nv)
}
diff --git a/docs/pluginreference/plugin_accessmethod_compose.md b/docs/pluginreference/plugin_accessmethod_compose.md
index bbe1bf2aa5..75b5291f46 100644
--- a/docs/pluginreference/plugin_accessmethod_compose.md
+++ b/docs/pluginreference/plugin_accessmethod_compose.md
@@ -34,6 +34,7 @@ by the plugin name.
The following predefined option types can be used:
+ - accessComponent
: [*string*] component for access specification
- accessHostname
: [*string*] hostname used for access
- accessPackage
: [*string*] package or object name
- accessRegistry
: [*string*] registry base URL
@@ -51,6 +52,8 @@ The following predefined option types can be used:
- groupId
: [*string*] maven group id
- header
: [*string:string,string*] http headers
- hint
: [*string*] (repository) hint for local artifacts
+ - identityPath
: [*[]identity*] identity path for specification
+ - idpath
: [*[]string*] identity path (attr=value{,attr=value}
- mediaType
: [*string*] media type for artifact blob representation
- noredirect
: [*bool*] http redirect behavior
- package
: [*string*] npm package name
@@ -67,6 +70,7 @@ The following predefined value types are supported:
- YAML
: JSON or YAML document string
- []byte
: byte value
+ - []identity
: identity path
- []string
: list of string values
- bool
: boolean flag
- int
: integer value
diff --git a/docs/pluginreference/plugin_descriptor.md b/docs/pluginreference/plugin_descriptor.md
index 2d4cb97ba3..ac7ffdcb5b 100644
--- a/docs/pluginreference/plugin_descriptor.md
+++ b/docs/pluginreference/plugin_descriptor.md
@@ -119,6 +119,7 @@ It uses the following fields:
The following predefined option types can be used:
+ - accessComponent
: [*string*] component for access specification
- accessHostname
: [*string*] hostname used for access
- accessPackage
: [*string*] package or object name
- accessRegistry
: [*string*] registry base URL
@@ -136,6 +137,8 @@ The following predefined option types can be used:
- groupId
: [*string*] maven group id
- header
: [*string:string,string*] http headers
- hint
: [*string*] (repository) hint for local artifacts
+ - identityPath
: [*[]identity*] identity path for specification
+ - idpath
: [*[]string*] identity path (attr=value{,attr=value}
- mediaType
: [*string*] media type for artifact blob representation
- noredirect
: [*bool*] http redirect behavior
- package
: [*string*] npm package name
@@ -152,6 +155,7 @@ The following predefined value types are supported:
- YAML
: JSON or YAML document string
- []byte
: byte value
+ - []identity
: identity path
- []string
: list of string values
- bool
: boolean flag
- int
: integer value
diff --git a/docs/pluginreference/plugin_valueset_compose.md b/docs/pluginreference/plugin_valueset_compose.md
index af7c03f30f..16e93871d9 100644
--- a/docs/pluginreference/plugin_valueset_compose.md
+++ b/docs/pluginreference/plugin_valueset_compose.md
@@ -34,6 +34,7 @@ by the plugin name.
The following predefined option types can be used:
+ - accessComponent
: [*string*] component for access specification
- accessHostname
: [*string*] hostname used for access
- accessPackage
: [*string*] package or object name
- accessRegistry
: [*string*] registry base URL
@@ -51,6 +52,8 @@ The following predefined option types can be used:
- groupId
: [*string*] maven group id
- header
: [*string:string,string*] http headers
- hint
: [*string*] (repository) hint for local artifacts
+ - identityPath
: [*[]identity*] identity path for specification
+ - idpath
: [*[]string*] identity path (attr=value{,attr=value}
- mediaType
: [*string*] media type for artifact blob representation
- noredirect
: [*bool*] http redirect behavior
- package
: [*string*] npm package name
@@ -67,6 +70,7 @@ The following predefined value types are supported:
- YAML
: JSON or YAML document string
- []byte
: byte value
+ - []identity
: identity path
- []string
: list of string values
- bool
: boolean flag
- int
: integer value
diff --git a/docs/reference/ocm_add_componentversions.md b/docs/reference/ocm_add_componentversions.md
index 56595a49f8..e5078c492e 100644
--- a/docs/reference/ocm_add_componentversions.md
+++ b/docs/reference/ocm_add_componentversions.md
@@ -184,27 +184,27 @@ The uploader name may be a path expression with the following possibilities:
Alternatively, a single string value can be given representing an OCI repository
reference.
- - ocm/mavenPackage
: uploading maven artifacts
+ - ocm/npmPackage
: uploading npm artifacts
- The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
- as artifact archive according to the maven artifact spec.
+ The ocm/npmPackage
uploader is able to upload npm artifacts
+ as artifact archive according to the npm package spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the maven repository.
+ 'url': the URL of the npm repository.
- plugin
: [downloaders provided by plugins]
sub namespace of the form <plugin name>/<handler>
- - ocm/npmPackage
: uploading npm artifacts
+ - ocm/mavenPackage
: uploading maven artifacts
- The ocm/npmPackage
uploader is able to upload npm artifacts
- as artifact archive according to the npm package spec.
+ The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
+ as artifact archive according to the maven artifact spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the npm repository.
+ 'url': the URL of the maven repository.
diff --git a/docs/reference/ocm_add_resource-configuration.md b/docs/reference/ocm_add_resource-configuration.md
index f318b0885d..27d8aa09b4 100644
--- a/docs/reference/ocm_add_resource-configuration.md
+++ b/docs/reference/ocm_add_resource-configuration.md
@@ -24,6 +24,7 @@ resource-configuration, resourceconfig, rsccfg, rcfg
```text
--access YAML blob access specification (YAML)
+ --accessComponent string component for access specification
--accessHostname string hostname used for access
--accessPackage string package or object name
--accessRegistry string registry base URL
@@ -41,6 +42,7 @@ resource-configuration, resourceconfig, rsccfg, rcfg
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--mediaType string media type for artifact blob representation
--noredirect http redirect behavior
--reference string reference name
@@ -54,6 +56,7 @@ resource-configuration, resourceconfig, rsccfg, rcfg
#### Input Specification Options
```text
+ --accessRepository string repository URL
--artifactId string maven artifact id
--body string body of a http request
--classifier string maven classifier
@@ -61,7 +64,9 @@ resource-configuration, resourceconfig, rsccfg, rcfg
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--input YAML blob input specification (YAML)
+ --inputComponent string component name
--inputCompress compress option for input
--inputData !bytesBase64 data (string, !!string or !
--inputExcludes stringArray excludes (path) for inputs
@@ -438,6 +443,30 @@ with the field type
in the input
field:
Options used to configure fields: --hint
, --inputCompress
, --inputPath
, --inputPlatforms
, --mediaType
+- Input type ocm
+
+ This input type allows to get a resource artifact from an OCM repository.
+
+ This blob type specification supports the following fields:
+ - **ocmRepository
** *repository specification*
+
+ This REQUIRED property describes the OCM repository specification
+
+ - **component
** *string*
+
+ This REQUIRED property describes the component na,e
+
+ - **version
** *string*
+
+ This REQUIRED property describes the version of a maven artifact.
+
+ - **resourceRef
** *relative resource reference*
+
+ This REQUIRED property describes the resource reference for the desired
+ resource relative to the given component version .
+
+ Options used to configure fields: --accessRepository
, --identityPath
, --inputComponent
, --inputVersion
+
- Input type spiff
The path must denote a [spiff](https://github.com/mandelsoft/spiff) template relative the resources file.
@@ -830,6 +859,40 @@ shown below.
Options used to configure fields: --digest
, --mediaType
, --reference
, --size
+- Access type ocm
+
+ This method implements the access of any resource artifact stored in an OCM
+ repository. Only repository types supporting remote access should be used.
+
+ The following versions are supported:
+ - Version v1
+
+ The type specific specification fields are:
+
+ - **ocmRepository
** *json*
+
+ The repository spec for the OCM repository
+
+ - **component
** *string*
+
+ *(Optional)* The name of the component. The default is the
+ own component.
+
+ - **version
** *string*
+
+ *(Optional)* The version of the component. The default is the
+ own component version.
+
+ - **resourceRef
** *relative resource ref*
+
+ The resource reference of the denoted resource relative to the
+ given component version.
+
+ It uses the consumer identity and credentials for the intermediate repositories
+ and the final resource access.
+
+ Options used to configure fields: --accessComponent
, --accessRepository
, --accessVersion
, --identityPath
+
- Access type s3
This method implements the access of a blob stored in an S3 bucket.
diff --git a/docs/reference/ocm_add_resources.md b/docs/reference/ocm_add_resources.md
index 058d9dda34..f26dee4cc4 100644
--- a/docs/reference/ocm_add_resources.md
+++ b/docs/reference/ocm_add_resources.md
@@ -31,6 +31,7 @@ resources, resource, res, r
```text
--access YAML blob access specification (YAML)
+ --accessComponent string component for access specification
--accessHostname string hostname used for access
--accessPackage string package or object name
--accessRegistry string registry base URL
@@ -48,6 +49,7 @@ resources, resource, res, r
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--mediaType string media type for artifact blob representation
--noredirect http redirect behavior
--reference string reference name
@@ -61,6 +63,7 @@ resources, resource, res, r
#### Input Specification Options
```text
+ --accessRepository string repository URL
--artifactId string maven artifact id
--body string body of a http request
--classifier string maven classifier
@@ -68,7 +71,9 @@ resources, resource, res, r
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--input YAML blob input specification (YAML)
+ --inputComponent string component name
--inputCompress compress option for input
--inputData !bytesBase64 data (string, !!string or !
--inputExcludes stringArray excludes (path) for inputs
@@ -449,6 +454,30 @@ with the field type
in the input
field:
Options used to configure fields: --hint
, --inputCompress
, --inputPath
, --inputPlatforms
, --mediaType
+- Input type ocm
+
+ This input type allows to get a resource artifact from an OCM repository.
+
+ This blob type specification supports the following fields:
+ - **ocmRepository
** *repository specification*
+
+ This REQUIRED property describes the OCM repository specification
+
+ - **component
** *string*
+
+ This REQUIRED property describes the component na,e
+
+ - **version
** *string*
+
+ This REQUIRED property describes the version of a maven artifact.
+
+ - **resourceRef
** *relative resource reference*
+
+ This REQUIRED property describes the resource reference for the desired
+ resource relative to the given component version .
+
+ Options used to configure fields: --accessRepository
, --identityPath
, --inputComponent
, --inputVersion
+
- Input type spiff
The path must denote a [spiff](https://github.com/mandelsoft/spiff) template relative the resources file.
@@ -841,6 +870,40 @@ shown below.
Options used to configure fields: --digest
, --mediaType
, --reference
, --size
+- Access type ocm
+
+ This method implements the access of any resource artifact stored in an OCM
+ repository. Only repository types supporting remote access should be used.
+
+ The following versions are supported:
+ - Version v1
+
+ The type specific specification fields are:
+
+ - **ocmRepository
** *json*
+
+ The repository spec for the OCM repository
+
+ - **component
** *string*
+
+ *(Optional)* The name of the component. The default is the
+ own component.
+
+ - **version
** *string*
+
+ *(Optional)* The version of the component. The default is the
+ own component version.
+
+ - **resourceRef
** *relative resource ref*
+
+ The resource reference of the denoted resource relative to the
+ given component version.
+
+ It uses the consumer identity and credentials for the intermediate repositories
+ and the final resource access.
+
+ Options used to configure fields: --accessComponent
, --accessRepository
, --accessVersion
, --identityPath
+
- Access type s3
This method implements the access of a blob stored in an S3 bucket.
diff --git a/docs/reference/ocm_add_source-configuration.md b/docs/reference/ocm_add_source-configuration.md
index b190c03bc2..108dd21a38 100644
--- a/docs/reference/ocm_add_source-configuration.md
+++ b/docs/reference/ocm_add_source-configuration.md
@@ -24,6 +24,7 @@ source-configuration, sourceconfig, srccfg, scfg
```text
--access YAML blob access specification (YAML)
+ --accessComponent string component for access specification
--accessHostname string hostname used for access
--accessPackage string package or object name
--accessRegistry string registry base URL
@@ -41,6 +42,7 @@ source-configuration, sourceconfig, srccfg, scfg
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--mediaType string media type for artifact blob representation
--noredirect http redirect behavior
--reference string reference name
@@ -54,6 +56,7 @@ source-configuration, sourceconfig, srccfg, scfg
#### Input Specification Options
```text
+ --accessRepository string repository URL
--artifactId string maven artifact id
--body string body of a http request
--classifier string maven classifier
@@ -61,7 +64,9 @@ source-configuration, sourceconfig, srccfg, scfg
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--input YAML blob input specification (YAML)
+ --inputComponent string component name
--inputCompress compress option for input
--inputData !bytesBase64 data (string, !!string or !
--inputExcludes stringArray excludes (path) for inputs
@@ -438,6 +443,30 @@ with the field type
in the input
field:
Options used to configure fields: --hint
, --inputCompress
, --inputPath
, --inputPlatforms
, --mediaType
+- Input type ocm
+
+ This input type allows to get a resource artifact from an OCM repository.
+
+ This blob type specification supports the following fields:
+ - **ocmRepository
** *repository specification*
+
+ This REQUIRED property describes the OCM repository specification
+
+ - **component
** *string*
+
+ This REQUIRED property describes the component na,e
+
+ - **version
** *string*
+
+ This REQUIRED property describes the version of a maven artifact.
+
+ - **resourceRef
** *relative resource reference*
+
+ This REQUIRED property describes the resource reference for the desired
+ resource relative to the given component version .
+
+ Options used to configure fields: --accessRepository
, --identityPath
, --inputComponent
, --inputVersion
+
- Input type spiff
The path must denote a [spiff](https://github.com/mandelsoft/spiff) template relative the resources file.
@@ -830,6 +859,40 @@ shown below.
Options used to configure fields: --digest
, --mediaType
, --reference
, --size
+- Access type ocm
+
+ This method implements the access of any resource artifact stored in an OCM
+ repository. Only repository types supporting remote access should be used.
+
+ The following versions are supported:
+ - Version v1
+
+ The type specific specification fields are:
+
+ - **ocmRepository
** *json*
+
+ The repository spec for the OCM repository
+
+ - **component
** *string*
+
+ *(Optional)* The name of the component. The default is the
+ own component.
+
+ - **version
** *string*
+
+ *(Optional)* The version of the component. The default is the
+ own component version.
+
+ - **resourceRef
** *relative resource ref*
+
+ The resource reference of the denoted resource relative to the
+ given component version.
+
+ It uses the consumer identity and credentials for the intermediate repositories
+ and the final resource access.
+
+ Options used to configure fields: --accessComponent
, --accessRepository
, --accessVersion
, --identityPath
+
- Access type s3
This method implements the access of a blob stored in an S3 bucket.
diff --git a/docs/reference/ocm_add_sources.md b/docs/reference/ocm_add_sources.md
index 4e1375597c..276deb2467 100644
--- a/docs/reference/ocm_add_sources.md
+++ b/docs/reference/ocm_add_sources.md
@@ -30,6 +30,7 @@ sources, source, src, s
```text
--access YAML blob access specification (YAML)
+ --accessComponent string component for access specification
--accessHostname string hostname used for access
--accessPackage string package or object name
--accessRegistry string registry base URL
@@ -47,6 +48,7 @@ sources, source, src, s
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--mediaType string media type for artifact blob representation
--noredirect http redirect behavior
--reference string reference name
@@ -60,6 +62,7 @@ sources, source, src, s
#### Input Specification Options
```text
+ --accessRepository string repository URL
--artifactId string maven artifact id
--body string body of a http request
--classifier string maven classifier
@@ -67,7 +70,9 @@ sources, source, src, s
--groupId string maven group id
--header :,,... http headers (default {})
--hint string (repository) hint for local artifacts
+ --identityPath {=} identity path for specification
--input YAML blob input specification (YAML)
+ --inputComponent string component name
--inputCompress compress option for input
--inputData !bytesBase64 data (string, !!string or !
--inputExcludes stringArray excludes (path) for inputs
@@ -447,6 +452,30 @@ with the field type
in the input
field:
Options used to configure fields: --hint
, --inputCompress
, --inputPath
, --inputPlatforms
, --mediaType
+- Input type ocm
+
+ This input type allows to get a resource artifact from an OCM repository.
+
+ This blob type specification supports the following fields:
+ - **ocmRepository
** *repository specification*
+
+ This REQUIRED property describes the OCM repository specification
+
+ - **component
** *string*
+
+ This REQUIRED property describes the component na,e
+
+ - **version
** *string*
+
+ This REQUIRED property describes the version of a maven artifact.
+
+ - **resourceRef
** *relative resource reference*
+
+ This REQUIRED property describes the resource reference for the desired
+ resource relative to the given component version .
+
+ Options used to configure fields: --accessRepository
, --identityPath
, --inputComponent
, --inputVersion
+
- Input type spiff
The path must denote a [spiff](https://github.com/mandelsoft/spiff) template relative the resources file.
@@ -839,6 +868,40 @@ shown below.
Options used to configure fields: --digest
, --mediaType
, --reference
, --size
+- Access type ocm
+
+ This method implements the access of any resource artifact stored in an OCM
+ repository. Only repository types supporting remote access should be used.
+
+ The following versions are supported:
+ - Version v1
+
+ The type specific specification fields are:
+
+ - **ocmRepository
** *json*
+
+ The repository spec for the OCM repository
+
+ - **component
** *string*
+
+ *(Optional)* The name of the component. The default is the
+ own component.
+
+ - **version
** *string*
+
+ *(Optional)* The version of the component. The default is the
+ own component version.
+
+ - **resourceRef
** *relative resource ref*
+
+ The resource reference of the denoted resource relative to the
+ given component version.
+
+ It uses the consumer identity and credentials for the intermediate repositories
+ and the final resource access.
+
+ Options used to configure fields: --accessComponent
, --accessRepository
, --accessVersion
, --identityPath
+
- Access type s3
This method implements the access of a blob stored in an S3 bucket.
diff --git a/docs/reference/ocm_ocm-accessmethods.md b/docs/reference/ocm_ocm-accessmethods.md
index b4890122e3..6f05c4f706 100644
--- a/docs/reference/ocm_ocm-accessmethods.md
+++ b/docs/reference/ocm_ocm-accessmethods.md
@@ -289,6 +289,40 @@ shown below.
Options used to configure fields: --digest
, --mediaType
, --reference
, --size
+- Access type ocm
+
+ This method implements the access of any resource artifact stored in an OCM
+ repository. Only repository types supporting remote access should be used.
+
+ The following versions are supported:
+ - Version v1
+
+ The type specific specification fields are:
+
+ - **ocmRepository
** *json*
+
+ The repository spec for the OCM repository
+
+ - **component
** *string*
+
+ *(Optional)* The name of the component. The default is the
+ own component.
+
+ - **version
** *string*
+
+ *(Optional)* The version of the component. The default is the
+ own component version.
+
+ - **resourceRef
** *relative resource ref*
+
+ The resource reference of the denoted resource relative to the
+ given component version.
+
+ It uses the consumer identity and credentials for the intermediate repositories
+ and the final resource access.
+
+ Options used to configure fields: --accessComponent
, --accessRepository
, --accessVersion
, --identityPath
+
- Access type s3
This method implements the access of a blob stored in an S3 bucket.
diff --git a/docs/reference/ocm_ocm-uploadhandlers.md b/docs/reference/ocm_ocm-uploadhandlers.md
index a0f9cdf854..1205c6333c 100644
--- a/docs/reference/ocm_ocm-uploadhandlers.md
+++ b/docs/reference/ocm_ocm-uploadhandlers.md
@@ -59,27 +59,27 @@ The following handler names are possible:
Alternatively, a single string value can be given representing an OCI repository
reference.
- - ocm/mavenPackage
: uploading maven artifacts
+ - ocm/npmPackage
: uploading npm artifacts
- The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
- as artifact archive according to the maven artifact spec.
+ The ocm/npmPackage
uploader is able to upload npm artifacts
+ as artifact archive according to the npm package spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the maven repository.
+ 'url': the URL of the npm repository.
- plugin
: [downloaders provided by plugins]
sub namespace of the form <plugin name>/<handler>
- - ocm/npmPackage
: uploading npm artifacts
+ - ocm/mavenPackage
: uploading maven artifacts
- The ocm/npmPackage
uploader is able to upload npm artifacts
- as artifact archive according to the npm package spec.
+ The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
+ as artifact archive according to the maven artifact spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the npm repository.
+ 'url': the URL of the maven repository.
diff --git a/docs/reference/ocm_transfer_commontransportarchive.md b/docs/reference/ocm_transfer_commontransportarchive.md
index 8134517587..0c7f28b12f 100644
--- a/docs/reference/ocm_transfer_commontransportarchive.md
+++ b/docs/reference/ocm_transfer_commontransportarchive.md
@@ -134,27 +134,27 @@ The uploader name may be a path expression with the following possibilities:
Alternatively, a single string value can be given representing an OCI repository
reference.
- - ocm/mavenPackage
: uploading maven artifacts
+ - ocm/npmPackage
: uploading npm artifacts
- The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
- as artifact archive according to the maven artifact spec.
+ The ocm/npmPackage
uploader is able to upload npm artifacts
+ as artifact archive according to the npm package spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the maven repository.
+ 'url': the URL of the npm repository.
- plugin
: [downloaders provided by plugins]
sub namespace of the form <plugin name>/<handler>
- - ocm/npmPackage
: uploading npm artifacts
+ - ocm/mavenPackage
: uploading maven artifacts
- The ocm/npmPackage
uploader is able to upload npm artifacts
- as artifact archive according to the npm package spec.
+ The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
+ as artifact archive according to the maven artifact spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the npm repository.
+ 'url': the URL of the maven repository.
diff --git a/docs/reference/ocm_transfer_componentversions.md b/docs/reference/ocm_transfer_componentversions.md
index fe4741b3b5..ca577a8c10 100644
--- a/docs/reference/ocm_transfer_componentversions.md
+++ b/docs/reference/ocm_transfer_componentversions.md
@@ -191,27 +191,27 @@ The uploader name may be a path expression with the following possibilities:
Alternatively, a single string value can be given representing an OCI repository
reference.
- - ocm/mavenPackage
: uploading maven artifacts
+ - ocm/npmPackage
: uploading npm artifacts
- The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
- as artifact archive according to the maven artifact spec.
+ The ocm/npmPackage
uploader is able to upload npm artifacts
+ as artifact archive according to the npm package spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the maven repository.
+ 'url': the URL of the npm repository.
- plugin
: [downloaders provided by plugins]
sub namespace of the form <plugin name>/<handler>
- - ocm/npmPackage
: uploading npm artifacts
+ - ocm/mavenPackage
: uploading maven artifacts
- The ocm/npmPackage
uploader is able to upload npm artifacts
- as artifact archive according to the npm package spec.
+ The ocm/mavenPackage
uploader is able to upload maven artifacts (whole GAV only!)
+ as artifact archive according to the maven artifact spec.
If registered the default mime type is: application/x-tgz
It accepts a plain string for the URL or a config with the following field:
- 'url': the URL of the npm repository.
+ 'url': the URL of the maven repository.
diff --git a/examples/lib/comparison-scenario/09-localize.go b/examples/lib/comparison-scenario/09-localize.go
index 77337a4f6c..4307a5df71 100644
--- a/examples/lib/comparison-scenario/09-localize.go
+++ b/examples/lib/comparison-scenario/09-localize.go
@@ -14,8 +14,9 @@ import (
"ocm.software/ocm/api/ocm/compdesc"
v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1"
"ocm.software/ocm/api/ocm/extensions/download"
- ocmutils "ocm.software/ocm/api/ocm/ocmutils"
+ "ocm.software/ocm/api/ocm/ocmutils"
"ocm.software/ocm/api/ocm/ocmutils/localize"
+ "ocm.software/ocm/api/ocm/resourcerefs"
"ocm.software/ocm/api/tech/helm/loader"
"ocm.software/ocm/api/utils"
"ocm.software/ocm/api/utils/runtime"
@@ -82,7 +83,7 @@ func EvaluateDeployDescriptor(cv ocm.ComponentVersionAccess, res ocm.ResourceAcc
}
// fourth, get the helm chart
- cres, ccv, err := ocmutils.ResolveResourceReference(cv, desc.ChartResource, resolver)
+ cres, ccv, err := resourcerefs.ResolveResourceReference(cv, desc.ChartResource, resolver)
if err != nil {
return evalErr(err, "cannot resolve chart resource %s", desc.ChartResource)
}
diff --git a/go.mod b/go.mod
index 2b16a99ed9..91d1eb8eac 100644
--- a/go.mod
+++ b/go.mod
@@ -70,7 +70,6 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
- golang.org/x/mod v0.20.0
golang.org/x/net v0.28.0
golang.org/x/oauth2 v0.22.0
golang.org/x/text v0.17.0
@@ -340,6 +339,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
+ golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect