diff --git a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go
index 4200633632..099b9a0451 100644
--- a/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go
+++ b/cmds/ocm/commands/ocmcmds/components/sign/cmd_test.go
@@ -238,7 +238,7 @@ Error: signing: github.com/mandelsoft/ref:v1: failed resolving component referen
buf := bytes.NewBuffer(nil)
Expect(env.CatchErrorOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, "--repo", ARCH, COMPONENTB+":"+VERSION)).To(HaveOccurred())
Expect(buf.String()).To(StringEqualTrimmedWithContext(`
-Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: ocm reference "github.com/mandelsoft/test:v1" not found
+Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: component "github.com/mandelsoft/test" not found in ComponentArchive
`))
})
@@ -246,7 +246,7 @@ Error: signing: github.com/mandelsoft/ref:v1: failed resolving component referen
buf := bytes.NewBuffer(nil)
Expect(env.CatchErrorOutput(buf).Execute("sign", "components", "-s", SIGNATURE, "-K", PRIVKEY, ARCH)).To(HaveOccurred())
Expect(buf.String()).To(StringEqualTrimmedWithContext(`
-Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: ocm reference "github.com/mandelsoft/test:v1" not found
+Error: signing: github.com/mandelsoft/ref:v1: failed resolving component reference ref[github.com/mandelsoft/test:v1]: component "github.com/mandelsoft/test" not found in ComponentArchive
`))
})
})
diff --git a/examples/lib/helper/helper.go b/examples/lib/helper/helper.go
index 26bd9ad00d..0739fa781c 100644
--- a/examples/lib/helper/helper.go
+++ b/examples/lib/helper/helper.go
@@ -15,14 +15,14 @@ import (
)
type Config struct {
- Username string `json:"username"`
- Password string `json:"password"`
- Component string `json:"component"`
- Repository string `json:"repository"`
- Version string `json:"version"`
-
- Target json.RawMessage `json:"targetRepository"`
- OCMConfig string `json:"ocmConfig"`
+ Username string `json:"username,omitempty"`
+ Password string `json:"password,omitempty"`
+ Component string `json:"component,omitempty"`
+ Repository string `json:"repository,omitempty"`
+ Version string `json:"version,omitempty"`
+
+ Target json.RawMessage `json:"targetRepository,omitempty"`
+ OCMConfig string `json:"ocmConfig,omitempty"`
}
func ReadConfig(path string) (*Config, error) {
diff --git a/examples/lib/tour/01-getting-started/README.md b/examples/lib/tour/01-getting-started/README.md
new file mode 100644
index 0000000000..b9623c4739
--- /dev/null
+++ b/examples/lib/tour/01-getting-started/README.md
@@ -0,0 +1,13 @@
+# Basic Usage of OCM Repositories
+
+This [tour](example.go) illustrates the basic usage of the API to
+access component versions in an OCM repository.
+
+You can just call the main program with some config file argument
+with the following content:
+
+```yaml
+component: github.com/mandelsoft/examples/cred1
+repository: ghcr.io/mandelsoft/ocm
+version: 0.1.0
+```
\ No newline at end of file
diff --git a/examples/lib/tour/02-composing-a-component-version/example-a.go b/examples/lib/tour/02-composing-a-component-version/01-basic-componentversion-creation.go
similarity index 99%
rename from examples/lib/tour/02-composing-a-component-version/example-a.go
rename to examples/lib/tour/02-composing-a-component-version/01-basic-componentversion-creation.go
index 6a5fa0142d..08785c7097 100644
--- a/examples/lib/tour/02-composing-a-component-version/example-a.go
+++ b/examples/lib/tour/02-composing-a-component-version/01-basic-componentversion-creation.go
@@ -279,6 +279,7 @@ func listVersions(repo ocm.Repository, list ...string) error {
}
return nil
}
+
func ComposingAComponentVersionA() error {
// yes, we need an OCM context, again
ctx := ocm.DefaultContext()
diff --git a/examples/lib/tour/02-composing-a-component-version/example-b.go b/examples/lib/tour/02-composing-a-component-version/02-composition-version.go
similarity index 100%
rename from examples/lib/tour/02-composing-a-component-version/example-b.go
rename to examples/lib/tour/02-composing-a-component-version/02-composition-version.go
diff --git a/examples/lib/tour/02-composing-a-component-version/README.md b/examples/lib/tour/02-composing-a-component-version/README.md
new file mode 100644
index 0000000000..6a7ac375cf
--- /dev/null
+++ b/examples/lib/tour/02-composing-a-component-version/README.md
@@ -0,0 +1,10 @@
+# Composing a Component Version
+
+This tor illustrates the basic usage of the API to
+create/compose component versions.
+
+It covers two basic scenarios:
+- [`basic`](01-basic-componentversion-creation.go) Create a component version stored in the filesystem
+- [`compose`](02-composition-version.go) Create a component version stored in memory using a non-persistent composition version.
+
+You can just call the main program with the scenario as argument.
diff --git a/examples/lib/tour/03-working-with-credentials/example-a.go b/examples/lib/tour/03-working-with-credentials/01-using-credentials.go
similarity index 100%
rename from examples/lib/tour/03-working-with-credentials/example-a.go
rename to examples/lib/tour/03-working-with-credentials/01-using-credentials.go
diff --git a/examples/lib/tour/03-working-with-credentials/example-b.go b/examples/lib/tour/03-working-with-credentials/02-basic-credential-management.go
similarity index 95%
rename from examples/lib/tour/03-working-with-credentials/example-b.go
rename to examples/lib/tour/03-working-with-credentials/02-basic-credential-management.go
index 96efde1dd3..4cd8527215 100644
--- a/examples/lib/tour/03-working-with-credentials/example-b.go
+++ b/examples/lib/tour/03-working-with-credentials/02-basic-credential-management.go
@@ -10,7 +10,6 @@ import (
"os"
"github.com/open-component-model/ocm/examples/lib/helper"
- "github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/credentials"
ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity"
"github.com/open-component-model/ocm/pkg/contexts/oci"
@@ -19,19 +18,6 @@ import (
"github.com/open-component-model/ocm/pkg/errors"
)
-func obfuscate(props common.Properties) string {
- if pw, ok := props[credentials.ATTR_PASSWORD]; ok {
- if len(pw) > 5 {
- pw = pw[:5] + "***"
- } else {
- pw = "***"
- }
- props = props.Copy()
- props[credentials.ATTR_PASSWORD] = pw
- }
- return props.String()
-}
-
func UsingCredentialsB(cfg *helper.Config, create bool) error {
ctx := ocm.DefaultContext()
@@ -137,6 +123,11 @@ func UsingCredentialsB(cfg *helper.Config, create bool) error {
if err != nil {
return errors.Wrapf(err, "no credentials")
}
+ // an error is only provided if something went wrong while determining
+ // the credentials. Delivering NO credentials is a valid result.
+ if creds == nil {
+ return fmt.Errorf("no credentials found")
+ }
fmt.Printf("credentials: %s\n", obfuscate(creds.Properties()))
// Now we can continue with our basic component version composition
diff --git a/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go b/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go
new file mode 100644
index 0000000000..26a1c3a66e
--- /dev/null
+++ b/examples/lib/tour/03-working-with-credentials/03-credential-repositories.go
@@ -0,0 +1,88 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/open-component-model/ocm/examples/lib/helper"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/dockerconfig"
+ "github.com/open-component-model/ocm/pkg/contexts/oci"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm"
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+func UsingCredentialsRepositories(cfg *helper.Config) error {
+ ctx := ocm.DefaultContext()
+ credctx := ctx.CredentialsContext()
+
+ // The OCM toolset embraces multiple storage
+ // backend technologies, for OCM meta data as well
+ // as for artifacts described by a component version.
+ // All those technologies typically have their own
+ // way to configure credentials for command line
+ // tools or servers.
+ //
+ // The credential management provides so-called
+ // credential repositories. Such a repository
+ // is able to provide any number of names
+ // credential sets. This way any special
+ // credential store can be connected to the
+ // OCM credential management jsu by providing
+ // an own implementation for the repository interface.
+
+ // One such case is the docker config json, a config
+ // file used by docker login
to store
+ // credentials for dedicatd OCI regsitries.
+ dspec := dockerconfig.NewRepositorySpec("~/.docker/config.json")
+
+ // There are general credential stores, like a HashiCorp Vault
+ // or type-specific ones, like the docker config json
+ // used to configure credentials for the docker client.
+ // (working with OCI registries).
+ // Those specialized repository implementation are not only able to
+ // provide credential sets, they also know about the usage context.
+ // Such repository implementations are able to provide credential
+ // mappings for consumer ids, also.
+
+ // The docker config is such a case, so we can instruct the
+ // repository to automatically propagate appropriate the consumer id
+ // mappings.
+ dspec = dspec.WithConsumerPropagation(true)
+
+ // now we can jsut add the repository for this specification to
+ // the credential context.
+ _, err := credctx.RepositoryForSpec(dspec)
+ if err != nil {
+ return errors.Wrapf(err, "invalid credential repository")
+ }
+ // we are not interested in the repository object, so we just ignore
+ // the result.
+
+ // so, if you have done the appropriate docker login for your
+ // OCI registry, it should be possible now to get the credentials
+ // for the configured repository.
+ id, err := oci.GetConsumerIdForRef(cfg.Repository)
+ if err != nil {
+ return errors.Wrapf(err, "invalid consumer")
+ }
+
+ // the returned credentials are provided via an interface, which might change its
+ // content, if the underlying credential source changes.
+ creds, err := credentials.CredentialsForConsumer(credctx, id, ociidentity.IdentityMatcher)
+ if err != nil {
+ return errors.Wrapf(err, "no credentials")
+ }
+ // an error is only provided if something went wrong while determining
+ // the credentials. Delivering NO credentials is a valid result.
+ if creds == nil {
+ return fmt.Errorf("no credentials found")
+ }
+ fmt.Printf("credentials: %s\n", obfuscate(creds.Properties()))
+
+ return nil
+}
diff --git a/examples/lib/tour/03-working-with-credentials/README.md b/examples/lib/tour/03-working-with-credentials/README.md
new file mode 100644
index 0000000000..56407a1b75
--- /dev/null
+++ b/examples/lib/tour/03-working-with-credentials/README.md
@@ -0,0 +1,21 @@
+# Working with Credentials
+
+This tour illustrates the basic handling of credentials
+using the OCM library. The library provides
+an extensible framework to bring together credential providers
+and credential cosunmers in a technology-agnostic way.
+
+It covers four basic scenarios:
+- [`basic`](01-using-credentials.go) Writing to a repository with directly specified credentials.
+- [`generic`](02-basic-credential-management.go) Using credentials via the credential management.
+- [`read`](02-basic-credential-management.go) Read the previously component version using the credential management.
+- [`credrepo`](03-credential-repositories.go) Providing credentials via credential repositories.
+
+You can just call the main program with some config file option (`--config `) and the name of the scenario.
+The config file should have the following content:
+
+```yaml
+repository: ghcr.io/mandelsoft/ocm
+username:
+password:
+```
\ No newline at end of file
diff --git a/examples/lib/tour/03-working-with-credentials/common.go b/examples/lib/tour/03-working-with-credentials/common.go
index d5efc5f78c..4846450c8c 100644
--- a/examples/lib/tour/03-working-with-credentials/common.go
+++ b/examples/lib/tour/03-working-with-credentials/common.go
@@ -10,6 +10,7 @@ import (
"github.com/open-component-model/ocm/pkg/blobaccess"
"github.com/open-component-model/ocm/pkg/common"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartifact"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
@@ -278,3 +279,16 @@ func listVersions(repo ocm.Repository, list ...string) error {
}
return nil
}
+
+func obfuscate(props common.Properties) string {
+ if pw, ok := props[credentials.ATTR_PASSWORD]; ok {
+ if len(pw) > 5 {
+ pw = pw[:5] + "***"
+ } else {
+ pw = "***"
+ }
+ props = props.Copy()
+ props[credentials.ATTR_PASSWORD] = pw
+ }
+ return props.String()
+}
diff --git a/examples/lib/tour/03-working-with-credentials/main.go b/examples/lib/tour/03-working-with-credentials/main.go
index 53b9465fbf..7abd06c7e0 100644
--- a/examples/lib/tour/03-working-with-credentials/main.go
+++ b/examples/lib/tour/03-working-with-credentials/main.go
@@ -52,6 +52,8 @@ func main() {
err = UsingCredentialsB(cfg, true)
case "read":
err = UsingCredentialsB(cfg, false)
+ case "credrepo":
+ err = UsingCredentialsRepositories(cfg)
default:
err = fmt.Errorf("unknown example %q", cmd)
}
diff --git a/examples/lib/tour/04-working-with-config/01-basic-config-management.go b/examples/lib/tour/04-working-with-config/01-basic-config-management.go
new file mode 100644
index 0000000000..bea3a74d7c
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/01-basic-config-management.go
@@ -0,0 +1,116 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/go-test/deep"
+ "github.com/open-component-model/ocm/examples/lib/helper"
+ "github.com/open-component-model/ocm/pkg/contexts/config"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ credcfg "github.com/open-component-model/ocm/pkg/contexts/credentials/config"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/directcreds"
+ "github.com/open-component-model/ocm/pkg/contexts/oci"
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+func BasicConfigurationHandling(cfg *helper.Config) error {
+ // configuration is handled by the configuration context.
+ ctx := config.DefaultContext()
+
+ // the configuration context handles configuration objects.
+ // a configuration object is any object implementing
+ // the config.Config interface.
+
+ // one such object is the configuration object for
+ // credentials.
+
+ creds := credcfg.New()
+
+ // here we can configure credential settings:
+ // credential repositories and consumer is mappings.
+ id, err := oci.GetConsumerIdForRef(cfg.Repository)
+ if err != nil {
+ return errors.Wrapf(err, "invalid consumer")
+ }
+ creds.AddConsumer(
+ id,
+ directcreds.NewRepositorySpec(cfg.GetCredentials().Properties()),
+ )
+
+ // credential objects are typically serializable and deserializable.
+
+ spec, err := json.MarshalIndent(creds, " ", " ")
+ if err != nil {
+ return errors.Wrapf(err, "marshal credential config")
+ }
+
+ fmt.Printf("this a a credential configuration object:\n%s\n", string(spec))
+
+ // like all the other maifest based description this format always includes
+ // a type field, which can be used to deserialize a specification into
+ // the appropriate object.
+ // This can ebe done by the config context. It accepts YAML or JSON.
+
+ o, err := ctx.GetConfigForData(spec, nil)
+ if err != nil {
+ return errors.Wrapf(err, "deserialize config")
+ }
+
+ if diff := deep.Equal(o, creds); len(diff) != 0 {
+ fmt.Printf("diff:\n%v\n", diff)
+ return fmt.Errorf("invalid des/erialization")
+ }
+
+ // regardless what variant is used (direct object or descriptor)
+ // the config object can be added to a config context.
+ err = ctx.ApplyConfig(creds, "explicit cred setting")
+ if err != nil {
+ return errors.Wrapf(err, "cannot apply config")
+ }
+
+ // Every config object implements the
+ // ApplyTo(ctx config.Context, target interface{}) error method.
+ // It takes an object, which wants to be configured.
+ // The config object then decides, whether it provides
+ // settings for the given object and calls the appropriate
+ // methods on this object (after a type cast).
+ //
+ // This way the config mechanism reverts the configuration
+ // request, it does not actively configure something, instead
+ // an object, which wants to be configured calls the config
+ // context to apply pending configs.
+ // The config context manages a queue of config objects
+ // and applys them to an object to be configured.
+
+ // If ask he credential context now for credentials,
+ // it asks the config context for pending config objects
+ // and apply them.
+ // Theregore, we now should the configured creentials, here.
+
+ credctx := credentials.DefaultContext()
+
+ found, err := credentials.CredentialsForConsumer(credctx, id)
+ if err != nil {
+ return errors.Wrapf(err, "cannot get credentials")
+ }
+ // an error is only provided if something went wrong while determining
+ // the credentials. Delivering NO credentials is a valid result.
+ if found == nil {
+ return fmt.Errorf("no credentials found")
+ }
+ fmt.Printf("consumer id: %s\n", id)
+ fmt.Printf("credentials: %s\n", obfuscate(found))
+
+ if found.GetProperty(credentials.ATTR_USERNAME) != cfg.Username {
+ return fmt.Errorf("password mismatch")
+ }
+ if found.GetProperty(credentials.ATTR_PASSWORD) != cfg.Password {
+ return fmt.Errorf("password mismatch")
+ }
+ return nil
+}
diff --git a/examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go b/examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go
new file mode 100644
index 0000000000..e130d31f5b
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/02-handle-arbitrary-config.go
@@ -0,0 +1,91 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/open-component-model/ocm/examples/lib/helper"
+ "github.com/open-component-model/ocm/pkg/contexts/config"
+ configcfg "github.com/open-component-model/ocm/pkg/contexts/config/config"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ credcfg "github.com/open-component-model/ocm/pkg/contexts/credentials/config"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/directcreds"
+ "github.com/open-component-model/ocm/pkg/contexts/oci"
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+func credConfig(cfg *helper.Config) (config.Config, error) {
+ creds := credcfg.New()
+
+ // here we can configure credential settings:
+ // credential repositories and consumer is mappings.
+ id, err := oci.GetConsumerIdForRef(cfg.Repository)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid consumer")
+ }
+ creds.AddConsumer(
+ id,
+ directcreds.NewRepositorySpec(cfg.GetCredentials().Properties()),
+ )
+ return creds, nil
+}
+
+func HandleArbitraryConfiguration(cfg *helper.Config) error {
+ // The configuration management provides a configuration object
+ // for it own.
+
+ generic := configcfg.New()
+
+ // the generic config holds a list of config objects,
+ // or their specification formats.
+ // Additionally, it is possible to configure names sets
+ // of configurations, which can later be enabled
+ // on-demand at the config context.
+
+ // we recycle our credential config from the last example.
+ creds, err := credConfig(cfg)
+ if err != nil {
+ return err
+ }
+ err = generic.AddConfig(creds)
+ if err != nil {
+ return errors.Wrapf(err, "adding config")
+ }
+
+ // credential objects are typically serializable and deserializable.
+ // this also holds for the generic config object of the config context.
+
+ spec, err := json.MarshalIndent(generic, " ", " ")
+ if err != nil {
+ return errors.Wrapf(err, "marshal credential config")
+ }
+
+ // the result is a config object hosting a list (with 1 entry)
+ // of other config object specifications.
+ fmt.Printf("this a a generic configuration object:\n%s\n", string(spec))
+
+ // the generic config object can be added to a config context, again.
+ ctx := config.DefaultContext()
+ err = ctx.ApplyConfig(creds, "generic setting")
+ if err != nil {
+ return errors.Wrapf(err, "cannot apply config")
+ }
+ credctx := credentials.DefaultContext()
+
+ // query now works, also.
+ id, err := oci.GetConsumerIdForRef(cfg.Repository)
+ if err != nil {
+ return errors.Wrapf(err, "invalid consumer")
+ }
+ found, err := credentials.CredentialsForConsumer(credctx, id)
+ if err != nil {
+ return errors.Wrapf(err, "cannot get credentials")
+ }
+ fmt.Printf("consumer id: %s\n", id)
+ fmt.Printf("credentials: %s\n", obfuscate(found))
+ return nil
+}
diff --git a/examples/lib/tour/04-working-with-config/03-using-ocm-config.go b/examples/lib/tour/04-working-with-config/03-using-ocm-config.go
new file mode 100644
index 0000000000..f6e4872ee1
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/03-using-ocm-config.go
@@ -0,0 +1,111 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/open-component-model/ocm/examples/lib/helper"
+ configcfg "github.com/open-component-model/ocm/pkg/contexts/config/config"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ credcfg "github.com/open-component-model/ocm/pkg/contexts/credentials/config"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials/repositories/dockerconfig"
+ "github.com/open-component-model/ocm/pkg/contexts/oci"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/utils"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "sigs.k8s.io/yaml"
+)
+
+func HandleOCMConfig(cfg *helper.Config) error {
+
+ // Although the configuration of an OCM context can
+ // be done by a sequence of explicit calls the mechanism
+ // shown in the example before, is used to provide a simple
+ // library function, which can be used to configure an OCM
+ // context and all related other contexts with a single call
+ // based on a central configuration file (~/.ocmconfig)
+ ctx := ocm.DefaultContext()
+ _, err := utils.Configure(ctx, "")
+ if err != nil {
+ return errors.Wrapf(err, "configuration")
+ }
+
+ // It is typically such a generic configuration specification,
+ // enriched with specialized config specifications for
+ // credentials, default repositories signing keys and any
+ // other configuration specification.
+ // Most important are here the credentials.
+ // Because OCM embraces lots of storage technologies
+ // for artifact storage as well as storing OCM meta data,
+ // tzere are typically multiple technology specific ways
+ // to configure credentials for command line tools.
+ // Using the credentials settings shown in the previous examples,
+ // it ius possible to specify credentials for all
+ // required purposes, but the configuration mangement provides
+ // an extensible way to embed native technology specific ways
+ // to provide credentials just by adding an appropriate type
+ // of config objects, which reads the specialized stoarge and
+ // feeds it into the credential context.
+ //
+ // One such config object type is the docker config type. It
+ // reads a dockerconfig.json file and fed in the credentials.
+ // because it is sed for a dedicated purpose (credentials for
+ // OCI registries), it not only can feed the credentials, but
+ // also their mapping to consumer ids.
+
+ // create the specification for a new credential repository of
+ // type dockerconfig.
+ credspec := dockerconfig.NewRepositorySpec("~/.docker/config.json", true)
+
+ // add this repository specification to a credential configuration.
+ ccfg := credcfg.New()
+ err = ccfg.AddRepository(credspec)
+ if err != nil {
+ return errors.Wrapf(err, "invalid credential config")
+ }
+
+ // By adding the default location for the standard docker config
+ // file, all credentials provided by the docker login
+ // are available in the OCM toolset, also.
+
+ // A typical minimal .ocmconfig
file can be composed as follows.
+
+ ocmcfg := configcfg.New()
+ err = ocmcfg.AddConfig(ccfg)
+
+ spec, err := yaml.Marshal(ocmcfg)
+ if err != nil {
+ return errors.Wrapf(err, "marshal ocm config")
+ }
+
+ // the result is a typical minimal ocm configuration file
+ // just providing the credentials configured with
+ // doicker login
.
+ fmt.Printf("this a typical ocm config file:\n%s\n", string(spec))
+
+ // Besides from a file, such a config can be provided as data, also,
+ // taken from any other source, for example from a Kubernetes secret
+
+ err = utils.ConfigureByData(ctx, spec, "from data")
+ if err != nil {
+ return errors.Wrapf(err, "configuration")
+ }
+
+ // If you have provided your OCI credentials with
+ // docker login, they should now be available.
+
+ id, err := oci.GetConsumerIdForRef(cfg.Repository)
+ if err != nil {
+ return errors.Wrapf(err, "invalid consumer")
+ }
+ found, err := credentials.CredentialsForConsumer(ctx, id)
+ if err != nil {
+ return errors.Wrapf(err, "cannot get credentials")
+ }
+ fmt.Printf("consumer id: %s\n", id)
+ fmt.Printf("credentials: %s\n", obfuscate(found))
+ return nil
+}
diff --git a/examples/lib/tour/04-working-with-config/04-write-config-type.go b/examples/lib/tour/04-working-with-config/04-write-config-type.go
new file mode 100644
index 0000000000..931dab7e75
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/04-write-config-type.go
@@ -0,0 +1,173 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/open-component-model/ocm/examples/lib/helper"
+ configcfg "github.com/open-component-model/ocm/pkg/contexts/config/config"
+ "github.com/open-component-model/ocm/pkg/contexts/config/cpi"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity"
+ "github.com/open-component-model/ocm/pkg/contexts/oci"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/runtime"
+ "sigs.k8s.io/yaml"
+)
+
+// TYPE is the name of our new configuration object type.
+// To be globally unique, it should always end with a
+// DNS domain owned by the provider of the new type.
+const TYPE = "example.config.acme.org"
+
+// ExampleConfigSpec is a new type of config specification
+// covering our example configuration.
+type ExampleConfigSpec struct {
+ // ObjectVersionedType is the base type providing the type feature
+ // form config specifications.
+ runtime.ObjectVersionedType `json:",inline"`
+ // Config is our example config representation.
+ helper.Config `json:",inline"`
+}
+
+// NewConfig provides a config object for out helper configuration.
+func NewConfig(cfg *helper.Config) cpi.Config {
+ return &ExampleConfigSpec{
+ ObjectVersionedType: runtime.NewVersionedTypedObject(TYPE),
+ Config: *cfg,
+ }
+}
+
+// RepositoryTarget consumes a repository name.
+type RepositoryTarget interface {
+ SetRepository(r string)
+}
+
+// ApplyTo is used to apply the provided configuration settings
+// to a dedicated object, which wants to be configured.
+func (c *ExampleConfigSpec) ApplyTo(_ cpi.Context, tgt interface{}) error {
+
+ switch t := tgt.(type) {
+ // if the target is a credentials context
+ // configure the credentials to be used for the
+ // described OCI repository.
+ case credentials.Context:
+ // determine the consumer id for our target repository-
+ id, err := oci.GetConsumerIdForRef(c.Repository)
+ if err != nil {
+ return errors.Wrapf(err, "invalid consumer")
+ }
+ // create the credentials.
+ creds := c.GetCredentials()
+
+ // configure the targeted credential context with
+ // the provided credentials (see previous examples).
+ t.SetCredentialsForConsumer(id, creds)
+
+ // if the target consumes an OCI repository, propagate
+ // the provided OCI repository ref.
+ case RepositoryTarget:
+ t.SetRepository(c.Repository)
+
+ // all other targets are ignored, we don't have
+ // something to set at these objects.
+ default:
+ return cpi.ErrNoContext(TYPE)
+ }
+ return nil
+}
+
+func init() {
+ // register the new config type, so that is can be used
+ // by the config management to deserialize appropriately
+ // typed specifications.
+ cpi.RegisterConfigType(cpi.NewConfigType[*ExampleConfigSpec](TYPE, "this ia config object type based on the example config data."))
+}
+
+func WriteConfigType(cfg *helper.Config) error {
+
+ // after preparing aout new special config type
+ // we can feed it into the config management.
+
+ credctx := credentials.DefaultContext()
+
+ // the credential context is based on a config context
+ // used to configure it.
+ ctx := credctx.ConfigContext()
+
+ // create our new config based on the actual settings
+ // and apply it to the config context.
+ examplecfg := NewConfig(cfg)
+ ctx.ApplyConfig(examplecfg, "special acme config")
+ // If you omit the above call, no credentials
+ // will be found later.
+ // _, _ = ctx, examplecfg
+
+ // now we should be prepared to get the credentials
+ id, err := oci.GetConsumerIdForRef(cfg.Repository)
+ if err != nil {
+ return errors.Wrapf(err, "cannot get consumer id")
+ }
+ fmt.Printf("usage context: %s\n", id)
+
+ // the returned credentials are provided via an interface, which might change its
+ // content, if the underlying credential source changes.
+ creds, err := credentials.CredentialsForConsumer(credctx, id, ociidentity.IdentityMatcher)
+ if err != nil {
+ return errors.Wrapf(err, "credentials")
+ }
+ fmt.Printf("credentials: %s\n", obfuscate(creds))
+
+ // Because of the new credential type, such a specification can
+ // now be added to the ocm config, also.
+ // So, we could use our special tour config file content
+ // directly as part of the ocm config.
+
+ ocmcfg := configcfg.New()
+ err = ocmcfg.AddConfig(examplecfg)
+
+ spec, err := yaml.Marshal(ocmcfg)
+ if err != nil {
+ return errors.Wrapf(err, "marshal ocm config")
+ }
+
+ // the result is a minimal ocm configuration file
+ // just providing our new example configuration.
+ fmt.Printf("this a typical ocm config file:\n%s\n", string(spec))
+
+ // above, we added a new kind of target, the RepositoryTarget interface.
+ // Just by providing an implementation for this interface, we can
+ // configure such an object using the config management.
+ target := &SimpleRepositoryTarget{}
+
+ _, err = ctx.ApplyTo(0, target)
+ if err != nil {
+ return errors.Wrapf(err, "applying to new target")
+ }
+ fmt.Printf("repository for target: %s\n", target.repository)
+
+ // This way any specialized configuration object can be added
+ // by a user of the OCM library. It can be used to configure
+ // existing objects or even new object types, even in combination.
+ //
+ // What is still required is a way
+ // to implement new config targets, objects, which want
+ // to be configured and which autoconfigure themselves when
+ // used. Our simple repository target is just an example
+ // for some kind of ad-hoc configuration.
+ // This is shown in the next example.
+ return nil
+}
+
+type SimpleRepositoryTarget struct {
+ repository string
+}
+
+var _ RepositoryTarget = (*SimpleRepositoryTarget)(nil)
+
+func (t *SimpleRepositoryTarget) SetRepository(repo string) {
+ t.repository = repo
+}
diff --git a/examples/lib/tour/04-working-with-config/05-write-config-consumer.go b/examples/lib/tour/04-working-with-config/05-write-config-consumer.go
new file mode 100644
index 0000000000..af95a8d29f
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/05-write-config-consumer.go
@@ -0,0 +1,125 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/open-component-model/ocm/examples/lib/helper"
+ "github.com/open-component-model/ocm/pkg/contexts/config/cpi"
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ ociidentity "github.com/open-component-model/ocm/pkg/contexts/credentials/builtin/oci/identity"
+ "github.com/open-component-model/ocm/pkg/contexts/oci"
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+// we already have our new acme.org config object type,
+// now we want to provide an object, which configures
+// itself when used.
+
+// RepositoryProvider should be an object, which is
+// able to provide an OCI repository reference.
+// It has a setter and a getter (the setter is
+// provided by our ad-hoc SimpleRepositoryTarget).
+type RepositoryProvider struct {
+ lock sync.Mutex
+ // updater is a utility, which ia able to
+ // configure an object basesd a a managed configuration
+ // watermark. It remembers which config objects from the
+ // config queue are already applies, and replays
+ // the config objects applied to the config context
+ // after the last update.
+ updater cpi.Updater
+ SimpleRepositoryTarget
+}
+
+func NewRepositoryProvider(ctx cpi.ContextProvider) *RepositoryProvider {
+ p := &RepositoryProvider{}
+ // To do its work, the updater needs a connection to
+ // the config context to use and the object, which should be
+ // configured.
+ p.updater = cpi.NewUpdater(ctx.ConfigContext(), p)
+ return p
+}
+
+// GetRepository returns a repository ref.
+func (p *RepositoryProvider) GetRepository() (string, error) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ // the first step for methods of configurable objects
+ // dependent on potential configuration is always
+ // to update itself using the embedded updater.
+ // Please remember, the config management reverses the
+ // request direction. Applying a config object to
+ // the config context does not configure dependent objects,
+ // it just manages a config queue, which is used by potential
+ // configuration targets to configure themselves.
+ // The reason for this is to avoid references from the
+ // management to managed objects. This would prohibit
+ // the garbage collection of all configurable objects.
+ err := p.updater.Update()
+ if err != nil {
+ return "", err
+ }
+ // now, we can do our regular function, aka
+ // providing a repository ref.
+ return p.repository, nil
+}
+
+func WriteConfigTargets(cfg *helper.Config) error {
+ credctx := credentials.DefaultContext()
+
+ // after defining or repository provider type
+ // we can now use it.
+ prov := NewRepositoryProvider(credctx)
+
+ // If we ask now for a repository we will get the empty
+ // answer.
+ repo, err := prov.GetRepository()
+ if err != nil {
+ errors.Wrapf(err, "get repo")
+ }
+ if repo != "" {
+ return fmt.Errorf("Oops, found repository %q", repo)
+ }
+
+ // Now, we apply our config from the last example.
+ ctx := credctx.ConfigContext()
+ examplecfg := NewConfig(cfg)
+ err = ctx.ApplyConfig(examplecfg, "special acme config")
+ if err != nil {
+ errors.Wrapf(err, "apply config")
+ }
+
+ // asking for a repository now will return the configured
+ // ref.
+ repo, err = prov.GetRepository()
+ if err != nil {
+ errors.Wrapf(err, "get repo")
+ }
+ if repo == "" {
+ return fmt.Errorf("no repository provided")
+ }
+ fmt.Printf("using repository: %s\n", repo)
+
+ // now, we should also be prepared to get the credentials,
+ // our config object configures the provider as well as
+ // the credential context.
+ id, err := oci.GetConsumerIdForRef(repo)
+ if err != nil {
+ return errors.Wrapf(err, "cannot get consumer id")
+ }
+ fmt.Printf("usage context: %s\n", id)
+
+ creds, err := credentials.CredentialsForConsumer(credctx, id, ociidentity.IdentityMatcher)
+ if err != nil {
+ return errors.Wrapf(err, "credentials")
+ }
+ fmt.Printf("credentials: %s\n", obfuscate(creds))
+
+ return nil
+}
diff --git a/examples/lib/tour/04-working-with-config/README.md b/examples/lib/tour/04-working-with-config/README.md
new file mode 100644
index 0000000000..48397d7179
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/README.md
@@ -0,0 +1,23 @@
+# Working with Configurations
+
+This tour illustrates the basic configuration management
+included in the OCM library. The library provides
+an extensible framework to bring together configuration settings
+and configurable objects.
+
+It covers five basic scenarios:
+- [`basic`](01-basic-config-management.go) Basic configuration management illustarting the configuration of credentials.
+- [`generic`](02-handle-arbitrary-config.go) Handling of arbitrary configuration.
+- [`ocm`](03-using-ocm-config.go) Central configuration
+- [`provide`](04-write-config-type.go) Providing new config object types
+- [`consume`](05-write-config-consumer.go) Preparing objects to be configured by the config management
+
+
+You can just call the main program with some config file option (`--config `) and the name of the scenario.
+The config file should have the following content:
+
+```yaml
+repository: ghcr.io/mandelsoft/ocm
+username:
+password:
+```
\ No newline at end of file
diff --git a/examples/lib/tour/04-working-with-config/common.go b/examples/lib/tour/04-working-with-config/common.go
new file mode 100644
index 0000000000..4957898867
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/common.go
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+)
+
+func obfuscate(creds credentials.Credentials) string {
+ if creds == nil {
+ return "no credentials"
+ }
+ props := creds.Properties()
+ if pw, ok := props[credentials.ATTR_PASSWORD]; ok {
+ if len(pw) > 5 {
+ pw = pw[:5] + "***"
+ } else {
+ pw = "***"
+ }
+ props = props.Copy()
+ props[credentials.ATTR_PASSWORD] = pw
+ }
+ return props.String()
+}
diff --git a/examples/lib/tour/04-working-with-config/main.go b/examples/lib/tour/04-working-with-config/main.go
new file mode 100644
index 0000000000..e25482bcd5
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/main.go
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/open-component-model/ocm/examples/lib/helper"
+)
+
+// CFG is the path to the file containing the credentials
+var CFG = "../examples/lib/cred.yaml"
+
+var current_version string
+
+func init() {
+ data, err := os.ReadFile("VERSION")
+ if err != nil {
+ panic("VERSION not found")
+ }
+ current_version = strings.TrimSpace(string(data))
+}
+
+func main() {
+ arg := 1
+ if len(os.Args) > 1 {
+ if os.Args[1] == "--config" {
+ if len(os.Args) > 2 {
+ CFG = os.Args[2]
+ arg = 3
+ } else {
+ fmt.Fprintf(os.Stderr, "error: config file missing\n")
+ os.Exit(1)
+ }
+ }
+ }
+ cfg, err := helper.ReadConfig(CFG)
+ if err == nil {
+ cmd := "basic"
+
+ if len(os.Args) > arg {
+ cmd = os.Args[arg]
+ }
+ switch cmd {
+ case "basic":
+ err = BasicConfigurationHandling(cfg)
+ case "generic":
+ err = HandleArbitraryConfiguration(cfg)
+ case "ocm":
+ err = HandleOCMConfig(cfg)
+ case "provide":
+ err = WriteConfigType(cfg)
+ case "consume":
+ err = WriteConfigTargets(cfg)
+ default:
+ err = fmt.Errorf("unknown example %q", cmd)
+ }
+ }
+
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %s\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/lib/tour/04-working-with-config/resources/ocmconfig b/examples/lib/tour/04-working-with-config/resources/ocmconfig
new file mode 100644
index 0000000000..6a663d533c
--- /dev/null
+++ b/examples/lib/tour/04-working-with-config/resources/ocmconfig
@@ -0,0 +1,8 @@
+type: generic.config.ocm.software
+configurations:
+- type: credentials.config.ocm.software
+ repositories:
+ - repository:
+ type: DockerConfig
+ dockerConfigFile: ~/.docker/config.json
+ propagateConsumerIdentity: true
diff --git a/examples/lib/tour/05-transporting-component-versions/README.md b/examples/lib/tour/05-transporting-component-versions/README.md
new file mode 100644
index 0000000000..a5b494a501
--- /dev/null
+++ b/examples/lib/tour/05-transporting-component-versions/README.md
@@ -0,0 +1,38 @@
+# Transporting Component Versions
+
+This [tour](example.go) illustrates the basic support for
+transporting content from one environment into another.
+
+
+You can just call the main program with some config file option (`--config `).
+The config file should have the following content:
+
+```yaml
+repository: ghcr.io/mandelsoft/ocm
+targetRepository:
+ type: CommonTransportFormat
+ filePath: /tmp/example05.target.ctf
+ fileFormat: directory
+ accessMode: 2
+username:
+password:
+```
+
+Any supported kind of target repository can be specified by using its
+specification type. An OCI regisztry target would look like this:
+
+```yaml
+repository: ghcr.io/mandelsoft/ocm
+username:
+password:
+targetRepository:
+ type: OCIRegistry
+ baseUrl: ghcr.io/mandelsoft/targetocm
+ocmConfig:
+```
+
+The actual version of the example just works with the filesystem
+target, because it is not possible to specify credentials for the
+target repository in this simple config file. But, if you specific an [OCM config file](../04-working-with-config/README.md) you can
+add more credential settings to make target repositories possible
+requiring credentials.
\ No newline at end of file
diff --git a/examples/lib/tour/05-transporting-component-versions/example.go b/examples/lib/tour/05-transporting-component-versions/example.go
index 9ba4943cf1..e22b66d08e 100644
--- a/examples/lib/tour/05-transporting-component-versions/example.go
+++ b/examples/lib/tour/05-transporting-component-versions/example.go
@@ -14,12 +14,32 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ocireg"
"github.com/open-component-model/ocm/pkg/contexts/ocm/transfer"
"github.com/open-component-model/ocm/pkg/contexts/ocm/transfer/transferhandler/standard"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/utils"
"github.com/open-component-model/ocm/pkg/errors"
)
+func ReadConfiguration(ctx ocm.Context, cfg *helper.Config) error {
+ if cfg.OCMConfig != "" {
+ fmt.Printf("*** applying config from %s\n", cfg.OCMConfig)
+
+ _, err := utils.Configure(ctx, cfg.OCMConfig)
+ if err != nil {
+ return errors.Wrapf(err, "error in ocm config %s", cfg.OCMConfig)
+ }
+ }
+ return nil
+}
+
func TransportingComponentVersions(cfg *helper.Config) error {
ctx := ocm.DefaultContext()
+ // Configure context with optional ocm config.
+ // See OCM config scenario in tour 04.
+ err := ReadConfiguration(ctx, cfg)
+ if err != nil {
+ return err
+ }
+
// the context acts as factory for various model types based on
// specification descriptor serialization formats in YAML or JSON.
// Access method specifications and repository specification are
diff --git a/examples/lib/tour/06-signing-component-versions/example-a.go b/examples/lib/tour/06-signing-component-versions/01-basic-signing.go
similarity index 95%
rename from examples/lib/tour/06-signing-component-versions/example-a.go
rename to examples/lib/tour/06-signing-component-versions/01-basic-signing.go
index 82aaf4a5dd..0154795279 100644
--- a/examples/lib/tour/06-signing-component-versions/example-a.go
+++ b/examples/lib/tour/06-signing-component-versions/01-basic-signing.go
@@ -19,6 +19,13 @@ func SigningComponentVersions(cfg *helper.Config) error {
ctx := ocm.DefaultContext()
+ // Configure context with optional ocm config.
+ // See OCM config scenario in tour 04.
+ err := ReadConfiguration(ctx, cfg)
+ if err != nil {
+ return err
+ }
+
// siginfo := signingattr.Get(ctx)
// to sign a component version we need a private key.
diff --git a/examples/lib/tour/06-signing-component-versions/example-b.go b/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go
similarity index 93%
rename from examples/lib/tour/06-signing-component-versions/example-b.go
rename to examples/lib/tour/06-signing-component-versions/02-using-context-settings.go
index 107ec85bf1..9656119a5e 100644
--- a/examples/lib/tour/06-signing-component-versions/example-b.go
+++ b/examples/lib/tour/06-signing-component-versions/02-using-context-settings.go
@@ -49,12 +49,19 @@ func prepareComponentInRepo(ctx ocm.Context, cfg *helper.Config) error {
func SigningComponentVersionInRepo(cfg *helper.Config) error {
ctx := ocm.DefaultContext()
- err := prepareComponentInRepo(ctx, cfg)
+ // Configure context with optional ocm config.
+ // See OCM config scenario in tour 04.
+ err := ReadConfiguration(ctx, cfg)
+ if err != nil {
+ return err
+ }
+
+ err = prepareComponentInRepo(ctx, cfg)
if err != nil {
return errors.Wrapf(err, "cannot prepare component version in target repo")
}
- // evrey context features a signing registry, which provides available
+ // every context features a signing registry, which provides available
// signers and hashers, but also keys for various purposes.
// It is always asked, if a key is required for a purpose, which is
// not explicitly given to a signing/verification call.
diff --git a/examples/lib/tour/06-signing-component-versions/README.md b/examples/lib/tour/06-signing-component-versions/README.md
new file mode 100644
index 0000000000..0fa356f6ed
--- /dev/null
+++ b/examples/lib/tour/06-signing-component-versions/README.md
@@ -0,0 +1,26 @@
+# Signing Component Versions
+
+This tour illustrates the basic functionality to
+sign and verify signatures.
+
+It covers two basic scenarios:
+- [`sign`](01-basic-signing.go) Create, Sign, Transport and Verify a component version.
+- [`repo`](02-using-context-settings.go) Using context settings to configure signing and verification in target repo.
+
+You can just call the main program with some config file option (`--config `) and the name of the scenario.
+The config file should have the following content:
+
+```yaml
+targetRepository:
+ type: CommonTransportFormat
+ filePath: /tmp/example06.target.ctf
+ fileFormat: directory
+ accessMode: 2
+ocmConfig:
+```
+
+The actual version of the example just works with the filesystem
+target, because it is not possible to specify credentials for the
+target repository in this simple config file. But, if you specific an [OCM config file](../04-working-with-config/README.md) you can
+add more credential settings to make target repositories possible
+requiring credentials.
diff --git a/examples/lib/tour/06-signing-component-versions/common.go b/examples/lib/tour/06-signing-component-versions/common.go
index ee36877893..0bb09b78cf 100644
--- a/examples/lib/tour/06-signing-component-versions/common.go
+++ b/examples/lib/tour/06-signing-component-versions/common.go
@@ -8,6 +8,7 @@ import (
"fmt"
"strings"
+ "github.com/open-component-model/ocm/examples/lib/helper"
"github.com/open-component-model/ocm/pkg/blobaccess"
"github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
@@ -18,6 +19,7 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactblob/dockermultiblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactblob/textblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/resourcetypes"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/utils"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/finalizer"
"github.com/open-component-model/ocm/pkg/mime"
@@ -300,3 +302,15 @@ func listVersions(repo ocm.Repository, list ...string) error {
}
return nil
}
+
+func ReadConfiguration(ctx ocm.Context, cfg *helper.Config) error {
+ if cfg.OCMConfig != "" {
+ fmt.Printf("*** applying config from %s\n", cfg.OCMConfig)
+
+ _, err := utils.Configure(ctx, cfg.OCMConfig)
+ if err != nil {
+ return errors.Wrapf(err, "error in ocm config %s", cfg.OCMConfig)
+ }
+ }
+ return nil
+}
diff --git a/examples/lib/tour/06-signing-component-versions/main.go b/examples/lib/tour/06-signing-component-versions/main.go
index 905a6921d6..e2b91e6d83 100644
--- a/examples/lib/tour/06-signing-component-versions/main.go
+++ b/examples/lib/tour/06-signing-component-versions/main.go
@@ -46,7 +46,7 @@ func main() {
cmd = os.Args[arg]
}
switch cmd {
- case "basic":
+ case "sign":
err = SigningComponentVersions(cfg)
case "repo":
err = SigningComponentVersionInRepo(cfg)
diff --git a/examples/lib/tour/README.md b/examples/lib/tour/README.md
new file mode 100644
index 0000000000..58ac1d1da1
--- /dev/null
+++ b/examples/lib/tour/README.md
@@ -0,0 +1,15 @@
+# A Tour through Usage Scenarios of the OCM Library
+
+This tour guides you from a very basic usage of the
+OCM repository API to more complex scenarios
+handling credentials and configurations.
+
+So far, it does not cover the implementation
+of extension points of the library.
+
+- [Basic Usage of OCM Repositories](01-getting-started/README.md)
+- [Composing Component Versions](02-composing-a-component-version/README.md)
+- [Working with Credentials](03-working-with-credentials/README.md)
+- [Working with Configuration](04-working-with-config/README.md)
+- [Transporting Component Versions](05-transporting-component-versions/README.md)
+- [Signing Component Versions](06-signing-component-versions/README.md)
\ No newline at end of file
diff --git a/pkg/blobaccess/standard.go b/pkg/blobaccess/standard.go
index 857d6e5606..1283933458 100644
--- a/pkg/blobaccess/standard.go
+++ b/pkg/blobaccess/standard.go
@@ -9,6 +9,7 @@ import (
"github.com/open-component-model/ocm/pkg/blobaccess/bpi"
"github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/finalizer"
"github.com/open-component-model/ocm/pkg/mime"
"github.com/open-component-model/ocm/pkg/refmgmt"
"github.com/open-component-model/ocm/pkg/utils"
@@ -217,6 +218,7 @@ type AnnotatedBlobAccess[T DataAccess] interface {
type annotatedBlobAccessView[T DataAccess] struct {
_blobAccess
+ id finalizer.ObjectIdentity
annotation T
}
@@ -230,6 +232,7 @@ func (a *annotatedBlobAccessView[T]) Dup() (BlobAccess, error) {
return nil, err
}
return &annotatedBlobAccessView[T]{
+ id: finalizer.NewObjectIdentity(a.id.String()),
_blobAccess: b,
annotation: a.annotation,
}, nil
@@ -247,6 +250,7 @@ func ForDataAccess[T DataAccess](digest digest.Digest, size int64, mimeType stri
a := bpi.BaseAccessForDataAccessAndMeta(mimeType, access, digest, size)
return &annotatedBlobAccessView[T]{
+ id: finalizer.NewObjectIdentity("annotatedBlobAccess"),
_blobAccess: bpi.NewBlobAccessForBase(a),
annotation: access,
}
diff --git a/pkg/contexts/config/internal/config.go b/pkg/contexts/config/internal/config.go
index 9ec143cb66..b65e3bc599 100644
--- a/pkg/contexts/config/internal/config.go
+++ b/pkg/contexts/config/internal/config.go
@@ -7,6 +7,7 @@ package internal
import (
"fmt"
+ "github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/runtime"
)
@@ -37,3 +38,17 @@ func (c *ConfigurationList) AddConfig(cfg Config) error {
return nil
}
+
+func (c *ConfigurationList) AddConfigData(ctx Context, data []byte) error {
+ cfg, err := ctx.GetConfigForData(data, nil)
+ if err != nil {
+ return errors.Wrapf(err, "invalid config specification")
+ }
+ g, err := ToGenericConfig(cfg)
+ if err != nil {
+ return fmt.Errorf("unable to convert config to generic: %w", err)
+ }
+
+ c.Configurations = append(c.Configurations, g)
+ return nil
+}
diff --git a/pkg/contexts/credentials/repositories/dockerconfig/type.go b/pkg/contexts/credentials/repositories/dockerconfig/type.go
index 30acfd93c9..bba00f371b 100644
--- a/pkg/contexts/credentials/repositories/dockerconfig/type.go
+++ b/pkg/contexts/credentials/repositories/dockerconfig/type.go
@@ -41,6 +41,9 @@ func NewRepositorySpec(path string, prop ...bool) *RepositorySpec {
for _, e := range prop {
p = p || e
}
+ if path == "" {
+ path = "~/.docker/config.json"
+ }
return &RepositorySpec{
ObjectVersionedType: runtime.NewVersionedTypedObject(Type),
DockerConfigFile: path,
diff --git a/pkg/contexts/oci/cpi/support/artifact.go b/pkg/contexts/oci/cpi/support/artifact.go
index 016bfe213b..131f861600 100644
--- a/pkg/contexts/oci/cpi/support/artifact.go
+++ b/pkg/contexts/oci/cpi/support/artifact.go
@@ -38,7 +38,6 @@ func NewArtifactForBlob(container NamespaceAccessImpl, blob blobaccess.BlobAcces
if err != nil {
return nil, err
}
-
return newArtifact(container, state, closer...)
}
diff --git a/pkg/contexts/ocm/accessmethods/ociartifact/method.go b/pkg/contexts/ocm/accessmethods/ociartifact/method.go
index d669c4a034..5e937f7cf3 100644
--- a/pkg/contexts/ocm/accessmethods/ociartifact/method.go
+++ b/pkg/contexts/ocm/accessmethods/ociartifact/method.go
@@ -100,12 +100,15 @@ func (a *AccessSpec) GetReferenceHint(cv accspeccpi.ComponentVersionAccess) stri
if err != nil {
return ""
}
- prefix := ocmcpi.RepositoryPrefix(cv.Repository().GetSpecification())
hint := ref.Repository
- if strings.HasPrefix(hint, prefix+grammar.RepositorySeparator) {
- // try to keep hint identical, even across intermediate
- // artifact globalizations
- hint = hint[len(prefix)+1:]
+ r := cv.Repository()
+ if r != nil {
+ prefix := ocmcpi.RepositoryPrefix(cv.Repository().GetSpecification())
+ if strings.HasPrefix(hint, prefix+grammar.RepositorySeparator) {
+ // try to keep hint identical, even across intermediate
+ // artifact globalizations
+ hint = hint[len(prefix)+1:]
+ }
}
if ref.Tag != nil {
hint += grammar.TagSeparator + *ref.Tag
diff --git a/pkg/contexts/ocm/cpi/dummy.go b/pkg/contexts/ocm/cpi/dummy.go
index 8282a5fbce..099bf84ab4 100644
--- a/pkg/contexts/ocm/cpi/dummy.go
+++ b/pkg/contexts/ocm/cpi/dummy.go
@@ -36,39 +36,39 @@ func (d *DummyComponentVersionAccess) Dup() (ComponentVersionAccess, error) {
}
func (d *DummyComponentVersionAccess) GetProvider() *compdesc.Provider {
- panic("implement me")
+ return nil
}
func (d *DummyComponentVersionAccess) SetProvider(p *compdesc.Provider) error {
- panic("implement me")
+ return errors.ErrNotSupported()
}
func (d *DummyComponentVersionAccess) AdjustSourceAccess(meta *internal.SourceMeta, acc compdesc.AccessSpec) error {
- panic("implement me")
+ return errors.ErrNotSupported()
}
func (c *DummyComponentVersionAccess) Repository() Repository {
- panic("implement me")
+ return nil
}
func (d *DummyComponentVersionAccess) GetName() string {
- panic("implement me")
+ return ""
}
func (d *DummyComponentVersionAccess) GetVersion() string {
- panic("implement me")
+ return ""
}
func (d *DummyComponentVersionAccess) GetDescriptor() *compdesc.ComponentDescriptor {
- panic("implement me")
+ return nil
}
func (d *DummyComponentVersionAccess) GetResources() []ResourceAccess {
return nil
}
-func (d *DummyComponentVersionAccess) GetResource(meta metav1.Identity) (ResourceAccess, error) {
- return nil, errors.ErrNotFound("resource", meta.String())
+func (d *DummyComponentVersionAccess) GetResource(id metav1.Identity) (ResourceAccess, error) {
+ return nil, errors.ErrNotFound("resource", id.String())
}
func (d *DummyComponentVersionAccess) GetResourceIndex(metav1.Identity) int {
@@ -84,11 +84,11 @@ func (d *DummyComponentVersionAccess) GetResourcesByName(name string, selectors
}
func (d *DummyComponentVersionAccess) GetSources() []SourceAccess {
- panic("implement me")
+ return nil
}
-func (d *DummyComponentVersionAccess) GetSource(meta metav1.Identity) (SourceAccess, error) {
- panic("implement me")
+func (d *DummyComponentVersionAccess) GetSource(id metav1.Identity) (SourceAccess, error) {
+ return nil, errors.ErrNotFound(KIND_SOURCE, id.String())
}
func (d *DummyComponentVersionAccess) GetSourceIndex(metav1.Identity) int {
@@ -158,11 +158,11 @@ func (d *DummyComponentVersionAccess) SetSource(meta *SourceMeta, spec compdesc.
}
func (d *DummyComponentVersionAccess) SetSourceByAccess(art SourceAccess) error {
- panic("implement me")
+ return errors.ErrNotSupported()
}
func (d *DummyComponentVersionAccess) SetReference(ref *ComponentReference) error {
- panic("implement me")
+ return errors.ErrNotSupported()
}
func (d *DummyComponentVersionAccess) DiscardChanges() {
diff --git a/pkg/contexts/ocm/cpi/modopts.go b/pkg/contexts/ocm/cpi/modopts.go
index cc4141f9c4..5b5773f4df 100644
--- a/pkg/contexts/ocm/cpi/modopts.go
+++ b/pkg/contexts/ocm/cpi/modopts.go
@@ -20,10 +20,24 @@ type (
BlobUploadOption = internal.BlobUploadOption
BlobUploadOptions = internal.BlobUploadOptions
+
+ AddVersionOption = internal.AddVersionOption
+ AddVersionOptions = internal.AddVersionOptions
)
////////////////////////////////////////////////////////////////////////////////
+func NewAddVersionOptions(list ...AddVersionOption) *AddVersionOptions {
+ return internal.NewAddVersionOptions(list...)
+}
+
+// Overwrite enabled the overwrite mode for adding a component version.
+func Overwrite(flag ...bool) AddVersionOption {
+ return internal.Overwrite(flag...)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
func NewBlobModificationOptions(list ...BlobModificationOption) *BlobModificationOptions {
return internal.NewBlobModificationOptions(list...)
}
diff --git a/pkg/contexts/ocm/cpi/repocpi/README.md b/pkg/contexts/ocm/cpi/repocpi/README.md
new file mode 100644
index 0000000000..c97d5c00e9
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/README.md
@@ -0,0 +1,77 @@
+# Context Programming Interface for Repositories
+
+Package repocpi contains the implementation support
+ for repository backends. It offers three methods
+ to create component version, component and repository
+ objects based on three simple implementation interfaces.
+
+ The basic provisioning model is layered:
+
+ ![Implamentation Layers](ocmimpllayers.png)
+
+ - on layer 1 there is the *user facing API* defined
+ in package [github.com/open-component-model/ocm/pkg/contexts/ocm].
+
+ - on layer 2 (this package) there is a backend agnostic
+ implementation of standard functionality based on layer 3.
+ This is divided into two parts
+
+ a) the *view* objects provided by the `Dup()` calls of the layer 1 API.
+ All dups are internally based on a single base object.
+ These objects are called *bridge*. They act as base object
+ for the views and as abstraction for the implementation objects
+ providing *generic* implementations potentially based on
+ the implementation functionality.
+ (see bridge design pattern https://refactoring.guru/design-patterns/bridge)
+
+ b) the *bridge* object as base for all dup views is used to implement some
+ common functionality like the view management. The bridge object
+ is closed, when the last view disappears.
+ This bridge object then calls the final
+ storage backend implementation interface.
+
+ - the storage backend implementations based on the implementation
+ interfaces provided by layer 2.
+
+ The implementation interfaces and the functions to create API objects are:
+
+ - interface [ComponentVersionAccessImpl] is used to create an ocm.ComponentVersionAccess object
+ using the function [NewComponentVersionAccess].
+ - interface [ComponentAccessImpl] is used to create an ocm.ComponentAccess object
+ using the function [NewComponentAccess].
+ - interface [RepositoryImpl] is used to create an ocm.ComponentAccess object
+ using the function [NewRepository].
+
+ Component version implementations provide basic access to component versions
+ and their descriptors. They keep a reference to component implementations, which are
+ again based on repository implementations. The task of repository implementations is
+ to provide component objects. Their implementations are responsible to provide
+ component version objects.
+
+## Simplified Respository Implementation Interface
+ Besides this basic implementation interfaces with separated objects for a
+ repository, component and component version, there is support for a simplified
+ implementation interface (`StorageBackendImpl`). This is a single interface
+ bundling all required functionality to implement the objects for the three
+ concerned elements. With `NewStorageBackend` it is possible to instantiate
+ a new kind of repository based on this single interface. The required
+ objects for components and component versions are generically provided
+ based on the methods provided by this interface.
+
+## Comparison of Implementation Models
+
+The simplified implementation model does not provide access to the
+implementation objects for components and component versions.
+Therefore, it is not possible to keep state for those elements.
+
+Storage Backend Implementations requiring such state, like the OCI
+implementation based on the OCI abstraction provided by the OCI
+context, therefore use dedicated implementations for repository,
+component and component version objects. This model provides
+complete control over the lifecycle of those elements.
+
+If a storage backend implementation is stateless or just keeps
+state at the repository level, the simplified implementation model
+can be chosen.
+
+
diff --git a/pkg/contexts/ocm/cpi/repocpi/backend.go b/pkg/contexts/ocm/cpi/repocpi/backend.go
new file mode 100644
index 0000000000..cbfeabee7b
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/backend.go
@@ -0,0 +1,283 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "io"
+ "sync/atomic"
+
+ "github.com/open-component-model/ocm/pkg/common"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/utils"
+)
+
+// StorageBackendImpl is an interface which can be implemented
+// to provide a complete repository view with repository, component
+// and component version objects, which are generically implemented
+// based on the methods of this interface.
+//
+// A repository interface based on this implementation interface can be
+// created using the function NewStorageBackend.
+type StorageBackendImpl interface {
+ // repository related methods.
+
+ io.Closer
+ GetContext() cpi.Context
+ GetSpecification() cpi.RepositorySpec
+ IsReadOnly() bool
+
+ ComponentLister() cpi.ComponentLister
+ HasComponent(name string) (bool, error)
+ HasComponentVersion(key common.NameVersion) (bool, error)
+
+ // component related methods.
+
+ ListVersions(comp string) ([]string, error)
+ HasVersion(vers string) (bool, error)
+
+ // version related methods.
+
+ GetDescriptor(key common.NameVersion) (*compdesc.ComponentDescriptor, error)
+ SetDescriptor(key common.NameVersion, descriptor *compdesc.ComponentDescriptor) error
+ AccessMethod(key common.NameVersion, acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error)
+ GetInexpensiveContentVersionIdentity(key common.NameVersion, acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string
+ GetStorageContext(key common.NameVersion) cpi.StorageContext
+ GetBlob(key common.NameVersion, name string) (cpi.DataAccess, error)
+ AddBlob(key common.NameVersion, blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error)
+}
+
+type storageBackendRepository struct {
+ bridge RepositoryBridge
+ closed atomic.Bool
+ noref cpi.Repository
+ kind string
+ impl StorageBackendImpl
+}
+
+var _ RepositoryImpl = (*storageBackendRepository)(nil)
+
+// NewStorageBackend provides a complete repository view
+// with repository, component and component version objects
+// based on the implementation of the sole interface StorageBackendImpl.
+// No further implementations are required besides a dedicated
+// specification object, the dependent object
+// types are generically provided based on the methods of this
+// interface.
+// The kind parameter is used to denote the kind of repository
+// in ids and log messages.
+func NewStorageBackend(kind string, impl StorageBackendImpl) cpi.Repository {
+ backend := &storageBackendRepository{
+ impl: impl,
+ kind: kind,
+ }
+ return NewRepository(backend, kind)
+}
+
+func (s *storageBackendRepository) SetBridge(bridge RepositoryBridge) {
+ s.bridge = bridge
+ s.noref = NewNoneRefRepositoryView(bridge)
+}
+
+func (s *storageBackendRepository) Close() error {
+ if s.closed.Swap(true) {
+ return ErrClosed
+ }
+ return s.impl.Close()
+}
+
+func (s *storageBackendRepository) GetContext() cpi.Context {
+ return s.impl.GetContext()
+}
+
+func (s *storageBackendRepository) GetSpecification() cpi.RepositorySpec {
+ return s.impl.GetSpecification()
+}
+
+func (s *storageBackendRepository) ComponentLister() cpi.ComponentLister {
+ return s.impl.ComponentLister()
+}
+
+func (s *storageBackendRepository) ExistsComponentVersion(name string, version string) (bool, error) {
+ return s.impl.HasComponentVersion(common.NewNameVersion(name, version))
+}
+
+func (s *storageBackendRepository) LookupComponent(name string) (*ComponentAccessInfo, error) {
+ if ok, err := s.impl.HasComponent(name); !ok || err != nil {
+ return nil, err
+ }
+ impl := &storageBackendComponent{
+ repo: s,
+ name: name,
+ }
+ return &ComponentAccessInfo{
+ Impl: impl,
+ Kind: s.kind + " component",
+ Main: true,
+ }, nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type storageBackendComponent struct {
+ bridge ComponentAccessBridge
+ repo *storageBackendRepository
+ name string
+}
+
+var _ ComponentAccessImpl = (*storageBackendComponent)(nil)
+
+func (s *storageBackendComponent) SetBridge(bridge ComponentAccessBridge) {
+ s.bridge = bridge
+}
+
+func (s *storageBackendComponent) GetParentBridge() RepositoryViewManager {
+ return s.repo.bridge
+}
+
+func (s *storageBackendComponent) Close() error {
+ return nil
+}
+
+func (s *storageBackendComponent) GetContext() cpi.Context {
+ return s.repo.impl.GetContext()
+}
+
+func (s *storageBackendComponent) GetName() string {
+ return s.name
+}
+
+func (s *storageBackendComponent) IsReadOnly() bool {
+ return s.repo.impl.IsReadOnly()
+}
+
+func (s *storageBackendComponent) ListVersions() ([]string, error) {
+ return s.repo.impl.ListVersions(s.name)
+}
+
+func (s *storageBackendComponent) HasVersion(vers string) (bool, error) {
+ return s.repo.impl.HasVersion(s.name)
+}
+
+func (s *storageBackendComponent) LookupVersion(version string) (*ComponentVersionAccessInfo, error) {
+ if ok, err := s.repo.impl.HasComponentVersion(common.NewNameVersion(s.name, version)); !ok || err != nil {
+ return nil, err
+ }
+
+ name := common.NewNameVersion(s.name, version)
+ d, err := s.repo.impl.GetDescriptor(name)
+ if err != nil {
+ return nil, err
+ }
+
+ impl := &storageBackendComponentVersion{
+ comp: s,
+ name: name,
+ descriptor: d,
+ }
+ return &ComponentVersionAccessInfo{
+ Impl: impl,
+ Lazy: true,
+ Persistent: true,
+ }, nil
+}
+
+func (s *storageBackendComponent) NewVersion(version string, overwrite ...bool) (*ComponentVersionAccessInfo, error) {
+ ok, err := s.repo.impl.HasComponentVersion(common.NewNameVersion(s.name, version))
+ if err != nil {
+ return nil, err
+ }
+ if ok && !utils.Optional(overwrite...) {
+ return nil, errors.ErrAlreadyExists(cpi.KIND_COMPONENTVERSION, s.name+"/"+version)
+ }
+
+ name := common.NewNameVersion(s.name, version)
+ d := compdesc.New(s.name, version)
+
+ impl := &storageBackendComponentVersion{
+ comp: s,
+ name: name,
+ descriptor: d,
+ }
+ return &ComponentVersionAccessInfo{
+ Impl: impl,
+ Lazy: true,
+ Persistent: false,
+ }, nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type storageBackendComponentVersion struct {
+ bridge ComponentVersionAccessBridge
+ comp *storageBackendComponent
+ name common.NameVersion
+ descriptor *compdesc.ComponentDescriptor
+}
+
+var _ ComponentVersionAccessImpl = (*storageBackendComponentVersion)(nil)
+
+func (s *storageBackendComponentVersion) Close() error {
+ return nil
+}
+
+func (s *storageBackendComponentVersion) GetContext() cpi.Context {
+ return s.comp.repo.impl.GetContext()
+}
+
+func (s *storageBackendComponentVersion) SetBridge(bridge ComponentVersionAccessBridge) {
+ s.bridge = bridge
+}
+
+func (s *storageBackendComponentVersion) GetParentBridge() ComponentAccessBridge {
+ return s.comp.bridge
+}
+
+func (s *storageBackendComponentVersion) Repository() cpi.Repository {
+ return s.comp.repo.noref
+}
+
+func (s *storageBackendComponentVersion) IsReadOnly() bool {
+ return s.comp.repo.impl.IsReadOnly()
+}
+
+func (s *storageBackendComponentVersion) GetDescriptor() *compdesc.ComponentDescriptor {
+ d, err := s.comp.repo.impl.GetDescriptor(s.name)
+ if err != nil {
+ return nil // TODO: handler error
+ }
+ return d
+}
+
+func (s *storageBackendComponentVersion) SetDescriptor(descriptor *compdesc.ComponentDescriptor) error {
+ err := s.comp.repo.impl.SetDescriptor(s.name, descriptor)
+ if err != nil {
+ return err
+ }
+ s.descriptor = descriptor
+ return nil
+}
+
+func (s *storageBackendComponentVersion) AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) {
+ return s.comp.repo.impl.AccessMethod(s.name, acc, cv)
+}
+
+func (s *storageBackendComponentVersion) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string {
+ return s.comp.repo.impl.GetInexpensiveContentVersionIdentity(s.name, acc, cv)
+}
+
+func (s storageBackendComponentVersion) GetStorageContext() cpi.StorageContext {
+ return s.comp.repo.impl.GetStorageContext(s.name)
+}
+
+func (s storageBackendComponentVersion) GetBlob(name string) (cpi.DataAccess, error) {
+ return s.comp.repo.impl.GetBlob(s.name, name)
+}
+
+func (s storageBackendComponentVersion) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
+ return s.comp.repo.impl.AddBlob(s.name, blob, refName, global)
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/blobcache.go b/pkg/contexts/ocm/cpi/repocpi/blobcache.go
new file mode 100644
index 0000000000..b324c97bd1
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/blobcache.go
@@ -0,0 +1,85 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "sync"
+
+ "github.com/open-component-model/ocm/pkg/blobaccess"
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+type (
+ BlobCacheEntry = blobaccess.BlobAccess
+ BlobCacheKey = interface{}
+)
+
+type BlobCache interface {
+ // AddBlobFor stores blobs for added blobs not yet accessible
+ // by generated access method until version is finally added.
+ AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error
+
+ // GetBlobFor retrieves the original blob access for
+ // a given access specification.
+ GetBlobFor(acc BlobCacheKey) BlobCacheEntry
+
+ RemoveBlobFor(acc BlobCacheKey)
+ Clear() error
+}
+
+type blobCache struct {
+ lock sync.Mutex
+ blobcache map[BlobCacheKey]BlobCacheEntry
+}
+
+func NewBlobCache() BlobCache {
+ return &blobCache{
+ blobcache: map[BlobCacheKey]BlobCacheEntry{},
+ }
+}
+
+func (c *blobCache) RemoveBlobFor(acc BlobCacheKey) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ if b := c.blobcache[acc]; b != nil {
+ b.Close()
+ delete(c.blobcache, acc)
+ }
+}
+
+func (c *blobCache) AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error {
+ if s, ok := acc.(string); ok && s == "" {
+ return errors.ErrInvalid("blob key")
+ }
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if c.blobcache[acc] == nil {
+ l, err := blob.Dup()
+ if err != nil {
+ return err
+ }
+ c.blobcache[acc] = l
+ }
+ return nil
+}
+
+func (c *blobCache) GetBlobFor(acc BlobCacheKey) BlobCacheEntry {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ return c.blobcache[acc]
+}
+
+func (c *blobCache) Clear() error {
+ list := errors.ErrList()
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ for _, b := range c.blobcache {
+ list.Add(b.Close())
+ }
+ c.blobcache = map[BlobCacheKey]BlobCacheEntry{}
+ return list.Result()
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/bridge_c.go b/pkg/contexts/ocm/cpi/repocpi/bridge_c.go
new file mode 100644
index 0000000000..c552832888
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/bridge_c.go
@@ -0,0 +1,185 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "io"
+
+ "github.com/open-component-model/ocm/pkg/blobaccess"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/finalizer"
+ "github.com/open-component-model/ocm/pkg/optionutils"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+)
+
+type ComponentVersionAccessInfo struct {
+ Impl ComponentVersionAccessImpl
+ Lazy bool
+ Persistent bool
+}
+
+// ComponentAccessImpl is the provider implementation
+// interface for component versions.
+type ComponentAccessImpl interface {
+ SetBridge(bridge ComponentAccessBridge)
+ GetParentBridge() RepositoryViewManager
+
+ GetContext() cpi.Context
+ GetName() string
+ IsReadOnly() bool
+
+ ListVersions() ([]string, error)
+ HasVersion(vers string) (bool, error)
+ LookupVersion(version string) (*ComponentVersionAccessInfo, error)
+ NewVersion(version string, overrides ...bool) (*ComponentVersionAccessInfo, error)
+
+ io.Closer
+}
+
+type _componentAccessBridgeBase = resource.ResourceImplBase[cpi.ComponentAccess]
+
+type componentAccessBridge struct {
+ *_componentAccessBridgeBase
+ ctx cpi.Context
+ name string
+ impl ComponentAccessImpl
+}
+
+func newComponentAccessBridge(impl ComponentAccessImpl, closer ...io.Closer) (ComponentAccessBridge, error) {
+ base, err := resource.NewResourceImplBase[cpi.ComponentAccess, cpi.Repository](impl.GetParentBridge(), closer...)
+ if err != nil {
+ return nil, err
+ }
+ b := &componentAccessBridge{
+ _componentAccessBridgeBase: base,
+ ctx: impl.GetContext(),
+ name: impl.GetName(),
+ impl: impl,
+ }
+ impl.SetBridge(b)
+ return b, nil
+}
+
+func (b *componentAccessBridge) Close() error {
+ list := errors.ErrListf("closing component %s", b.name)
+ refmgmt.AllocLog.Trace("closing component bridge", "name", b.name)
+ list.Add(b.impl.Close())
+ list.Add(b._componentAccessBridgeBase.Close())
+ refmgmt.AllocLog.Trace("closed component bridge", "name", b.name)
+ return list.Result()
+}
+
+func (b *componentAccessBridge) GetContext() cpi.Context {
+ return b.ctx
+}
+
+func (b *componentAccessBridge) GetName() string {
+ return b.name
+}
+
+func (b *componentAccessBridge) IsReadOnly() bool {
+ return b.impl.IsReadOnly()
+}
+
+func (c *componentAccessBridge) IsOwned(cv cpi.ComponentVersionAccess) bool {
+ bridge, err := GetComponentVersionAccessBridge(cv)
+ if err != nil {
+ return false
+ }
+
+ impl := bridge.(*componentVersionAccessBridge).impl
+ cvcompmgr := impl.GetParentBridge()
+ return c == cvcompmgr
+}
+
+func (b *componentAccessBridge) ListVersions() ([]string, error) {
+ return b.impl.ListVersions()
+}
+
+func (b *componentAccessBridge) LookupVersion(version string) (cpi.ComponentVersionAccess, error) {
+ i, err := b.impl.LookupVersion(version)
+ if err != nil {
+ return nil, err
+ }
+ if i == nil || i.Impl == nil {
+ return nil, errors.ErrInvalid("component implementation behaviour", "LookupVersion")
+ }
+ return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, i.Persistent, !compositionmodeattr.Get(b.GetContext()))
+}
+
+func (b *componentAccessBridge) HasVersion(vers string) (bool, error) {
+ return b.impl.HasVersion(vers)
+}
+
+func (b *componentAccessBridge) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) {
+ i, err := b.impl.NewVersion(version, overrides...)
+ if err != nil {
+ return nil, err
+ }
+ if i == nil || i.Impl == nil {
+ return nil, errors.ErrInvalid("component implementation behaviour", "NewVersion")
+ }
+ return NewComponentVersionAccess(b.GetName(), version, i.Impl, i.Lazy, false, !compositionmodeattr.Get(b.GetContext()))
+}
+
+func (c *componentAccessBridge) AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error) {
+ var finalize finalizer.Finalizer
+ defer finalize.FinalizeWithErrorPropagation(&ferr)
+
+ cvbridge, err := GetComponentVersionAccessBridge(cv)
+ if err != nil {
+ return err
+ }
+
+ forcestore := c.IsOwned(cv)
+ if !forcestore {
+ eff, err := c.NewVersion(cv.GetVersion(), optionutils.AsValue(opts.Overwrite))
+ if err != nil {
+ return err
+ }
+ finalize.With(func() error {
+ return eff.Close()
+ })
+ cvbridge, err = GetComponentVersionAccessBridge(eff)
+ if err != nil {
+ return err
+ }
+
+ d := eff.GetDescriptor()
+ *d = *cv.GetDescriptor().Copy()
+
+ err = c.setupLocalBlobs("resource", cv, cvbridge, d.Resources, &opts.BlobUploadOptions)
+ if err == nil {
+ err = c.setupLocalBlobs("source", cv, cvbridge, d.Sources, &opts.BlobUploadOptions)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ cvbridge.EnablePersistence()
+ err = cvbridge.Update(!cvbridge.UseDirectAccess())
+ return err
+}
+
+func (c *componentAccessBridge) setupLocalBlobs(kind string, src cpi.ComponentVersionAccess, tgtbridge ComponentVersionAccessBridge, it compdesc.ArtifactAccessor, opts *cpi.BlobUploadOptions) (ferr error) {
+ ctx := src.GetContext()
+ // transfer all local blobs
+ prov := func(spec cpi.AccessSpec) (blob blobaccess.BlobAccess, ref string, global cpi.AccessSpec, err error) {
+ if spec.IsLocal(ctx) {
+ m, err := spec.AccessMethod(src)
+ if err != nil {
+ return nil, "", nil, err
+ }
+ return m.AsBlobAccess(), cpi.ReferenceHint(spec, src), cpi.GlobalAccess(spec, tgtbridge.GetContext()), nil
+ }
+ return nil, "", nil, nil
+ }
+
+ return tgtbridge.(*componentVersionAccessBridge).setupLocalBlobs(kind, prov, it, false, opts)
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/bridge_cv.go b/pkg/contexts/ocm/cpi/repocpi/bridge_cv.go
new file mode 100644
index 0000000000..95bdc20a47
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/bridge_cv.go
@@ -0,0 +1,488 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "sync"
+
+ "github.com/open-component-model/ocm/pkg/blobaccess"
+ "github.com/open-component-model/ocm/pkg/common"
+ "github.com/open-component-model/ocm/pkg/common/accessio"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/internal"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/finalizer"
+ "github.com/open-component-model/ocm/pkg/optionutils"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+ "github.com/open-component-model/ocm/pkg/utils"
+)
+
+// here, we define the common implementation agnostic parts
+// for component version objects referred to by a ComponentVersionView.
+
+// ComponentVersionAccessImpl is the provider implementation
+// interface for component versions.
+type ComponentVersionAccessImpl interface {
+ GetContext() cpi.Context
+ SetBridge(bridge ComponentVersionAccessBridge)
+ GetParentBridge() ComponentAccessBridge
+
+ Repository() cpi.Repository
+
+ IsReadOnly() bool
+
+ GetDescriptor() *compdesc.ComponentDescriptor
+ SetDescriptor(*compdesc.ComponentDescriptor) error
+
+ AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error)
+ GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string
+
+ BlobContainer
+ io.Closer
+}
+
+type _componentVersionAccessBridgeBase = resource.ResourceImplBase[cpi.ComponentVersionAccess]
+
+// componentVersionAccessBridge is the counterpart to views, all views
+// created by Dup calls use this base object to work on.
+// Besides some functionality covered by view objects these base objects
+// implement provider-agnostic parts of the ComponentVersionAccess API.
+type componentVersionAccessBridge struct {
+ lock sync.Mutex
+ id finalizer.ObjectIdentity
+
+ *_componentVersionAccessBridgeBase
+ ctx cpi.Context
+ name string
+ version string
+
+ descriptor *compdesc.ComponentDescriptor
+ blobcache BlobCache
+
+ lazy bool
+ directAccess bool
+ persistent bool
+ discardChanges bool
+
+ impl ComponentVersionAccessImpl
+}
+
+var _ ComponentVersionAccessBridge = (*componentVersionAccessBridge)(nil)
+
+func newComponentVersionAccessBridge(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (ComponentVersionAccessBridge, error) {
+ base, err := resource.NewResourceImplBase[cpi.ComponentVersionAccess, cpi.ComponentAccess](impl.GetParentBridge(), closer...)
+ if err != nil {
+ return nil, err
+ }
+ b := &componentVersionAccessBridge{
+ _componentVersionAccessBridgeBase: base,
+ id: finalizer.NewObjectIdentity(fmt.Sprintf("%s:%s", name, version)),
+ ctx: impl.GetContext(),
+ name: name,
+ version: version,
+ blobcache: NewBlobCache(),
+ lazy: lazy,
+ persistent: persistent,
+ directAccess: direct,
+ impl: impl,
+ }
+ impl.SetBridge(b)
+ return b, nil
+}
+
+func GetComponentVersionImpl[T ComponentVersionAccessImpl](cv cpi.ComponentVersionAccess) (T, error) {
+ var _nil T
+
+ impl, err := GetComponentVersionAccessBridge(cv)
+ if err != nil {
+ return _nil, err
+ }
+ if mine, ok := impl.(*componentVersionAccessBridge); ok {
+ cont, ok := mine.impl.(T)
+ if ok {
+ return cont, nil
+ }
+ return _nil, errors.Newf("non-matching component version implementation %T", mine.impl)
+ }
+ return _nil, errors.Newf("non-matching component version implementation %T", impl)
+}
+
+func (b *componentVersionAccessBridge) Close() error {
+ list := errors.ErrListf("closing component version %s", common.VersionedElementKey(b))
+ refmgmt.AllocLog.Trace("closing component version base", "name", common.VersionedElementKey(b))
+ // prepare artifact access for final close in
+ // direct access mode.
+ if !compositionmodeattr.Get(b.ctx) {
+ list.Add(b.update(true))
+ }
+ list.Add(b.impl.Close())
+ list.Add(b._componentVersionAccessBridgeBase.Close())
+ list.Add(b.blobcache.Clear())
+ refmgmt.AllocLog.Trace("closed component version base", "name", common.VersionedElementKey(b))
+ return list.Result()
+}
+
+func (b *componentVersionAccessBridge) GetContext() cpi.Context {
+ return b.ctx
+}
+
+func (b *componentVersionAccessBridge) GetName() string {
+ return b.name
+}
+
+func (b *componentVersionAccessBridge) GetVersion() string {
+ return b.version
+}
+
+func (b *componentVersionAccessBridge) GetImplementation() ComponentVersionAccessImpl {
+ return b.impl
+}
+
+func (b *componentVersionAccessBridge) GetBlobCache() BlobCache {
+ return b.blobcache
+}
+
+func (b *componentVersionAccessBridge) EnablePersistence() bool {
+ if b.discardChanges {
+ return false
+ }
+ b.persistent = true
+ b.GetStorageContext()
+ return true
+}
+
+func (b *componentVersionAccessBridge) IsPersistent() bool {
+ return b.persistent
+}
+
+func (b *componentVersionAccessBridge) UseDirectAccess() bool {
+ return b.directAccess
+}
+
+func (b *componentVersionAccessBridge) DiscardChanges() {
+ b.discardChanges = true
+}
+
+func (b *componentVersionAccessBridge) Repository() cpi.Repository {
+ return b.impl.Repository()
+}
+
+func (b *componentVersionAccessBridge) IsReadOnly() bool {
+ return b.impl.IsReadOnly()
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// with access to actual view
+
+func (b *componentVersionAccessBridge) AccessMethod(spec cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (meth cpi.AccessMethod, err error) {
+ switch {
+ case compose.Is(spec):
+ cspec, ok := spec.(*compose.AccessSpec)
+ if !ok {
+ return nil, fmt.Errorf("invalid implementation (%T) for access method compose", spec)
+ }
+ blob := b.getLocalBlob(cspec)
+ if blob == nil {
+ return nil, errors.ErrUnknown(blobaccess.KIND_BLOB, cspec.Id, common.VersionedElementKey(b).String())
+ }
+ meth, err = compose.NewMethod(cspec, blob)
+ case spec.IsLocal(b.ctx):
+ meth, err = b.impl.AccessMethod(spec, cv)
+ if err == nil {
+ if blob := b.getLocalBlob(spec); blob != nil {
+ meth, err = newFakeMethod(meth, blob)
+ }
+ }
+ }
+ return meth, err
+}
+
+func (b *componentVersionAccessBridge) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string {
+ return b.impl.GetInexpensiveContentVersionIdentity(acc, cv)
+}
+
+func (b *componentVersionAccessBridge) GetDescriptor() *compdesc.ComponentDescriptor {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+
+ return b.getDescriptor()
+}
+
+func (b *componentVersionAccessBridge) getDescriptor() *compdesc.ComponentDescriptor {
+ if b.descriptor == nil {
+ b.descriptor = b.impl.GetDescriptor()
+ }
+ return b.descriptor
+}
+
+func (b *componentVersionAccessBridge) GetStorageContext() cpi.StorageContext {
+ return b.impl.GetStorageContext()
+}
+
+func (b *componentVersionAccessBridge) ShouldUpdate(final bool) bool {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+
+ return b.shouldUpdate(final)
+}
+
+func (b *componentVersionAccessBridge) shouldUpdate(final bool) bool {
+ if b.discardChanges {
+ return false
+ }
+ if final {
+ return b.persistent
+ }
+ return !b.lazy && b.directAccess && b.persistent
+}
+
+func (b *componentVersionAccessBridge) Update(final bool) error {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+
+ return b.update(final)
+}
+
+func (b *componentVersionAccessBridge) update(final bool) error {
+ if !b.shouldUpdate(final) {
+ return nil
+ }
+
+ d := b.getDescriptor()
+
+ opts := &cpi.BlobUploadOptions{
+ UseNoDefaultIfNotSet: optionutils.PointerTo(true),
+ }
+ err := b.setupLocalBlobs("resource", b.composeAccess, d.Resources, true, opts)
+ if err == nil {
+ err = b.setupLocalBlobs("source", b.composeAccess, d.Sources, true, opts)
+ }
+ if err != nil {
+ return err
+ }
+
+ err = b.impl.SetDescriptor(b.descriptor.Copy())
+ if err != nil {
+ return err
+ }
+ err = b.blobcache.Clear()
+ return err
+}
+
+func (b *componentVersionAccessBridge) getLocalBlob(acc cpi.AccessSpec) cpi.BlobAccess {
+ key, err := json.Marshal(acc)
+ if err != nil {
+ return nil
+ }
+ return b.blobcache.GetBlobFor(string(key))
+}
+
+func (b *componentVersionAccessBridge) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error) {
+ if blob == nil {
+ return nil, errors.New("a resource has to be defined")
+ }
+ if b.IsReadOnly() {
+ return nil, accessio.ErrReadOnly
+ }
+ blob, err := blob.Dup()
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid blob access")
+ }
+ defer blob.Close()
+ err = utils.ValidateObject(blob)
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid blob access")
+ }
+
+ ctx := b.GetContext()
+
+ // handle foreign blob upload
+ var prov cpi.BlobHandlerProvider
+ if opts.BlobHandlerProvider != nil {
+ prov = opts.BlobHandlerProvider
+ } else {
+ if !optionutils.AsValue(opts.UseNoDefaultIfNotSet) {
+ prov = internal.BlobHandlerProviderForRegistry(ctx.BlobHandlers())
+ } else { //nolint: staticcheck // yes
+ // use no blob uploader
+ }
+ }
+ if prov != nil {
+ storagectx := b.GetStorageContext()
+ h := prov.LookupHandler(storagectx, artType, blob.MimeType())
+ if h != nil {
+ acc, err := h.StoreBlob(blob, artType, refName, nil, storagectx)
+ if err != nil {
+ return nil, err
+ }
+ if acc != nil {
+ if !keepblobattr.Get(ctx) || acc.IsLocal(ctx) {
+ return acc, nil
+ }
+ global = acc
+ }
+ }
+ }
+
+ var acc cpi.AccessSpec
+
+ if final || b.UseDirectAccess() {
+ acc, err = b.impl.AddBlob(blob, refName, global)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ // use local composition access to be added to the repository with AddVersion.
+ acc = compose.New(refName, blob.MimeType(), global)
+ }
+ return b.cacheLocalBlob(acc, blob)
+}
+
+func (b *componentVersionAccessBridge) cacheLocalBlob(acc cpi.AccessSpec, blob cpi.BlobAccess) (cpi.AccessSpec, error) {
+ key, err := json.Marshal(acc)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot marshal access spec")
+ }
+ // local blobs might not be accessible from the underlying
+ // repository implementation if the component version is not
+ // finally added (for example ghcr.io as OCI repository).
+ // Therefore, we keep a copy of the blob access for further usage.
+
+ // if a local blob is uploader and the access method is replaced
+ // we have to handle the case that the technical upload repo
+ // is the same as the storage backend of the OCM repository, which
+ // might have been configured with local credentials, which were
+ // reused by the uploader.
+ // The access spec is independent of the actual repo, so it does
+ // not have access to those credentials. Therefore, we have to
+ // keep the original blob for further usage, also.
+ k := BlobCacheKey(string(key))
+ err = b.blobcache.AddBlobFor(k, blob)
+ if err != nil {
+ return nil, err
+ }
+ return acc, nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+func (b *componentVersionAccessBridge) composeAccess(spec cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error) {
+ if !compose.Is(spec) {
+ return nil, "", nil, nil
+ }
+ cspec, ok := spec.(*compose.AccessSpec)
+ if !ok {
+ return nil, "", nil, fmt.Errorf("invalid implementation (%T) for access method compose", spec)
+ }
+ blob := b.getLocalBlob(cspec)
+ if blob == nil {
+ return nil, "", nil, errors.ErrUnknown(blobaccess.KIND_BLOB, cspec.Id, common.VersionedElementKey(b).String())
+ }
+ blob, err := blob.Dup()
+ if err != nil {
+ return nil, "", nil, errors.Wrapf(err, "cached blob")
+ }
+
+ return blob, cspec.ReferenceName, cspec.GlobalAccess.Get(), nil
+}
+
+func (b *componentVersionAccessBridge) setupLocalBlobs(kind string, accprov func(cpi.AccessSpec) (blobaccess.BlobAccess, string, cpi.AccessSpec, error), it compdesc.ArtifactAccessor, final bool, opts *cpi.BlobUploadOptions) (ferr error) {
+ var finalize finalizer.Finalizer
+ defer finalize.FinalizeWithErrorPropagation(&ferr)
+
+ for i := 0; i < it.Len(); i++ {
+ nested := finalize.Nested()
+ a := it.GetArtifact(i)
+ spec, err := b.ctx.AccessSpecForSpec(a.GetAccess())
+ if err != nil {
+ return errors.Wrapf(err, "%s %d", kind, i)
+ }
+ blob, ref, global, err := accprov(spec)
+ if err != nil {
+ return errors.Wrapf(err, "%s %d", kind, i)
+ }
+ if blob != nil {
+ nested.Close(blob)
+
+ effspec, err := b.AddBlob(blob, a.GetType(), ref, global, final, opts)
+ if err != nil {
+ return errors.Wrapf(err, "cannot store %s %d", kind, i)
+ }
+ a.SetAccess(effspec)
+ }
+ err = nested.Finalize()
+ if err != nil {
+ return errors.Wrapf(err, "%s %d", kind, i)
+ }
+ }
+ return nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type fakeMethod struct {
+ spec cpi.AccessSpec
+ local bool
+ mime string
+ blob blobaccess.BlobAccess
+}
+
+var _ accspeccpi.AccessMethodImpl = (*fakeMethod)(nil)
+
+func newFakeMethod(m cpi.AccessMethod, blob cpi.BlobAccess) (cpi.AccessMethod, error) {
+ b, err := blob.Dup()
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot remember blob for access method")
+ }
+ f := &fakeMethod{
+ spec: m.AccessSpec(),
+ local: m.IsLocal(),
+ mime: m.MimeType(),
+ blob: b,
+ }
+ err = m.Close()
+ if err != nil {
+ _ = b.Close()
+ return nil, errors.Wrapf(err, "closing access method")
+ }
+ return accspeccpi.AccessMethodForImplementation(f, nil)
+}
+
+func (f *fakeMethod) MimeType() string {
+ return f.mime
+}
+
+func (f *fakeMethod) IsLocal() bool {
+ return f.local
+}
+
+func (f *fakeMethod) GetKind() string {
+ return f.spec.GetKind()
+}
+
+func (f *fakeMethod) AccessSpec() internal.AccessSpec {
+ return f.spec
+}
+
+func (f *fakeMethod) Close() error {
+ return f.blob.Close()
+}
+
+func (f *fakeMethod) Reader() (io.ReadCloser, error) {
+ return f.blob.Reader()
+}
+
+func (f *fakeMethod) Get() ([]byte, error) {
+ return f.blob.Get()
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/bridge_r.go b/pkg/contexts/ocm/cpi/repocpi/bridge_r.go
new file mode 100644
index 0000000000..b2d6f093dc
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/bridge_r.go
@@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "io"
+
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+)
+
+type ComponentAccessInfo struct {
+ Impl ComponentAccessImpl
+ Kind string
+ Main bool
+}
+
+type RepositoryImpl interface {
+ SetBridge(bridge RepositoryBridge)
+
+ GetContext() cpi.Context
+
+ GetSpecification() cpi.RepositorySpec
+ ComponentLister() cpi.ComponentLister
+
+ ExistsComponentVersion(name string, version string) (bool, error)
+ LookupComponent(name string) (*ComponentAccessInfo, error)
+
+ io.Closer
+}
+
+type _repositoryBridgeBase = resource.ResourceImplBase[cpi.Repository]
+
+type repositoryBridge struct {
+ *_repositoryBridgeBase
+ ctx cpi.Context
+ kind string
+ impl RepositoryImpl
+}
+
+func newRepositoryBridge(impl RepositoryImpl, kind string, closer ...io.Closer) RepositoryBridge {
+ base := resource.NewSimpleResourceImplBase[cpi.Repository](closer...)
+ b := &repositoryBridge{
+ _repositoryBridgeBase: base,
+ ctx: impl.GetContext(),
+ impl: impl,
+ }
+ impl.SetBridge(b)
+ return b
+}
+
+func (b *repositoryBridge) Close() error {
+ list := errors.ErrListf("closing %s", b.kind)
+ refmgmt.AllocLog.Trace("closing repository bridge", "kind", b.kind)
+ list.Add(b.impl.Close())
+ list.Add(b._repositoryBridgeBase.Close())
+ refmgmt.AllocLog.Trace("closed repository bridge", "kind", b.kind)
+ return list.Result()
+}
+
+func (b *repositoryBridge) GetContext() cpi.Context {
+ return b.ctx
+}
+
+func (b *repositoryBridge) GetSpecification() cpi.RepositorySpec {
+ return b.impl.GetSpecification()
+}
+
+func (b *repositoryBridge) ComponentLister() cpi.ComponentLister {
+ return b.impl.ComponentLister()
+}
+
+func (b *repositoryBridge) ExistsComponentVersion(name string, version string) (bool, error) {
+ return b.impl.ExistsComponentVersion(name, version)
+}
+
+func (b *repositoryBridge) LookupComponentVersion(name string, version string) (cv cpi.ComponentVersionAccess, rerr error) {
+ c, err := b.LookupComponent(name)
+ if err != nil {
+ return nil, err
+ }
+ defer refmgmt.PropagateCloseTemporary(&rerr, c) // temporary component object not exposed.
+ refmgmt.AllocLog.Trace("lookup version for temporary component ref", "component", name, "version", version)
+ return c.LookupVersion(version)
+}
+
+func (b *repositoryBridge) LookupComponent(name string) (cpi.ComponentAccess, error) {
+ i, err := b.impl.LookupComponent(name)
+ if err != nil {
+ return nil, err
+ }
+ return NewComponentAccess(i.Impl, i.Kind, i.Main)
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/doc.go b/pkg/contexts/ocm/cpi/repocpi/doc.go
new file mode 100644
index 0000000000..017ad14993
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/doc.go
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// Package repocpi contains the implementation support
+// for repository backends. It offers three methods
+// to create component version, component and repository
+// objects based on three simple implementation interfaces.
+//
+// The basic provisioning model is layered:
+//
+// - on layer 1 there is the user facing API defined
+// in package [github.com/open-component-model/ocm/pkg/contexts/ocm].
+//
+// - on layer 2 (this package) there is a backend agnostic
+// implementation of standard functionality based on layer 3.
+// This is divided into two parts
+//
+// a) the view objects provided by the Dup() calls of the layer 1 API.
+// All dups are internally based on a single base object.
+// These objects are called bridge. They act as base object
+// for the views and as abstraction for the implementation objects
+// providing generic implementations potentially based on
+// the implementation functionality.
+// (see bridge design pattern https://refactoring.guru/design-patterns/bridge)
+//
+// b) the bridge object as base for all dup views is used to implement some
+// common functionality like the view management. The bridge object
+// is closed, when the last view disappears.
+// This bridge object then calls the final
+// storage backend implementation interface.
+//
+// - the storage backend implementations based on the implementation
+// interfaces provided by layer 2.
+//
+// The implementation interfaces and the functions to create API objects are:
+//
+// - interface [ComponentVersionAccessImpl] is used to create an ocm.ComponentVersionAccess object
+// using the function [NewComponentVersionAccess].
+// - interface [ComponentAccessImpl] is used to create an ocm.ComponentAccess object
+// using the function [NewComponentAccess].
+// - interface [RepositoryImpl] is used to create an ocm.ComponentAccess object
+// using the function [NewRepository].
+//
+// Component version implementations provide basic access to component versions
+// and their descriptors. They keep a reference to component implementations, which are
+// again based on repository implementations. The task of repository implementations is
+// to provide component objects. Their implementations are responsible to provide
+// component version objects.
+//
+// Besides this basic implementation interface with separated object for a
+// repository, component and component version, there is support for a simplified
+// implementation interface (StorageBackendImpl). This is a single interface
+// bundling all required functionality to implement the objects for the three
+// concerned elements. With NewStorageBackend it is possible to instantiate
+// a new kind of repository based on this single interface. The required
+// objects for components and component versions are generically provided
+// based on the methods provided by this interface.
+package repocpi
diff --git a/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go
new file mode 100644
index 0000000000..aec5b364f0
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/helperinterfaces.go
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "fmt"
+
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+)
+
+var (
+ ErrClosed = resource.ErrClosed
+ ErrTempVersion = fmt.Errorf("temporary component version cannot be updated")
+)
+
+// BlobContainer is the interface for an element capable to store blobs.
+type BlobContainer interface {
+ GetBlob(name string) (cpi.DataAccess, error)
+
+ // GetStorageContext creates a storage context for blobs
+ // that is used to feed blob handlers for specific blob storage methods.
+ // If no handler accepts the blob, the AddBlobFor method will
+ // be used to store the blob
+ GetStorageContext() cpi.StorageContext
+
+ // AddBlob stores a local blob together with the component and
+ // potentially provides a global reference according to the OCI distribution spec
+ // if the blob described an oci artifact.
+ // The resulting access information (global and local) is provided as
+ // an access method specification usable in a component descriptor.
+ // This is the direct technical storage, without caring about any handler.
+ AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error)
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/ocmimpllayers.png b/pkg/contexts/ocm/cpi/repocpi/ocmimpllayers.png
new file mode 100755
index 0000000000..d0fc75645f
Binary files /dev/null and b/pkg/contexts/ocm/cpi/repocpi/ocmimpllayers.png differ
diff --git a/pkg/contexts/ocm/cpi/repocpi/view_c.go b/pkg/contexts/ocm/cpi/repocpi/view_c.go
new file mode 100644
index 0000000000..fa6678c1a7
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/view_c.go
@@ -0,0 +1,152 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/open-component-model/ocm/pkg/common/accessio"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+ "github.com/open-component-model/ocm/pkg/utils"
+)
+
+type _componentAccessView interface {
+ resource.ResourceViewInt[cpi.ComponentAccess] // here you have to redeclare
+}
+
+type ComponentAccessViewManager = resource.ViewManager[cpi.ComponentAccess] // here you have to use an alias
+
+type ComponentAccessBridge interface {
+ resource.ResourceImplementation[cpi.ComponentAccess]
+
+ GetContext() cpi.Context
+ IsReadOnly() bool
+ GetName() string
+
+ IsOwned(access cpi.ComponentVersionAccess) bool
+
+ ListVersions() ([]string, error)
+ LookupVersion(version string) (cpi.ComponentVersionAccess, error)
+ HasVersion(vers string) (bool, error)
+ NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error)
+
+ Close() error
+ AddVersion(cv cpi.ComponentVersionAccess, opts *cpi.AddVersionOptions) (ferr error)
+}
+
+type componentAccessView struct {
+ _componentAccessView
+ bridge ComponentAccessBridge
+}
+
+var (
+ _ cpi.ComponentAccess = (*componentAccessView)(nil)
+ _ utils.Unwrappable = (*componentAccessView)(nil)
+)
+
+func GetComponentAccessBridge(n cpi.ComponentAccess) (ComponentAccessBridge, error) {
+ if v, ok := n.(*componentAccessView); ok {
+ return v.bridge, nil
+ }
+ return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", n))
+}
+
+func GetComponentAccessImplementation(n cpi.ComponentAccess) (ComponentAccessImpl, error) {
+ if v, ok := n.(*componentAccessView); ok {
+ if b, ok := v.bridge.(*componentAccessBridge); ok {
+ return b.impl, nil
+ }
+ return nil, errors.ErrNotSupported("component base type", fmt.Sprintf("%T", v.bridge))
+ }
+ return nil, errors.ErrNotSupported("component implementation type", fmt.Sprintf("%T", n))
+}
+
+func componentAccessViewCreator(i ComponentAccessBridge, v resource.CloserView, d ComponentAccessViewManager) cpi.ComponentAccess {
+ return &componentAccessView{
+ _componentAccessView: resource.NewView[cpi.ComponentAccess](v, d),
+ bridge: i,
+ }
+}
+
+func NewComponentAccess(impl ComponentAccessImpl, kind string, main bool, closer ...io.Closer) (cpi.ComponentAccess, error) {
+ bridge, err := newComponentAccessBridge(impl, closer...)
+ if err != nil {
+ return nil, errors.Join(err, impl.Close())
+ }
+ if kind == "" {
+ kind = "component"
+ }
+ cv := resource.NewResource[cpi.ComponentAccess](bridge, componentAccessViewCreator, fmt.Sprintf("%s %s", kind, impl.GetName()), main)
+ return cv, nil
+}
+
+func (c *componentAccessView) Unwrap() interface{} {
+ return c.bridge
+}
+
+func (c *componentAccessView) GetContext() cpi.Context {
+ return c.bridge.GetContext()
+}
+
+func (c *componentAccessView) GetName() string {
+ return c.bridge.GetName()
+}
+
+func (c *componentAccessView) ListVersions() (list []string, err error) {
+ err = c.Execute(func() error {
+ list, err = c.bridge.ListVersions()
+ return err
+ })
+ return list, err
+}
+
+func (c *componentAccessView) LookupVersion(version string) (acc cpi.ComponentVersionAccess, err error) {
+ err = c.Execute(func() error {
+ acc, err = c.bridge.LookupVersion(version)
+ return err
+ })
+ return acc, err
+}
+
+func (c *componentAccessView) AddVersion(acc cpi.ComponentVersionAccess, overwrite ...bool) error {
+ if acc.GetName() != c.GetName() {
+ return errors.ErrInvalid("component name", acc.GetName())
+ }
+
+ return c.Execute(func() error {
+ return c.bridge.AddVersion(acc, cpi.NewAddVersionOptions(cpi.Overwrite(utils.Optional(overwrite...))))
+ })
+}
+
+func (c *componentAccessView) AddVersionOpt(acc cpi.ComponentVersionAccess, opts ...cpi.AddVersionOption) error {
+ if acc.GetName() != c.GetName() {
+ return errors.ErrInvalid("component name", acc.GetName())
+ }
+ return c.Execute(func() error {
+ return c.bridge.AddVersion(acc, cpi.NewAddVersionOptions(opts...))
+ })
+}
+
+func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc cpi.ComponentVersionAccess, err error) {
+ err = c.Execute(func() error {
+ if c.bridge.IsReadOnly() {
+ return accessio.ErrReadOnly
+ }
+ acc, err = c.bridge.NewVersion(version, overrides...)
+ return err
+ })
+ return acc, err
+}
+
+func (c *componentAccessView) HasVersion(vers string) (ok bool, err error) {
+ err = c.Execute(func() error {
+ ok, err = c.bridge.HasVersion(vers)
+ return err
+ })
+ return ok, err
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/view_cv.go b/pkg/contexts/ocm/cpi/repocpi/view_cv.go
new file mode 100644
index 0000000000..f6c131f989
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/view_cv.go
@@ -0,0 +1,741 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+
+ "github.com/open-component-model/ocm/pkg/blobaccess"
+ "github.com/open-component-model/ocm/pkg/common"
+ "github.com/open-component-model/ocm/pkg/common/accessio"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
+ metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/internal"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+ "github.com/open-component-model/ocm/pkg/utils"
+ "github.com/open-component-model/ocm/pkg/utils/selector"
+)
+
+// View objects are the user facing generic implementations of the context interfaces.
+// They are responsible to handle the reference counting and use
+// shared implementations objects for th concrete type-specific implementations.
+// Additionally, they are used to implement interface functionality which is
+// common to all implementations and NOT dependent on the backend system technology.
+
+// here are the views implementing the user facing ComponentVersionAccess
+// interface.
+
+type _componentVersionAccessView interface {
+ resource.ResourceViewInt[cpi.ComponentVersionAccess]
+}
+
+type ComponentVersionAccessViewManager = resource.ViewManager[cpi.ComponentVersionAccess]
+
+type ComponentVersionAccessBridge interface {
+ resource.ResourceImplementation[cpi.ComponentVersionAccess]
+ common.VersionedElement
+ io.Closer
+
+ GetContext() cpi.Context
+ Repository() cpi.Repository
+
+ GetImplementation() ComponentVersionAccessImpl
+
+ EnablePersistence() bool
+ DiscardChanges()
+ IsPersistent() bool
+
+ GetDescriptor() *compdesc.ComponentDescriptor
+
+ AccessMethod(cpi.AccessSpec, refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error)
+ GetInexpensiveContentVersionIdentity(cpi.AccessSpec, refmgmt.ExtendedAllocatable) string
+
+ // GetStorageContext creates a storage context for blobs
+ // that is used to feed blob handlers for specific blob storage methods.
+ // If no handler accepts the blob, the AddBlob method will
+ // be used to store the blob
+ GetStorageContext() cpi.StorageContext
+
+ // AddBlob stores a local blob together with the component and
+ // potentially provides a global reference.
+ // The resulting access information (global and local) is provided as
+ // an access method specification usable in a component descriptor.
+ // This is the direct technical storage, without caring about any handler.
+ AddBlob(blob cpi.BlobAccess, arttype, refName string, global cpi.AccessSpec, final bool, opts *cpi.BlobUploadOptions) (cpi.AccessSpec, error)
+
+ IsReadOnly() bool
+
+ // ShouldUpdate checks, whether an update is indicated
+ // by the state of object, considering persistence, lazy, discard
+ // and update mode state
+ ShouldUpdate(final bool) bool
+
+ // GetBlobCache retieves the blob cache used to store preliminary
+ // blob accesses for freshly generated local access specs not directly
+ // usable until a component version is finally added to the repository.
+ GetBlobCache() BlobCache
+
+ // UseDirectAccess returns true if composition should be directly
+ // forwarded to the repository backend.,
+ UseDirectAccess() bool
+
+ // Update persists the current state of the component version to the
+ // underlying repository backend.
+ Update(final bool) error
+}
+
+type componentVersionAccessView struct {
+ _componentVersionAccessView
+ bridge ComponentVersionAccessBridge
+ err error
+}
+
+var (
+ _ cpi.ComponentVersionAccess = (*componentVersionAccessView)(nil)
+ _ utils.Unwrappable = (*componentVersionAccessView)(nil)
+)
+
+func GetComponentVersionAccessBridge(n cpi.ComponentVersionAccess) (ComponentVersionAccessBridge, error) {
+ if v, ok := n.(*componentVersionAccessView); ok {
+ return v.bridge, nil
+ }
+ return nil, errors.ErrNotSupported("component version bridge type", fmt.Sprintf("%T", n))
+}
+
+func GetComponentVersionAccessImplementation(n cpi.ComponentVersionAccess) (ComponentVersionAccessImpl, error) {
+ if v, ok := n.(*componentVersionAccessView); ok {
+ if b, ok := v.bridge.(*componentVersionAccessBridge); ok {
+ return b.impl, nil
+ }
+ return nil, errors.ErrNotSupported("component version bridge type", fmt.Sprintf("%T", v.bridge))
+ }
+ return nil, errors.ErrNotSupported("component version implementation type", fmt.Sprintf("%T", n))
+}
+
+func artifactAccessViewCreator(i ComponentVersionAccessBridge, v resource.CloserView, d resource.ViewManager[cpi.ComponentVersionAccess]) cpi.ComponentVersionAccess {
+ cv := &componentVersionAccessView{
+ _componentVersionAccessView: resource.NewView[cpi.ComponentVersionAccess](v, d),
+ bridge: i,
+ }
+ return cv
+}
+
+func NewComponentVersionAccess(name, version string, impl ComponentVersionAccessImpl, lazy, persistent, direct bool, closer ...io.Closer) (cpi.ComponentVersionAccess, error) {
+ bridge, err := newComponentVersionAccessBridge(name, version, impl, lazy, persistent, direct, closer...)
+ if err != nil {
+ return nil, errors.Join(err, impl.Close())
+ }
+ return resource.NewResource[cpi.ComponentVersionAccess](bridge, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", name, version), true), nil
+}
+
+func (c *componentVersionAccessView) Unwrap() interface{} {
+ return c.bridge
+}
+
+func (c *componentVersionAccessView) Close() error {
+ list := errors.ErrListf("closing %s", common.VersionedElementKey(c))
+ err := c._componentVersionAccessView.Close()
+ return list.Add(c.err, err).Result()
+}
+
+func (c *componentVersionAccessView) Repository() cpi.Repository {
+ return c.bridge.Repository()
+}
+
+func (c *componentVersionAccessView) GetContext() internal.Context {
+ return c.bridge.GetContext()
+}
+
+func (c *componentVersionAccessView) GetName() string {
+ return c.bridge.GetName()
+}
+
+func (c *componentVersionAccessView) GetVersion() string {
+ return c.bridge.GetVersion()
+}
+
+func (c *componentVersionAccessView) GetDescriptor() *compdesc.ComponentDescriptor {
+ return c.bridge.GetDescriptor()
+}
+
+func (c *componentVersionAccessView) GetProvider() *compdesc.Provider {
+ return c.GetDescriptor().Provider.Copy()
+}
+
+func (c *componentVersionAccessView) SetProvider(p *compdesc.Provider) error {
+ return c.Execute(func() error {
+ c.GetDescriptor().Provider = *p.Copy()
+ return nil
+ })
+}
+
+func (c *componentVersionAccessView) AccessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) {
+ spec, err = c.GetContext().AccessSpecForSpec(spec)
+ if err != nil {
+ return nil, err
+ }
+ err = c.Execute(func() error {
+ var err error
+ meth, err = c.accessMethod(spec)
+ return err
+ })
+ return meth, err
+}
+
+func (c *componentVersionAccessView) accessMethod(spec cpi.AccessSpec) (meth cpi.AccessMethod, err error) {
+ switch {
+ case spec.IsLocal(c.GetContext()):
+ return c.bridge.AccessMethod(spec, c.Allocatable())
+ default:
+ return spec.AccessMethod(c)
+ }
+}
+
+func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec cpi.AccessSpec) string {
+ var err error
+
+ spec, err = c.GetContext().AccessSpecForSpec(spec)
+ if err != nil {
+ return ""
+ }
+
+ var id string
+ _ = c.Execute(func() error {
+ id = c.getInexpensiveContentVersionIdentity(spec)
+ return nil
+ })
+ return id
+}
+
+func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec cpi.AccessSpec) string {
+ switch {
+ case compose.Is(spec):
+ fallthrough
+ case !spec.IsLocal(c.GetContext()):
+ // fall back to original version
+ return spec.GetInexpensiveContentVersionIdentity(c)
+ default:
+ return c.bridge.GetInexpensiveContentVersionIdentity(spec, c.Allocatable())
+ }
+}
+
+func (c *componentVersionAccessView) Update() error {
+ return c.Execute(func() error {
+ if !c.bridge.IsPersistent() {
+ return ErrTempVersion
+ }
+ return c.bridge.Update(true)
+ })
+}
+
+func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refName string, global cpi.AccessSpec, opts ...internal.BlobUploadOption) (cpi.AccessSpec, error) {
+ var spec cpi.AccessSpec
+ eff := cpi.NewBlobUploadOptions(opts...)
+ err := c.Execute(func() error {
+ var err error
+ spec, err = c.bridge.AddBlob(blob, artType, refName, global, false, eff)
+ return err
+ })
+
+ return spec, err
+}
+
+func (c *componentVersionAccessView) AdjustResourceAccess(meta *cpi.ResourceMeta, acc compdesc.AccessSpec, opts ...internal.ModificationOption) error {
+ cd := c.GetDescriptor()
+ if idx := cd.GetResourceIndex(meta); idx >= 0 {
+ return c.SetResource(&cd.Resources[idx].ResourceMeta, acc, opts...)
+ }
+ return errors.ErrUnknown(cpi.KIND_RESOURCE, meta.GetIdentity(cd.Resources).String())
+}
+
+// SetResourceBlob adds a blob resource to the component version.
+func (c *componentVersionAccessView) SetResourceBlob(meta *cpi.ResourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec, opts ...internal.BlobModificationOption) error {
+ cpi.Logger(c).Debug("adding resource blob", "resource", meta.Name)
+ if err := utils.ValidateObject(blob); err != nil {
+ return err
+ }
+ eff := cpi.NewBlobModificationOptions(opts...)
+ acc, err := c.AddBlob(blob, meta.Type, refName, global, eff)
+ if err != nil {
+ return fmt.Errorf("unable to add blob (component %s:%s resource %s): %w", c.GetName(), c.GetVersion(), meta.GetName(), err)
+ }
+
+ if err := c.SetResource(meta, acc, eff, cpi.ModifyResource()); err != nil {
+ return fmt.Errorf("unable to set resource: %w", err)
+ }
+
+ return nil
+}
+
+func (c *componentVersionAccessView) AdjustSourceAccess(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error {
+ cd := c.GetDescriptor()
+ if idx := cd.GetSourceIndex(meta); idx >= 0 {
+ return c.SetSource(&cd.Sources[idx].SourceMeta, acc)
+ }
+ return errors.ErrUnknown(cpi.KIND_RESOURCE, meta.GetIdentity(cd.Resources).String())
+}
+
+func (c *componentVersionAccessView) SetSourceBlob(meta *cpi.SourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec) error {
+ cpi.Logger(c).Debug("adding source blob", "source", meta.Name)
+ if err := utils.ValidateObject(blob); err != nil {
+ return err
+ }
+ acc, err := c.AddBlob(blob, meta.Type, refName, global)
+ if err != nil {
+ return fmt.Errorf("unable to add blob: (component %s:%s source %s): %w", c.GetName(), c.GetVersion(), meta.GetName(), err)
+ }
+
+ if err := c.SetSource(meta, acc); err != nil {
+ return fmt.Errorf("unable to set source: %w", err)
+ }
+
+ return nil
+}
+
+func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessView, kind string, art A,
+ set func(*T, compdesc.AccessSpec) error,
+ setblob func(*T, cpi.BlobAccess, string, cpi.AccessSpec) error,
+) error {
+ if c.bridge.IsReadOnly() {
+ return accessio.ErrReadOnly
+ }
+ meta := art.Meta()
+ if meta == nil {
+ return errors.Newf("no meta data provided by %s access", kind)
+ }
+ acc, err := art.Access()
+ if err != nil && !errors.IsErrNotFoundElem(err, "", descriptor.KIND_ACCESSMETHOD) {
+ return err
+ }
+
+ var (
+ blob cpi.BlobAccess
+ hint string
+ global cpi.AccessSpec
+ )
+
+ if acc != nil {
+ if !acc.IsLocal(c.GetContext()) {
+ return set(meta, acc)
+ }
+
+ blob, err = accspeccpi.BlobAccessForAccessSpec(acc, c)
+ if err != nil && errors.IsErrNotFoundElem(err, "", blobaccess.KIND_BLOB) {
+ return err
+ }
+ hint = cpi.ReferenceHint(acc, c)
+ global = cpi.GlobalAccess(acc, c.GetContext())
+ }
+ if blob == nil {
+ blob, err = art.BlobAccess()
+ if err != nil {
+ return err
+ }
+ defer blob.Close()
+ }
+ if blob == nil {
+ return errors.Newf("neither access nor blob specified in %s access", kind)
+ }
+ if v := art.ReferenceHint(); v != "" {
+ hint = v
+ }
+ if v := art.GlobalAccess(); v != nil {
+ global = v
+ }
+ return setblob(meta, blob, hint, global)
+}
+
+func (c *componentVersionAccessView) SetResourceAccess(art cpi.ResourceAccess, modopts ...cpi.BlobModificationOption) error {
+ return setAccess(c, "resource", art,
+ func(meta *cpi.ResourceMeta, acc compdesc.AccessSpec) error {
+ return c.SetResource(meta, acc, cpi.NewBlobModificationOptions(modopts...))
+ },
+ func(meta *cpi.ResourceMeta, blob cpi.BlobAccess, hint string, global cpi.AccessSpec) error {
+ return c.SetResourceBlob(meta, blob, hint, global, modopts...)
+ })
+}
+
+func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, acc compdesc.AccessSpec, modopts ...cpi.ModificationOption) error {
+ if c.bridge.IsReadOnly() {
+ return accessio.ErrReadOnly
+ }
+
+ res := &compdesc.Resource{
+ ResourceMeta: *meta.Copy(),
+ Access: acc,
+ }
+
+ ctx := c.bridge.GetContext()
+ opts := internal.NewModificationOptions(modopts...)
+ cpi.CompleteModificationOptions(ctx, opts)
+
+ spec, err := c.bridge.GetContext().AccessSpecForSpec(acc)
+ if err != nil {
+ return err
+ }
+
+ // if the blob described by the access spec has been added
+ // as local blob, just reuse the stored blob access
+ // to calculate the digest to circumvent credential problems
+ // for access specs generated by an uploader.
+ meth, err := c.AccessMethod(spec)
+ if err != nil {
+ return err
+ }
+ defer meth.Close()
+
+ return c.Execute(func() error {
+ var old *compdesc.Resource
+
+ if res.Relation == metav1.LocalRelation {
+ if res.Version == "" {
+ res.Version = c.GetVersion()
+ }
+ }
+
+ cd := c.bridge.GetDescriptor()
+ idx := cd.GetResourceIndex(&res.ResourceMeta)
+ if idx >= 0 {
+ old = &cd.Resources[idx]
+ }
+
+ if old == nil {
+ if !opts.IsModifyResource() && c.bridge.IsPersistent() {
+ return fmt.Errorf("new resource would invalidate signature")
+ }
+ }
+
+ // evaluate given digesting constraints and settings
+ hashAlgo, digester, digest := c.evaluateResourceDigest(res, old, *opts)
+ hasher := opts.GetHasher(hashAlgo)
+ if digester.HashAlgorithm == "" && 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 == "") {
+ dig, err := ctx.BlobDigesters().DetermineDigests(res.Type, hasher, opts.HasherProvider, meth, digester)
+ if err != nil {
+ return err
+ }
+ if len(dig) == 0 {
+ 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 !opts.IsSkipDigest() {
+ if digest == "" {
+ res.Digest = calculatedDigest
+ } else {
+ res.Digest = &compdesc.DigestSpec{
+ HashAlgorithm: digester.HashAlgorithm,
+ NormalisationAlgorithm: digester.NormalizationAlgorithm,
+ Value: digest,
+ }
+ }
+ }
+ }
+
+ if old != nil {
+ eq := res.Equivalent(old)
+ if !eq.IsLocalHashEqual() && c.bridge.IsPersistent() {
+ if !opts.IsModifyResource() {
+ return fmt.Errorf("resource would invalidate signature")
+ }
+ cd.Signatures = nil
+ }
+ }
+
+ if old == nil {
+ cd.Resources = append(cd.Resources, *res)
+ } else {
+ cd.Resources[idx] = *res
+ }
+ return c.bridge.Update(false)
+ })
+}
+
+// evaluateResourceDigest evaluate given potentially partly set digest to determine defaults.
+func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.Resource, opts cpi.ModificationOptions) (string, cpi.DigesterType, string) {
+ var digester cpi.DigesterType
+
+ hashAlgo := opts.DefaultHashAlgorithm
+ value := ""
+ if !res.Digest.IsNone() {
+ if res.Digest.IsComplete() {
+ value = res.Digest.Value
+ }
+ if res.Digest.HashAlgorithm != "" {
+ hashAlgo = res.Digest.HashAlgorithm
+ }
+ if res.Digest.NormalisationAlgorithm != "" {
+ digester = cpi.DigesterType{
+ HashAlgorithm: hashAlgo,
+ NormalizationAlgorithm: res.Digest.NormalisationAlgorithm,
+ }
+ }
+ }
+ res.Digest = nil
+
+ // keep potential old digest settings
+ if old != nil && old.Type == res.Type {
+ if !old.Digest.IsNone() {
+ digester.HashAlgorithm = old.Digest.HashAlgorithm
+ digester.NormalizationAlgorithm = old.Digest.NormalisationAlgorithm
+ if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.bridge.IsPersistent() {
+ res.Digest = old.Digest
+ value = old.Digest.Value
+ }
+ }
+ }
+ return hashAlgo, digester, value
+}
+
+func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess) error {
+ return setAccess(c, "source", art,
+ c.SetSource, c.SetSourceBlob)
+}
+
+func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error {
+ if c.bridge.IsReadOnly() {
+ return accessio.ErrReadOnly
+ }
+
+ res := &compdesc.Source{
+ SourceMeta: *meta.Copy(),
+ Access: acc,
+ }
+ return c.Execute(func() error {
+ if res.Version == "" {
+ res.Version = c.bridge.GetVersion()
+ }
+ cd := c.bridge.GetDescriptor()
+ if idx := cd.GetSourceIndex(&res.SourceMeta); idx == -1 {
+ cd.Sources = append(cd.Sources, *res)
+ } else {
+ cd.Sources[idx] = *res
+ }
+ return c.bridge.Update(false)
+ })
+}
+
+func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference) error {
+ return c.Execute(func() error {
+ cd := c.bridge.GetDescriptor()
+ if idx := cd.GetComponentReferenceIndex(*ref); idx == -1 {
+ cd.References = append(cd.References, *ref)
+ } else {
+ cd.References[idx] = *ref
+ }
+ return c.bridge.Update(false)
+ })
+}
+
+func (c *componentVersionAccessView) DiscardChanges() {
+ c.bridge.DiscardChanges()
+}
+
+func (c *componentVersionAccessView) IsPersistent() bool {
+ return c.bridge.IsPersistent()
+}
+
+func (c *componentVersionAccessView) UseDirectAccess() bool {
+ return c.bridge.UseDirectAccess()
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Standard Implementation for descriptor based methods
+
+func (c *componentVersionAccessView) GetResource(id metav1.Identity) (cpi.ResourceAccess, error) {
+ r, err := c.GetDescriptor().GetResourceByIdentity(id)
+ if err != nil {
+ return nil, err
+ }
+ return cpi.NewResourceAccess(c, r.Access, r.ResourceMeta), nil
+}
+
+func (c *componentVersionAccessView) GetResourceIndex(id metav1.Identity) int {
+ return c.GetDescriptor().GetResourceIndexByIdentity(id)
+}
+
+func (c *componentVersionAccessView) GetResourceByIndex(i int) (cpi.ResourceAccess, error) {
+ if i < 0 || i >= len(c.GetDescriptor().Resources) {
+ return nil, errors.ErrInvalid("resource index", strconv.Itoa(i))
+ }
+ r := c.GetDescriptor().Resources[i]
+ return cpi.NewResourceAccess(c, r.Access, r.ResourceMeta), nil
+}
+
+func (c *componentVersionAccessView) GetResourcesByName(name string, selectors ...compdesc.IdentitySelector) ([]cpi.ResourceAccess, error) {
+ resources, err := c.GetDescriptor().GetResourcesByName(name, selectors...)
+ if err != nil {
+ return nil, err
+ }
+
+ result := []cpi.ResourceAccess{}
+ for _, resource := range resources {
+ result = append(result, cpi.NewResourceAccess(c, resource.Access, resource.ResourceMeta))
+ }
+ return result, nil
+}
+
+func (c *componentVersionAccessView) GetResources() []cpi.ResourceAccess {
+ result := []cpi.ResourceAccess{}
+ for _, r := range c.GetDescriptor().Resources {
+ result = append(result, cpi.NewResourceAccess(c, r.Access, r.ResourceMeta))
+ }
+ return result
+}
+
+// GetResourcesByIdentitySelectors returns resources that match the given identity selectors.
+func (c *componentVersionAccessView) GetResourcesByIdentitySelectors(selectors ...compdesc.IdentitySelector) ([]cpi.ResourceAccess, error) {
+ return c.GetResourcesBySelectors(selectors, nil)
+}
+
+// GetResourcesByResourceSelectors returns resources that match the given resource selectors.
+func (c *componentVersionAccessView) GetResourcesByResourceSelectors(selectors ...compdesc.ResourceSelector) ([]cpi.ResourceAccess, error) {
+ return c.GetResourcesBySelectors(nil, selectors)
+}
+
+// GetResourcesBySelectors returns resources that match the given selector.
+func (c *componentVersionAccessView) GetResourcesBySelectors(selectors []compdesc.IdentitySelector, resourceSelectors []compdesc.ResourceSelector) ([]cpi.ResourceAccess, error) {
+ resources := make([]cpi.ResourceAccess, 0)
+ rscs := c.GetDescriptor().Resources
+ for i := range rscs {
+ selctx := compdesc.NewResourceSelectionContext(i, rscs)
+ if len(selectors) > 0 {
+ ok, err := selector.MatchSelectors(selctx.Identity(), selectors...)
+ if err != nil {
+ return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
+ }
+ if !ok {
+ continue
+ }
+ }
+ ok, err := compdesc.MatchResourceByResourceSelector(selctx, resourceSelectors...)
+ if err != nil {
+ return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
+ }
+ if !ok {
+ continue
+ }
+ r, err := c.GetResourceByIndex(i)
+ if err != nil {
+ return nil, err
+ }
+ resources = append(resources, r)
+ }
+ if len(resources) == 0 {
+ return resources, compdesc.NotFound
+ }
+ return resources, nil
+}
+
+func (c *componentVersionAccessView) GetSource(id metav1.Identity) (cpi.SourceAccess, error) {
+ r, err := c.GetDescriptor().GetSourceByIdentity(id)
+ if err != nil {
+ return nil, err
+ }
+ return cpi.NewSourceAccess(c, r.Access, r.SourceMeta), nil
+}
+
+func (c *componentVersionAccessView) GetSourceIndex(id metav1.Identity) int {
+ return c.GetDescriptor().GetSourceIndexByIdentity(id)
+}
+
+func (c *componentVersionAccessView) GetSourceByIndex(i int) (cpi.SourceAccess, error) {
+ if i < 0 || i >= len(c.GetDescriptor().Sources) {
+ return nil, errors.ErrInvalid("source index", strconv.Itoa(i))
+ }
+ r := c.GetDescriptor().Sources[i]
+ return cpi.NewSourceAccess(c, r.Access, r.SourceMeta), nil
+}
+
+func (c *componentVersionAccessView) GetSources() []cpi.SourceAccess {
+ result := []cpi.SourceAccess{}
+ for _, r := range c.GetDescriptor().Sources {
+ result = append(result, cpi.NewSourceAccess(c, r.Access, r.SourceMeta))
+ }
+ return result
+}
+
+func (c *componentVersionAccessView) GetReferences() compdesc.References {
+ return c.GetDescriptor().References
+}
+
+func (c *componentVersionAccessView) GetReference(id metav1.Identity) (cpi.ComponentReference, error) {
+ return c.GetDescriptor().GetReferenceByIdentity(id)
+}
+
+func (c *componentVersionAccessView) GetReferenceIndex(id metav1.Identity) int {
+ return c.GetDescriptor().GetReferenceIndexByIdentity(id)
+}
+
+func (c *componentVersionAccessView) GetReferenceByIndex(i int) (cpi.ComponentReference, error) {
+ if i < 0 || i > len(c.GetDescriptor().References) {
+ return cpi.ComponentReference{}, errors.ErrInvalid("reference index", strconv.Itoa(i))
+ }
+ return c.GetDescriptor().References[i], nil
+}
+
+func (c *componentVersionAccessView) GetReferencesByName(name string, selectors ...compdesc.IdentitySelector) (compdesc.References, error) {
+ return c.GetDescriptor().GetReferencesByName(name, selectors...)
+}
+
+// GetReferencesByIdentitySelectors returns references that match the given identity selectors.
+func (c *componentVersionAccessView) GetReferencesByIdentitySelectors(selectors ...compdesc.IdentitySelector) (compdesc.References, error) {
+ return c.GetReferencesBySelectors(selectors, nil)
+}
+
+// GetReferencesByReferenceSelectors returns references that match the given resource selectors.
+func (c *componentVersionAccessView) GetReferencesByReferenceSelectors(selectors ...compdesc.ReferenceSelector) (compdesc.References, error) {
+ return c.GetReferencesBySelectors(nil, selectors)
+}
+
+// GetReferencesBySelectors returns references that match the given selector.
+func (c *componentVersionAccessView) GetReferencesBySelectors(selectors []compdesc.IdentitySelector, referenceSelectors []compdesc.ReferenceSelector) (compdesc.References, error) {
+ references := make(compdesc.References, 0)
+ refs := c.GetDescriptor().References
+ for i := range refs {
+ selctx := compdesc.NewReferenceSelectionContext(i, refs)
+ if len(selectors) > 0 {
+ ok, err := selector.MatchSelectors(selctx.Identity(), selectors...)
+ if err != nil {
+ return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
+ }
+ if !ok {
+ continue
+ }
+ }
+ ok, err := compdesc.MatchReferencesByReferenceSelector(selctx, referenceSelectors...)
+ if err != nil {
+ return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
+ }
+ if !ok {
+ continue
+ }
+ references = append(references, *selctx.ComponentReference)
+ }
+ if len(references) == 0 {
+ return references, compdesc.NotFound
+ }
+ return references, nil
+}
diff --git a/pkg/contexts/ocm/cpi/repocpi/view_r.go b/pkg/contexts/ocm/cpi/repocpi/view_r.go
new file mode 100644
index 0000000000..3c94f10fb6
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/repocpi/view_r.go
@@ -0,0 +1,166 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package repocpi
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/open-component-model/ocm/pkg/contexts/credentials"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/refmgmt/resource"
+ "github.com/open-component-model/ocm/pkg/utils"
+)
+
+// View objects are the user facing generic implementations of the context interfaces.
+// They are responsible to handle the reference counting and use
+// shared implementations objects for th concrete type-specific implementations.
+// Additionally, they are used to implement interface functionality which is
+// common to all implementations and NOT dependent on the backend system technology.
+
+////////////////////////////////////////////////////////////////////////////////
+
+type _repositoryView interface {
+ resource.ResourceViewInt[cpi.Repository] // here you have to redeclare
+}
+
+type RepositoryViewManager = resource.ViewManager[cpi.Repository] // here you have to use an alias
+
+type RepositoryBridge interface {
+ resource.ResourceImplementation[cpi.Repository]
+
+ GetContext() cpi.Context
+
+ GetSpecification() cpi.RepositorySpec
+ ComponentLister() cpi.ComponentLister
+
+ ExistsComponentVersion(name string, version string) (bool, error)
+ LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error)
+ LookupComponent(name string) (cpi.ComponentAccess, error)
+
+ io.Closer
+}
+
+type repositoryView struct {
+ _repositoryView
+ bridge RepositoryBridge
+}
+
+var (
+ _ cpi.Repository = (*repositoryView)(nil)
+ _ credentials.ConsumerIdentityProvider = (*repositoryView)(nil)
+ _ utils.Unwrappable = (*repositoryView)(nil)
+)
+
+func GetRepositoryBridge(n cpi.Repository) (RepositoryBridge, error) {
+ if v, ok := n.(*repositoryView); ok {
+ return v.bridge, nil
+ }
+ return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n))
+}
+
+func GetRepositoryImplementation(n cpi.Repository) (RepositoryImpl, error) {
+ if v, ok := n.(*repositoryView); ok {
+ if b, ok := v.bridge.(*repositoryBridge); ok {
+ return b.impl, nil
+ }
+ return nil, errors.ErrNotSupported("repository base type", fmt.Sprintf("%T", v.bridge))
+ }
+ return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n))
+}
+
+func repositoryViewCreator(i RepositoryBridge, v resource.CloserView, d RepositoryViewManager) cpi.Repository {
+ return &repositoryView{
+ _repositoryView: resource.NewView[cpi.Repository](v, d),
+ bridge: i,
+ }
+}
+
+// NewNoneRefRepositoryView provides a repository reflecting the state of the
+// view manager without holding an additional reference.
+func NewNoneRefRepositoryView(i RepositoryBridge) cpi.Repository {
+ return &repositoryView{
+ _repositoryView: resource.NewView[cpi.Repository](resource.NewNonRefView[cpi.Repository](i), i),
+ bridge: i,
+ }
+}
+
+func NewRepository(impl RepositoryImpl, kind string, closer ...io.Closer) cpi.Repository {
+ bridge := newRepositoryBridge(impl, kind, closer...)
+ if kind == "" {
+ kind = "OCM repository"
+ }
+ return resource.NewResource[cpi.Repository](bridge, repositoryViewCreator, kind, true)
+}
+
+func (r *repositoryView) Unwrap() interface{} {
+ return r.bridge
+}
+
+func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity {
+ return credentials.GetProvidedConsumerId(r.bridge, uctx...)
+}
+
+func (r *repositoryView) GetIdentityMatcher() string {
+ return credentials.GetProvidedIdentityMatcher(r.bridge)
+}
+
+func (r *repositoryView) GetSpecification() cpi.RepositorySpec {
+ return r.bridge.GetSpecification()
+}
+
+func (r *repositoryView) GetContext() cpi.Context {
+ return r.bridge.GetContext()
+}
+
+func (r *repositoryView) ComponentLister() cpi.ComponentLister {
+ return r.bridge.ComponentLister()
+}
+
+func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) {
+ err = r.Execute(func() error {
+ ok, err = r.bridge.ExistsComponentVersion(name, version)
+ return err
+ })
+ return ok, err
+}
+
+func (r *repositoryView) LookupComponentVersion(name string, version string) (acc cpi.ComponentVersionAccess, err error) {
+ err = r.Execute(func() error {
+ acc, err = r.bridge.LookupComponentVersion(name, version)
+ return err
+ })
+ return acc, err
+}
+
+func (r *repositoryView) LookupComponent(name string) (acc cpi.ComponentAccess, err error) {
+ err = r.Execute(func() error {
+ acc, err = r.bridge.LookupComponent(name)
+ return err
+ })
+ return acc, err
+}
+
+func (r *repositoryView) NewComponentVersion(comp, vers string, overrides ...bool) (cpi.ComponentVersionAccess, error) {
+ c, err := refmgmt.ToLazy(r.LookupComponent(comp))
+ if err != nil {
+ return nil, err
+ }
+ defer c.Close()
+
+ return c.NewVersion(vers, overrides...)
+}
+
+func (r *repositoryView) AddComponentVersion(cv cpi.ComponentVersionAccess, overrides ...bool) error {
+ c, err := refmgmt.ToLazy(r.LookupComponent(cv.GetName()))
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ return c.AddVersion(cv, overrides...)
+}
diff --git a/pkg/contexts/ocm/cpi/support/compversaccess.go b/pkg/contexts/ocm/cpi/support/compversaccess.go
deleted file mode 100644
index 962d6180e5..0000000000
--- a/pkg/contexts/ocm/cpi/support/compversaccess.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package support
-
-import (
- "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/errors"
- "github.com/open-component-model/ocm/pkg/refmgmt"
-)
-
-type _ComponentVersionAccessImplBase = cpi.ComponentVersionAccessImplBase
-
-type ComponentVersionAccessImpl interface {
- cpi.ComponentVersionAccessImpl
- EnablePersistence() bool
-}
-
-type componentVersionAccessImpl struct {
- *_ComponentVersionAccessImplBase
- lazy bool
- directAccess bool
- persistent bool
- discardChanges bool
- base ComponentVersionContainer
-}
-
-var _ ComponentVersionAccessImpl = (*componentVersionAccessImpl)(nil)
-
-func GetComponentVersionContainer[T ComponentVersionContainer](cv cpi.ComponentVersionAccess) (T, error) {
- var _nil T
-
- impl, err := cpi.GetComponentVersionAccessImplementation(cv)
- if err != nil {
- return _nil, err
- }
- if mine, ok := impl.(*componentVersionAccessImpl); ok {
- cont, ok := mine.base.(T)
- if ok {
- return cont, nil
- }
- return _nil, errors.Newf("non-matching component version implementation %T", mine.base)
- }
- return _nil, errors.Newf("non-matching component version implementation %T", impl)
-}
-
-func NewComponentVersionAccessImpl(name, version string, container ComponentVersionContainer, lazy, persistent, direct bool) (cpi.ComponentVersionAccessImpl, error) {
- base, err := cpi.NewComponentVersionAccessImplBase(container.GetContext(), name, version, container.GetParentViewManager())
- if err != nil {
- return nil, err
- }
- impl := &componentVersionAccessImpl{
- _ComponentVersionAccessImplBase: base,
- lazy: lazy,
- persistent: persistent,
- directAccess: direct,
- base: container,
- }
- container.SetImplementation(impl)
- return impl, nil
-}
-
-func (a *componentVersionAccessImpl) EnablePersistence() bool {
- if a.discardChanges {
- return false
- }
- a.persistent = true
- a.GetStorageContext()
- return true
-}
-
-func (a *componentVersionAccessImpl) IsPersistent() bool {
- return a.persistent
-}
-
-func (d *componentVersionAccessImpl) UseDirectAccess() bool {
- return d.directAccess
-}
-
-func (a *componentVersionAccessImpl) DiscardChanges() {
- a.discardChanges = true
-}
-
-func (a *componentVersionAccessImpl) Close() error {
- list := errors.ErrListf("closing component version access %s/%s", a.GetName(), a.GetVersion())
- return list.Add(a.base.Close(), a._ComponentVersionAccessImplBase.Close()).Result()
-}
-
-func (a *componentVersionAccessImpl) Repository() cpi.Repository {
- return a.base.Repository()
-}
-
-func (a *componentVersionAccessImpl) IsReadOnly() bool {
- return a.base.IsReadOnly()
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// with access to actual view
-
-func (a *componentVersionAccessImpl) AccessMethod(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error) {
- return a.base.AccessMethod(acc, cv)
-}
-
-func (a *componentVersionAccessImpl) GetInexpensiveContentVersionIdentity(acc cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string {
- return a.base.GetInexpensiveContentVersionIdentity(acc, cv)
-}
-
-func (a *componentVersionAccessImpl) GetDescriptor() *compdesc.ComponentDescriptor {
- return a.base.GetDescriptor()
-}
-
-func (a *componentVersionAccessImpl) GetStorageContext() cpi.StorageContext {
- return a.base.GetStorageContext()
-}
-
-func (a *componentVersionAccessImpl) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
- return a.base.AddBlobFor(blob, refName, global)
-}
-
-func (a *componentVersionAccessImpl) ShouldUpdate(final bool) bool {
- if a.discardChanges {
- return false
- }
- if final {
- return a.persistent
- }
- return !a.lazy && a.directAccess && a.persistent
-}
-
-func (a *componentVersionAccessImpl) Update(final bool) error {
- if a.ShouldUpdate(final) {
- return a.base.Update()
- }
- return nil
-}
diff --git a/pkg/contexts/ocm/cpi/support/container.go b/pkg/contexts/ocm/cpi/support/container.go
deleted file mode 100644
index df9a1bc895..0000000000
--- a/pkg/contexts/ocm/cpi/support/container.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package support
-
-import (
- "io"
-
- "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/refmgmt"
-)
-
-// BlobContainer is the interface for an element capable to store blobs.
-type BlobContainer interface {
- GetBlobData(name string) (cpi.DataAccess, error)
-
- // GetStorageContext creates a storage context for blobs
- // that is used to feed blob handlers for specific blob storage methods.
- // If no handler accepts the blob, the AddBlobFor method will
- // be used to store the blob
- GetStorageContext() cpi.StorageContext
-
- // AddBlobFor stores a local blob together with the component and
- // potentially provides a global reference according to the OCI distribution spec
- // if the blob described an oci artifact.
- // The resulting access information (global and local) is provided as
- // an access method specification usable in a component descriptor.
- // This is the direct technical storage, without caring about any handler.
- AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error)
-}
-
-// ComponentVersionContainer is the interface of an element hosting a component version.
-type ComponentVersionContainer interface {
- SetImplementation(impl ComponentVersionAccessImpl)
-
- GetParentViewManager() cpi.ComponentAccessViewManager
-
- GetContext() cpi.Context
- Repository() cpi.Repository
-
- IsReadOnly() bool
- Update() error
-
- GetDescriptor() *compdesc.ComponentDescriptor
- BlobContainer
- AccessMethod(a cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) (cpi.AccessMethod, error)
- GetInexpensiveContentVersionIdentity(a cpi.AccessSpec, cv refmgmt.ExtendedAllocatable) string
-
- io.Closer
-}
diff --git a/pkg/contexts/ocm/cpi/support/doc.go b/pkg/contexts/ocm/cpi/support/doc.go
deleted file mode 100644
index de614ae9a4..0000000000
--- a/pkg/contexts/ocm/cpi/support/doc.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
-//
-// SPDX-License-Identifier: Apache-2.0
-
-/*
-Package support provides a standard implementation for the object type set
-required to implement the OCM repository interface.
-
-This implementation is based on three interfaces that have to implemented:
-
- - BlobContainer
- is used to provide access to blob data
- - ComponentVersionContainer
- is used to provide access to component version for component.
-
-The function NewComponentVersionAccessImpl can be used to create an
-object implementing the complete ComponentVersionAccess contract.
-*/
-package support
diff --git a/pkg/contexts/ocm/cpi/support/error.go b/pkg/contexts/ocm/cpi/support/error.go
deleted file mode 100644
index 56b12f7e41..0000000000
--- a/pkg/contexts/ocm/cpi/support/error.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package support
-
-import "fmt"
-
-type UpdateComponentVersionContainerError struct {
- Name string
- Version string
-
- Original error
-}
-
-func (e UpdateComponentVersionContainerError) Error() string {
- message := fmt.Sprintf(
- "unable to update '%s:%s' base component container",
- e.Name,
- e.Version,
- )
-
- if e.Original != nil {
- message = fmt.Sprintf("%s: %s", message, e.Original.Error())
- }
-
- return message
-}
-
-func (e UpdateComponentVersionContainerError) Unwrap() error {
- return e.Original
-}
-
-type AccessCheckError struct {
- Name string
- Version string
- Type string
-
- Original error
-}
-
-func (e AccessCheckError) Error() string {
- message := fmt.Sprintf(
- "failed access spec check on '%s:%s' with type '%s'",
- e.Name,
- e.Version,
- e.Type,
- )
-
- if e.Original != nil {
- message = fmt.Sprintf("%s: %s", message, e.Original.Error())
- }
-
- return message
-}
-
-func (e AccessCheckError) Unwrap() error {
- return e.Original
-}
diff --git a/pkg/contexts/ocm/cpi/view.go b/pkg/contexts/ocm/cpi/view.go
deleted file mode 100644
index 490d294e3d..0000000000
--- a/pkg/contexts/ocm/cpi/view.go
+++ /dev/null
@@ -1,1425 +0,0 @@
-// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
-//
-// SPDX-License-Identifier: Apache-2.0
-
-package cpi
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "strconv"
- "sync"
-
- "github.com/opencontainers/go-digest"
-
- "github.com/open-component-model/ocm/pkg/blobaccess"
- "github.com/open-component-model/ocm/pkg/common"
- "github.com/open-component-model/ocm/pkg/common/accessio"
- "github.com/open-component-model/ocm/pkg/contexts/credentials"
- "github.com/open-component-model/ocm/pkg/contexts/oci/cpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/compose"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/keepblobattr"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
- metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/internal"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor"
- "github.com/open-component-model/ocm/pkg/errors"
- "github.com/open-component-model/ocm/pkg/finalizer"
- "github.com/open-component-model/ocm/pkg/refmgmt"
- "github.com/open-component-model/ocm/pkg/refmgmt/resource"
- "github.com/open-component-model/ocm/pkg/utils"
- "github.com/open-component-model/ocm/pkg/utils/selector"
-)
-
-// View objects are the user facing generic implementations of the context interfaces.
-// They are responsible to handle the reference counting and use
-// shared implementations objects for th concrete type-specific implementations.
-// Additionally, they are used to implement interface functionality which is
-// common to all implementations and NOT dependent on the backend system technology.
-
-var (
- ErrClosed = resource.ErrClosed
- ErrTempVersion = fmt.Errorf("temporary component version cannot be updated")
-)
-
-////////////////////////////////////////////////////////////////////////////////
-
-type _RepositoryView interface {
- resource.ResourceViewInt[Repository] // here you have to redeclare
-}
-
-type RepositoryViewManager = resource.ViewManager[Repository] // here you have to use an alias
-
-type RepositoryImpl interface {
- resource.ResourceImplementation[Repository]
- internal.RepositoryImpl
-}
-
-type _RepositoryImplBase = resource.ResourceImplBase[Repository]
-
-type RepositoryImplBase struct {
- _RepositoryImplBase
- ctx Context
-}
-
-func (b *RepositoryImplBase) GetContext() Context {
- return b.ctx
-}
-
-func NewRepositoryImplBase(ctx Context, closer ...io.Closer) *RepositoryImplBase {
- base, _ := resource.NewResourceImplBase[Repository, io.Closer](nil, closer...)
- return &RepositoryImplBase{
- _RepositoryImplBase: *base,
- ctx: ctx,
- }
-}
-
-type repositoryView struct {
- _RepositoryView
- impl RepositoryImpl
-}
-
-var (
- _ Repository = (*repositoryView)(nil)
- _ credentials.ConsumerIdentityProvider = (*repositoryView)(nil)
- _ utils.Unwrappable = (*repositoryView)(nil)
-)
-
-func GetRepositoryImplementation(n Repository) (RepositoryImpl, error) {
- if v, ok := n.(*repositoryView); ok {
- return v.impl, nil
- }
- return nil, errors.ErrNotSupported("repository implementation type", fmt.Sprintf("%T", n))
-}
-
-func repositoryViewCreator(i RepositoryImpl, v resource.CloserView, d RepositoryViewManager) Repository {
- return &repositoryView{
- _RepositoryView: resource.NewView[Repository](v, d),
- impl: i,
- }
-}
-
-// NewNoneRefRepositoryView provides a repository reflecting the state of the
-// view manager without holding an additional reference.
-func NewNoneRefRepositoryView(i RepositoryImpl) Repository {
- return &repositoryView{
- _RepositoryView: resource.NewView[Repository](resource.NewNonRefView[Repository](i), i),
- impl: i,
- }
-}
-
-func NewRepository(impl RepositoryImpl, name ...string) Repository {
- return resource.NewResource[Repository](impl, repositoryViewCreator, utils.OptionalDefaulted("OCM repo", name...), true)
-}
-
-func (r *repositoryView) Unwrap() interface{} {
- return r.impl
-}
-
-func (r *repositoryView) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity {
- return credentials.GetProvidedConsumerId(r.impl, uctx...)
-}
-
-func (r *repositoryView) GetIdentityMatcher() string {
- return credentials.GetProvidedIdentityMatcher(r.impl)
-}
-
-func (r *repositoryView) GetSpecification() RepositorySpec {
- return r.impl.GetSpecification()
-}
-
-func (r *repositoryView) GetContext() Context {
- return r.impl.GetContext()
-}
-
-func (r *repositoryView) ComponentLister() ComponentLister {
- return r.impl.ComponentLister()
-}
-
-func (r *repositoryView) ExistsComponentVersion(name string, version string) (ok bool, err error) {
- err = r.Execute(func() error {
- ok, err = r.impl.ExistsComponentVersion(name, version)
- return err
- })
- return ok, err
-}
-
-func (r *repositoryView) LookupComponentVersion(name string, version string) (acc ComponentVersionAccess, err error) {
- err = r.Execute(func() error {
- acc, err = r.impl.LookupComponentVersion(name, version)
- return err
- })
- return acc, err
-}
-
-func (r *repositoryView) LookupComponent(name string) (acc ComponentAccess, err error) {
- err = r.Execute(func() error {
- acc, err = r.impl.LookupComponent(name)
- return err
- })
- return acc, err
-}
-
-func (r *repositoryView) NewComponentVersion(comp, vers string, overrides ...bool) (ComponentVersionAccess, error) {
- c, err := refmgmt.ToLazy(r.LookupComponent(comp))
- if err != nil {
- return nil, err
- }
- defer c.Close()
-
- return c.NewVersion(vers, overrides...)
-}
-
-func (r *repositoryView) AddComponentVersion(cv ComponentVersionAccess, overrides ...bool) error {
- c, err := refmgmt.ToLazy(r.LookupComponent(cv.GetName()))
- if err != nil {
- return err
- }
- defer c.Close()
-
- return c.AddVersion(cv, overrides...)
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-type _ComponentAccessView interface {
- resource.ResourceViewInt[ComponentAccess] // here you have to redeclare
-}
-
-type ComponentAccessViewManager = resource.ViewManager[ComponentAccess] // here you have to use an alias
-
-type ComponentAccessImpl interface {
- resource.ResourceImplementation[ComponentAccess]
- internal.ComponentAccessImpl
-
- IsReadOnly() bool
- GetName() string
-
- IsOwned(access ComponentVersionAccess) bool
-
- AddVersion(cv ComponentVersionAccess) error
-}
-
-type _ComponentAccessImplBase = resource.ResourceImplBase[ComponentAccess]
-
-type ComponentAccessImplBase struct {
- *_ComponentAccessImplBase
- ctx Context
- name string
-}
-
-func NewComponentAccessImplBase(ctx Context, name string, repo RepositoryViewManager, closer ...io.Closer) (*ComponentAccessImplBase, error) {
- base, err := resource.NewResourceImplBase[ComponentAccess](repo, closer...)
- if err != nil {
- return nil, err
- }
- return &ComponentAccessImplBase{
- _ComponentAccessImplBase: base,
- ctx: ctx,
- name: name,
- }, nil
-}
-
-func (b *ComponentAccessImplBase) GetContext() Context {
- return b.ctx
-}
-
-func (b *ComponentAccessImplBase) GetName() string {
- return b.name
-}
-
-type componentAccessView struct {
- _ComponentAccessView
- impl ComponentAccessImpl
-}
-
-var (
- _ ComponentAccess = (*componentAccessView)(nil)
- _ utils.Unwrappable = (*componentAccessView)(nil)
-)
-
-func GetComponentAccessImplementation(n ComponentAccess) (ComponentAccessImpl, error) {
- if v, ok := n.(*componentAccessView); ok {
- return v.impl, nil
- }
- return nil, errors.ErrNotSupported("component implementation type", fmt.Sprintf("%T", n))
-}
-
-func componentAccessViewCreator(i ComponentAccessImpl, v resource.CloserView, d ComponentAccessViewManager) ComponentAccess {
- return &componentAccessView{
- _ComponentAccessView: resource.NewView[ComponentAccess](v, d),
- impl: i,
- }
-}
-
-func NewComponentAccess(impl ComponentAccessImpl, kind ...string) ComponentAccess {
- return resource.NewResource[ComponentAccess](impl, componentAccessViewCreator, fmt.Sprintf("%s %s", utils.OptionalDefaulted("component", kind...), impl.GetName()), true)
-}
-
-func (c *componentAccessView) Unwrap() interface{} {
- return c.impl
-}
-
-func (c *componentAccessView) GetContext() Context {
- return c.impl.GetContext()
-}
-
-func (c *componentAccessView) GetName() string {
- return c.impl.GetName()
-}
-
-func (c *componentAccessView) ListVersions() (list []string, err error) {
- err = c.Execute(func() error {
- list, err = c.impl.ListVersions()
- return err
- })
- return list, err
-}
-
-func (c *componentAccessView) LookupVersion(version string) (acc ComponentVersionAccess, err error) {
- err = c.Execute(func() error {
- acc, err = c.impl.LookupVersion(version)
- return err
- })
- return acc, err
-}
-
-func (c *componentAccessView) AddVersion(acc ComponentVersionAccess, overrides ...bool) error {
- if acc.GetName() != c.GetName() {
- return errors.ErrInvalid("component name", acc.GetName())
- }
- return c.Execute(func() error {
- return c.addVersion(acc, overrides...)
- })
-}
-
-func (c *componentAccessView) addVersion(acc ComponentVersionAccess, overrides ...bool) (ferr error) {
- var finalize finalizer.Finalizer
- defer finalize.FinalizeWithErrorPropagation(&ferr)
-
- ctx := acc.GetContext()
-
- impl, err := GetComponentVersionAccessImplementation(acc)
- if err != nil {
- return err
- }
-
- var (
- d *compdesc.ComponentDescriptor
- sel func(AccessSpec) bool
- eff ComponentVersionAccess
- )
-
- opts := NewBlobUploadOptions()
-
- forcestore := c.impl.IsOwned(acc)
- if !forcestore {
- // transfer all local blobs into a new owned version.
- sel = func(spec AccessSpec) bool { return spec.IsLocal(ctx) }
-
- eff, err = c.impl.NewVersion(acc.GetVersion(), overrides...)
- if err != nil {
- return err
- }
- finalize.With(func() error {
- return eff.Close()
- })
- impl, err = GetComponentVersionAccessImplementation(eff)
- if err != nil {
- return err
- }
-
- d = eff.GetDescriptor()
- *d = *acc.GetDescriptor().Copy()
- } else {
- // transfer composition blobs into local blobs
- opts.UseNoDefaultIfNotSet = true
- opts.BlobHandlerProvider = nil
- sel = compose.Is
- d = acc.GetDescriptor()
- eff = acc
- }
-
- err = setupLocalBobs(ctx, "resource", acc, nil, impl, d.Resources, sel, forcestore, opts)
- if err == nil {
- err = setupLocalBobs(ctx, "source", acc, nil, impl, d.Sources, sel, forcestore, opts)
- }
- if err != nil {
- return err
- }
-
- return c.impl.AddVersion(eff)
-}
-
-func setupLocalBobs(ctx Context, kind string, src ComponentVersionAccess, accprov func(AccessSpec) (AccessMethod, error), tgtimpl ComponentVersionAccessImpl, it compdesc.ArtifactAccessor, sel func(AccessSpec) bool, forcestore bool, opts *BlobUploadOptions) (ferr error) {
- var finalize finalizer.Finalizer
- defer finalize.FinalizeWithErrorPropagation(&ferr)
-
- for i := 0; i < it.Len(); i++ {
- nested := finalize.Nested()
- a := it.GetArtifact(i)
- spec, err := ctx.AccessSpecForSpec(a.GetAccess())
- if err != nil {
- return errors.Wrapf(err, "%s %d", kind, i)
- }
- if sel(spec) {
- blob, err := blobAccessForLocalAccessSpec(spec, src, accprov)
- if err != nil {
- return errors.Wrapf(err, "%s %d", kind, i)
- }
- nested.Close(blob)
-
- var effspec AccessSpec
- if forcestore {
- effspec, err = tgtimpl.AddBlobFor(blob, ReferenceHint(spec, src), GlobalAccess(spec, ctx))
- } else {
- effspec, err = addBlob(tgtimpl, a.GetType(), ReferenceHint(spec, src), blob, GlobalAccess(spec, ctx))
- }
- if err != nil {
- return errors.Wrapf(err, "cannot store %s %d", kind, i)
- }
- a.SetAccess(effspec)
- }
- err = nested.Finalize()
- if err != nil {
- return errors.Wrapf(err, "%s %d", kind, i)
- }
- }
- return nil
-}
-
-func blobAccessForLocalAccessSpec(spec AccessSpec, cv ComponentVersionAccess, accprov func(AccessSpec) (AccessMethod, error)) (blobaccess.BlobAccess, error) {
- var m AccessMethod
- var err error
- if accprov != nil {
- m, err = accprov(spec)
- } else {
- m, err = spec.AccessMethod(cv)
- }
- if err != nil {
- return nil, err
- }
- return m.AsBlobAccess(), nil
-}
-
-func (c *componentAccessView) NewVersion(version string, overrides ...bool) (acc ComponentVersionAccess, err error) {
- err = c.Execute(func() error {
- if c.impl.IsReadOnly() {
- return accessio.ErrReadOnly
- }
- acc, err = c.impl.NewVersion(version, overrides...)
- return err
- })
- return acc, err
-}
-
-func (c *componentAccessView) HasVersion(vers string) (ok bool, err error) {
- err = c.Execute(func() error {
- ok, err = c.impl.HasVersion(vers)
- return err
- })
- return ok, err
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-type _ComponentVersionAccessView interface {
- resource.ResourceViewInt[ComponentVersionAccess]
-}
-
-type ComponentVersionAccessViewManager = resource.ViewManager[ComponentVersionAccess]
-
-type ComponentVersionAccessImpl interface {
- resource.ResourceImplementation[ComponentVersionAccess]
- common.VersionedElement
- io.Closer
-
- GetContext() Context
- Repository() Repository
-
- DiscardChanges()
- IsPersistent() bool
-
- GetDescriptor() *compdesc.ComponentDescriptor
-
- AccessMethod(AccessSpec, refmgmt.ExtendedAllocatable) (AccessMethod, error)
- GetInexpensiveContentVersionIdentity(AccessSpec, refmgmt.ExtendedAllocatable) string
-
- // GetStorageContext creates a storage context for blobs
- // that is used to feed blob handlers for specific blob storage methods.
- // If no handler accepts the blob, the AddBlobFor method will
- // be used to store the blob
- GetStorageContext() StorageContext
-
- // AddBlobFor stores a local blob together with the component and
- // potentially provides a global reference.
- // The resulting access information (global and local) is provided as
- // an access method specification usable in a component descriptor.
- // This is the direct technical storage, without caring about any handler.
- AddBlobFor(blob BlobAccess, refName string, global AccessSpec) (AccessSpec, error)
-
- IsReadOnly() bool
-
- // ShouldUpdate checks, whether an update is indicated
- // by the state of object, considering persistence, lazy, discard
- // and update mode state
- ShouldUpdate(final bool) bool
-
- // GetBlobCache retieves the blob cache used to store preliminary
- // blob accesses for freshly generated local access specs not directly
- // usable until a component version is finally added to the repository.
- GetBlobCache() BlobCache
-
- // UseDirectAccess returns true if composition should be directly
- // forwarded to the repository backend.,
- UseDirectAccess() bool
-
- // Update persists the current state of the component version to the
- // underlying repository backend.
- Update(final bool) error
-}
-
-type (
- BlobCacheEntry = blobaccess.BlobAccess
- BlobCacheKey = interface{}
-)
-
-type BlobCache interface {
- // AddBlobFor stores blobs for added blobs not yet accessible
- // by generated access method until version is finally added.
- AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error
-
- // GetBlobFor retrieves the original blob access for
- // a given access specification.
- GetBlobFor(acc BlobCacheKey) BlobCacheEntry
-
- RemoveBlobFor(acc BlobCacheKey)
- Clear() error
-}
-
-type blobCache struct {
- lock sync.Mutex
- blobcache map[BlobCacheKey]BlobCacheEntry
-}
-
-func NewBlobCache() BlobCache {
- return &blobCache{
- blobcache: map[BlobCacheKey]BlobCacheEntry{},
- }
-}
-
-func (c *blobCache) RemoveBlobFor(acc BlobCacheKey) {
- c.lock.Lock()
- defer c.lock.Unlock()
- if b := c.blobcache[acc]; b != nil {
- b.Close()
- delete(c.blobcache, acc)
- }
-}
-
-func (c *blobCache) AddBlobFor(acc BlobCacheKey, blob BlobCacheEntry) error {
- if s, ok := acc.(string); ok && s == "" {
- return errors.ErrInvalid("blob key")
- }
- c.lock.Lock()
- defer c.lock.Unlock()
-
- if c.blobcache[acc] == nil {
- l, err := blob.Dup()
- if err != nil {
- return err
- }
- c.blobcache[acc] = l
- }
- return nil
-}
-
-func (c *blobCache) GetBlobFor(acc BlobCacheKey) BlobCacheEntry {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- return c.blobcache[acc]
-}
-
-func (c *blobCache) Clear() error {
- list := errors.ErrList()
- c.lock.Lock()
- defer c.lock.Unlock()
- for _, b := range c.blobcache {
- list.Add(b.Close())
- }
- c.blobcache = map[BlobCacheKey]BlobCacheEntry{}
- return list.Result()
-}
-
-type _ComponentVersionAccessImplBase = resource.ResourceImplBase[ComponentVersionAccess]
-
-type ComponentVersionAccessImplBase struct {
- *_ComponentVersionAccessImplBase
- ctx Context
- name string
- version string
-
- blobcache BlobCache
-}
-
-func NewComponentVersionAccessImplBase(ctx Context, name, version string, repo ComponentAccessViewManager, closer ...io.Closer) (*ComponentVersionAccessImplBase, error) {
- base, err := resource.NewResourceImplBase[ComponentVersionAccess](repo, closer...)
- if err != nil {
- return nil, err
- }
- return &ComponentVersionAccessImplBase{
- _ComponentVersionAccessImplBase: base,
- ctx: ctx,
- name: name,
- version: version,
- blobcache: NewBlobCache(),
- }, nil
-}
-
-func (b *ComponentVersionAccessImplBase) Close() error {
- list := errors.ErrListf("closing %s", common.VersionedElementKey(b))
- list.Add(b._ComponentVersionAccessImplBase.Close())
- list.Add(b.blobcache.Clear())
- return list.Result()
-}
-
-func (b *ComponentVersionAccessImplBase) GetContext() Context {
- return b.ctx
-}
-
-func (b *ComponentVersionAccessImplBase) GetName() string {
- return b.name
-}
-
-func (b *ComponentVersionAccessImplBase) GetVersion() string {
- return b.version
-}
-
-func (b *ComponentVersionAccessImplBase) GetBlobCache() BlobCache {
- return b.blobcache
-}
-
-type componentVersionAccessView struct {
- _ComponentVersionAccessView
- impl ComponentVersionAccessImpl
- err error
-}
-
-var (
- _ ComponentVersionAccess = (*componentVersionAccessView)(nil)
- _ utils.Unwrappable = (*componentVersionAccessView)(nil)
-)
-
-func GetComponentVersionAccessImplementation(n ComponentVersionAccess) (ComponentVersionAccessImpl, error) {
- if v, ok := n.(*componentVersionAccessView); ok {
- return v.impl, nil
- }
- return nil, errors.ErrNotSupported("component version implementation type", fmt.Sprintf("%T", n))
-}
-
-func artifactAccessViewCreator(i ComponentVersionAccessImpl, v resource.CloserView, d resource.ViewManager[ComponentVersionAccess]) ComponentVersionAccess {
- cv := &componentVersionAccessView{
- _ComponentVersionAccessView: resource.NewView[ComponentVersionAccess](v, d),
- impl: i,
- }
- v.Allocatable().BeforeCleanup(refmgmt.CleanupHandlerFunc(cv.finish))
- return cv
-}
-
-func NewComponentVersionAccess(impl ComponentVersionAccessImpl) ComponentVersionAccess {
- return resource.NewResource[ComponentVersionAccess](impl, artifactAccessViewCreator, fmt.Sprintf("component version %s/%s", impl.GetName(), impl.GetVersion()), true)
-}
-
-func (c *componentVersionAccessView) Unwrap() interface{} {
- return c.impl
-}
-
-func (c *componentVersionAccessView) Close() error {
- list := errors.ErrListf("closing %s", common.VersionedElementKey(c))
- err := c._ComponentVersionAccessView.Close()
- return list.Add(c.err, err).Result()
-}
-
-func (c *componentVersionAccessView) finish() {
- if !c.IsClosed() {
- // prepare artifact access for final close in
- // direct access mode.
- if !compositionmodeattr.Get(c.GetContext()) {
- c.err = c.update(true)
- }
- }
-}
-
-func (c *componentVersionAccessView) Repository() Repository {
- return c.impl.Repository()
-}
-
-func (c *componentVersionAccessView) GetContext() internal.Context {
- return c.impl.GetContext()
-}
-
-func (c *componentVersionAccessView) GetName() string {
- return c.impl.GetName()
-}
-
-func (c *componentVersionAccessView) GetVersion() string {
- return c.impl.GetVersion()
-}
-
-func (c *componentVersionAccessView) GetDescriptor() *compdesc.ComponentDescriptor {
- return c.impl.GetDescriptor()
-}
-
-func (c *componentVersionAccessView) GetProvider() *compdesc.Provider {
- return c.GetDescriptor().Provider.Copy()
-}
-
-func (c *componentVersionAccessView) SetProvider(p *compdesc.Provider) error {
- return c.Execute(func() error {
- c.GetDescriptor().Provider = *p.Copy()
- return nil
- })
-}
-
-func (c *componentVersionAccessView) AccessMethod(spec AccessSpec) (meth AccessMethod, err error) {
- spec, err = c.GetContext().AccessSpecForSpec(spec)
- if err != nil {
- return nil, err
- }
- err = c.Execute(func() error {
- var err error
- meth, err = c.accessMethod(spec)
- return err
- })
- return meth, err
-}
-
-func (c *componentVersionAccessView) accessMethod(spec AccessSpec) (meth AccessMethod, err error) {
- switch {
- case compose.Is(spec):
- cspec, ok := spec.(*compose.AccessSpec)
- if !ok {
- return nil, fmt.Errorf("invalid implementation (%T) for access method compose", spec)
- }
- blob := c.getLocalBlob(cspec)
- if blob == nil {
- return nil, errors.ErrUnknown(blobaccess.KIND_BLOB, cspec.Id, common.VersionedElementKey(c).String())
- }
- meth, err = compose.NewMethod(cspec, blob)
- case !spec.IsLocal(c.GetContext()):
- meth, err = spec.AccessMethod(c)
- default:
- meth, err = c.impl.AccessMethod(spec, c.Allocatable())
- if err == nil {
- if blob := c.getLocalBlob(spec); blob != nil {
- meth, err = newFakeMethod(meth, blob)
- }
- }
- }
- return meth, err
-}
-
-func (c *componentVersionAccessView) GetInexpensiveContentVersionIdentity(spec AccessSpec) string {
- var err error
-
- spec, err = c.GetContext().AccessSpecForSpec(spec)
- if err != nil {
- return ""
- }
-
- var id string
- _ = c.Execute(func() error {
- id = c.getInexpensiveContentVersionIdentity(spec)
- return nil
- })
- return id
-}
-
-func (c *componentVersionAccessView) getInexpensiveContentVersionIdentity(spec AccessSpec) string {
- switch {
- case compose.Is(spec):
- fallthrough
- case !spec.IsLocal(c.GetContext()):
- // fall back to original version
- return spec.GetInexpensiveContentVersionIdentity(c)
- default:
- return c.impl.GetInexpensiveContentVersionIdentity(spec, c.Allocatable())
- }
-}
-
-func (c *componentVersionAccessView) Update() error {
- return c.Execute(func() error {
- if !c.impl.IsPersistent() {
- return ErrTempVersion
- }
- return c.update(true)
- })
-}
-
-func (c *componentVersionAccessView) update(final bool) error {
- if !c.impl.ShouldUpdate(final) {
- return nil
- }
-
- ctx := c.GetContext()
- d := c.GetDescriptor()
- impl, err := GetComponentVersionAccessImplementation(c)
- if err != nil {
- return err
- }
- // TODO: exceute for separately lockable view
- err = setupLocalBobs(ctx, "resource", c, c.accessMethod, impl, d.Resources, compose.Is, true, nil)
- if err == nil {
- err = setupLocalBobs(ctx, "source", c, c.accessMethod, impl, d.Sources, compose.Is, true, nil)
- }
- if err != nil {
- return err
- }
-
- err = c.impl.Update(true)
- if err != nil {
- return err
- }
- return c.impl.GetBlobCache().Clear()
-}
-
-func (c *componentVersionAccessView) AddBlob(blob cpi.BlobAccess, artType, refName string, global AccessSpec, opts ...internal.BlobUploadOption) (AccessSpec, error) {
- if blob == nil {
- return nil, errors.New("a resource has to be defined")
- }
- if c.impl.IsReadOnly() {
- return nil, accessio.ErrReadOnly
- }
- blob, err := blob.Dup()
- if err != nil {
- return nil, errors.Wrapf(err, "invalid blob access")
- }
- defer blob.Close()
- err = utils.ValidateObject(blob)
- if err != nil {
- return nil, errors.Wrapf(err, "invalid blob access")
- }
-
- return addBlob(c.impl, artType, refName, blob, global)
-}
-
-func addBlob(impl ComponentVersionAccessImpl, artType, refName string, blob BlobAccess, global AccessSpec) (AccessSpec, error) {
- storagectx := impl.GetStorageContext()
- ctx := impl.GetContext()
- h := ctx.BlobHandlers().LookupHandler(storagectx.GetImplementationRepositoryType(), artType, blob.MimeType())
- if h != nil {
- acc, err := h.StoreBlob(blob, artType, refName, nil, storagectx)
- if err != nil {
- return nil, err
- }
- if acc != nil {
- if !keepblobattr.Get(ctx) || acc.IsLocal(ctx) {
- return acc, nil
- }
- global = acc
- }
- }
- if impl.UseDirectAccess() {
- return impl.AddBlobFor(blob, refName, global)
- }
- // use local composition access to be added to the repository with AddVersion.
- acc := compose.New(refName, blob.MimeType(), global)
- return cacheLocalBlob(impl, acc, blob)
-}
-
-func (c *componentVersionAccessView) getLocalBlob(acc AccessSpec) BlobAccess {
- key, err := json.Marshal(acc)
- if err != nil {
- return nil
- }
- return c.impl.GetBlobCache().GetBlobFor(string(key))
-}
-
-func cacheLocalBlob(impl ComponentVersionAccessImpl, acc AccessSpec, blob BlobAccess) (AccessSpec, error) {
- key, err := json.Marshal(acc)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot marshal access spec")
- }
- // local blobs might not be accessible from the underlying
- // repository implementation if the component version is not
- // finally added (for example ghcr.io as OCI repository).
- // Therefore, we keep a copy of the blob access for further usage.
-
- // if a local blob is uploader and the access method is replaced
- // we have to handle the case that the technical upload repo
- // is the same as the storage backend of the OCM repository, which
- // might have been configured with local credentials, which were
- // reused by the uploader.
- // The access spec is independent of the actual repo, so it does
- // not have access to those credentials. Therefore, we have to
- // keep the original blob for further usage, also.
- err = impl.GetBlobCache().AddBlobFor(string(key), blob)
- if err != nil {
- return nil, err
- }
- return acc, nil
-}
-
-func (c *componentVersionAccessView) AdjustResourceAccess(meta *ResourceMeta, acc compdesc.AccessSpec, opts ...internal.ModificationOption) error {
- cd := c.GetDescriptor()
- if idx := cd.GetResourceIndex(meta); idx >= 0 {
- return c.SetResource(&cd.Resources[idx].ResourceMeta, acc, opts...)
- }
- return errors.ErrUnknown(KIND_RESOURCE, meta.GetIdentity(cd.Resources).String())
-}
-
-// SetResourceBlob adds a blob resource to the component version.
-func (c *componentVersionAccessView) SetResourceBlob(meta *ResourceMeta, blob cpi.BlobAccess, refName string, global AccessSpec, opts ...internal.BlobModificationOption) error {
- Logger(c).Debug("adding resource blob", "resource", meta.Name)
- if err := utils.ValidateObject(blob); err != nil {
- return err
- }
- eff := NewBlobModificationOptions(opts...)
- acc, err := c.AddBlob(blob, meta.Type, refName, global, eff)
- if err != nil {
- return fmt.Errorf("unable to add blob (component %s:%s resource %s): %w", c.GetName(), c.GetVersion(), meta.GetName(), err)
- }
-
- if err := c.SetResource(meta, acc, eff, ModifyResource()); err != nil {
- return fmt.Errorf("unable to set resource: %w", err)
- }
-
- return nil
-}
-
-func (c *componentVersionAccessView) AdjustSourceAccess(meta *SourceMeta, acc compdesc.AccessSpec) error {
- cd := c.GetDescriptor()
- if idx := cd.GetSourceIndex(meta); idx >= 0 {
- return c.SetSource(&cd.Sources[idx].SourceMeta, acc)
- }
- return errors.ErrUnknown(KIND_RESOURCE, meta.GetIdentity(cd.Resources).String())
-}
-
-func (c *componentVersionAccessView) SetSourceBlob(meta *SourceMeta, blob BlobAccess, refName string, global AccessSpec) error {
- Logger(c).Debug("adding source blob", "source", meta.Name)
- if err := utils.ValidateObject(blob); err != nil {
- return err
- }
- acc, err := c.AddBlob(blob, meta.Type, refName, global)
- if err != nil {
- return fmt.Errorf("unable to add blob: (component %s:%s source %s): %w", c.GetName(), c.GetVersion(), meta.GetName(), err)
- }
-
- if err := c.SetSource(meta, acc); err != nil {
- return fmt.Errorf("unable to set source: %w", err)
- }
-
- return nil
-}
-
-type fakeMethod struct {
- spec AccessSpec
- local bool
- mime string
- blob blobaccess.BlobAccess
-}
-
-var _ accspeccpi.AccessMethodImpl = (*fakeMethod)(nil)
-
-func newFakeMethod(m AccessMethod, blob BlobAccess) (AccessMethod, error) {
- b, err := blob.Dup()
- if err != nil {
- return nil, errors.Wrapf(err, "cannot remember blob for access method")
- }
- f := &fakeMethod{
- spec: m.AccessSpec(),
- local: m.IsLocal(),
- mime: m.MimeType(),
- blob: b,
- }
- err = m.Close()
- if err != nil {
- _ = b.Close()
- return nil, errors.Wrapf(err, "closing access method")
- }
- return accspeccpi.AccessMethodForImplementation(f, nil)
-}
-
-func (f *fakeMethod) MimeType() string {
- return f.mime
-}
-
-func (f *fakeMethod) IsLocal() bool {
- return f.local
-}
-
-func (f *fakeMethod) GetKind() string {
- return f.spec.GetKind()
-}
-
-func (f *fakeMethod) AccessSpec() internal.AccessSpec {
- return f.spec
-}
-
-func (f *fakeMethod) Close() error {
- return f.blob.Close()
-}
-
-func (f *fakeMethod) Reader() (io.ReadCloser, error) {
- return f.blob.Reader()
-}
-
-func (f *fakeMethod) Get() ([]byte, error) {
- return f.blob.Get()
-}
-
-func setAccess[T any, A internal.ArtifactAccess[T]](c *componentVersionAccessView, kind string, art A,
- set func(*T, compdesc.AccessSpec) error,
- setblob func(*T, BlobAccess, string, AccessSpec) error,
-) error {
- if c.impl.IsReadOnly() {
- return accessio.ErrReadOnly
- }
- meta := art.Meta()
- if meta == nil {
- return errors.Newf("no meta data provided by %s access", kind)
- }
- acc, err := art.Access()
- if err != nil && !errors.IsErrNotFoundElem(err, "", descriptor.KIND_ACCESSMETHOD) {
- return err
- }
-
- var (
- blob BlobAccess
- hint string
- global AccessSpec
- )
-
- if acc != nil {
- if !acc.IsLocal(c.GetContext()) {
- return set(meta, acc)
- }
-
- blob, err = accspeccpi.BlobAccessForAccessSpec(acc, c)
- if err != nil && errors.IsErrNotFoundElem(err, "", blobaccess.KIND_BLOB) {
- return err
- }
- hint = ReferenceHint(acc, c)
- global = GlobalAccess(acc, c.GetContext())
- }
- if blob == nil {
- blob, err = art.BlobAccess()
- if err != nil {
- return err
- }
- defer blob.Close()
- }
- if blob == nil {
- return errors.Newf("neither access nor blob specified in %s access", kind)
- }
- if v := art.ReferenceHint(); v != "" {
- hint = v
- }
- if v := art.GlobalAccess(); v != nil {
- global = v
- }
- return setblob(meta, blob, hint, global)
-}
-
-func (c *componentVersionAccessView) SetResourceAccess(art ResourceAccess, modopts ...BlobModificationOption) error {
- return setAccess(c, "resource", art,
- func(meta *ResourceMeta, acc compdesc.AccessSpec) error {
- return c.SetResource(meta, acc, NewBlobModificationOptions(modopts...))
- },
- func(meta *ResourceMeta, blob BlobAccess, hint string, global AccessSpec) error {
- return c.SetResourceBlob(meta, blob, hint, global, modopts...)
- })
-}
-
-func (c *componentVersionAccessView) SetResource(meta *internal.ResourceMeta, acc compdesc.AccessSpec, modopts ...ModificationOption) error {
- if c.impl.IsReadOnly() {
- return accessio.ErrReadOnly
- }
-
- res := &compdesc.Resource{
- ResourceMeta: *meta.Copy(),
- Access: acc,
- }
-
- ctx := c.impl.GetContext()
- opts := internal.NewModificationOptions(modopts...)
- CompleteModificationOptions(ctx, opts)
-
- spec, err := c.impl.GetContext().AccessSpecForSpec(acc)
- if err != nil {
- return err
- }
-
- // if the blob described by the access spec has been added
- // as local blob, just reuse the stored blob access
- // to calculate the digest to circumvent credential problems
- // for access specs generated by an uploader.
- meth, err := c.AccessMethod(spec)
- if err != nil {
- return err
- }
- if blob := c.getLocalBlob(spec); blob != nil {
- var dig digest.Digest
- if s, ok := meth.(blobaccess.DigestSource); ok {
- dig = s.Digest()
- }
- err = meth.Close()
- if err != nil {
- return errors.Wrapf(err, "clsoing shadowed method")
- }
- meth, err = accspeccpi.NewDefaultMethodForBlobAccess(c, spec, dig, blob, spec.IsLocal(c.GetContext()))
- if err != nil {
- return err
- }
- }
- defer meth.Close()
-
- return c.Execute(func() error {
- var old *compdesc.Resource
-
- if res.Relation == metav1.LocalRelation {
- if res.Version == "" {
- res.Version = c.GetVersion()
- }
- }
-
- cd := c.impl.GetDescriptor()
- idx := cd.GetResourceIndex(&res.ResourceMeta)
- if idx >= 0 {
- old = &cd.Resources[idx]
- }
-
- if old == nil {
- if !opts.IsModifyResource() && c.impl.IsPersistent() {
- return fmt.Errorf("new resource would invalidate signature")
- }
- }
-
- // evaluate given digesting constraints and settings
- hashAlgo, digester, digest := c.evaluateResourceDigest(res, old, *opts)
- hasher := opts.GetHasher(hashAlgo)
- if digester.HashAlgorithm == "" && hasher == nil {
- return errors.ErrUnknown(compdesc.KIND_HASH_ALGORITHM, hashAlgo)
- }
-
- if !compdesc.IsNoneAccessKind(res.Access.GetKind()) {
- var calculatedDigest *DigestDescriptor
- if (!opts.IsSkipVerify() && digest != "") || (!opts.IsSkipDigest() && digest == "") {
- dig, err := ctx.BlobDigesters().DetermineDigests(res.Type, hasher, opts.HasherProvider, meth, digester)
- if err != nil {
- return err
- }
- if len(dig) == 0 {
- 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 !opts.IsSkipDigest() {
- if digest == "" {
- res.Digest = calculatedDigest
- } else {
- res.Digest = &compdesc.DigestSpec{
- HashAlgorithm: digester.HashAlgorithm,
- NormalisationAlgorithm: digester.NormalizationAlgorithm,
- Value: digest,
- }
- }
- }
- }
-
- if old != nil {
- eq := res.Equivalent(old)
- if !eq.IsLocalHashEqual() && c.impl.IsPersistent() {
- if !opts.IsModifyResource() {
- return fmt.Errorf("resource would invalidate signature")
- }
- cd.Signatures = nil
- }
- }
-
- if old == nil {
- cd.Resources = append(cd.Resources, *res)
- } else {
- cd.Resources[idx] = *res
- }
- return c.update(false)
- })
-}
-
-// evaluateResourceDigest evaluate given potentially partly set digest to determine defaults.
-func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.Resource, opts ModificationOptions) (string, DigesterType, string) {
- var digester DigesterType
-
- hashAlgo := opts.DefaultHashAlgorithm
- value := ""
- if !res.Digest.IsNone() {
- if res.Digest.IsComplete() {
- value = res.Digest.Value
- }
- if res.Digest.HashAlgorithm != "" {
- hashAlgo = res.Digest.HashAlgorithm
- }
- if res.Digest.NormalisationAlgorithm != "" {
- digester = DigesterType{
- HashAlgorithm: hashAlgo,
- NormalizationAlgorithm: res.Digest.NormalisationAlgorithm,
- }
- }
- }
- res.Digest = nil
-
- // keep potential old digest settings
- if old != nil && old.Type == res.Type {
- if !old.Digest.IsNone() {
- digester.HashAlgorithm = old.Digest.HashAlgorithm
- digester.NormalizationAlgorithm = old.Digest.NormalisationAlgorithm
- if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.impl.IsPersistent() {
- res.Digest = old.Digest
- value = old.Digest.Value
- }
- }
- }
- return hashAlgo, digester, value
-}
-
-func (c *componentVersionAccessView) SetSourceByAccess(art SourceAccess) error {
- return setAccess(c, "source", art,
- c.SetSource, c.SetSourceBlob)
-}
-
-func (c *componentVersionAccessView) SetSource(meta *SourceMeta, acc compdesc.AccessSpec) error {
- if c.impl.IsReadOnly() {
- return accessio.ErrReadOnly
- }
-
- res := &compdesc.Source{
- SourceMeta: *meta.Copy(),
- Access: acc,
- }
- return c.Execute(func() error {
- if res.Version == "" {
- res.Version = c.impl.GetVersion()
- }
- cd := c.impl.GetDescriptor()
- if idx := cd.GetSourceIndex(&res.SourceMeta); idx == -1 {
- cd.Sources = append(cd.Sources, *res)
- } else {
- cd.Sources[idx] = *res
- }
- return c.update(false)
- })
-}
-
-func (c *componentVersionAccessView) SetReference(ref *ComponentReference) error {
- return c.Execute(func() error {
- cd := c.impl.GetDescriptor()
- if idx := cd.GetComponentReferenceIndex(*ref); idx == -1 {
- cd.References = append(cd.References, *ref)
- } else {
- cd.References[idx] = *ref
- }
- return c.update(false)
- })
-}
-
-func (c *componentVersionAccessView) DiscardChanges() {
- c.impl.DiscardChanges()
-}
-
-func (c *componentVersionAccessView) IsPersistent() bool {
- return c.impl.IsPersistent()
-}
-
-func (c *componentVersionAccessView) UseDirectAccess() bool {
- return c.impl.UseDirectAccess()
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Standard Implementation for descriptor based methods
-
-func (c *componentVersionAccessView) GetResource(id metav1.Identity) (ResourceAccess, error) {
- r, err := c.GetDescriptor().GetResourceByIdentity(id)
- if err != nil {
- return nil, err
- }
- return NewResourceAccess(c, r.Access, r.ResourceMeta), nil
-}
-
-func (c *componentVersionAccessView) GetResourceIndex(id metav1.Identity) int {
- return c.GetDescriptor().GetResourceIndexByIdentity(id)
-}
-
-func (c *componentVersionAccessView) GetResourceByIndex(i int) (ResourceAccess, error) {
- if i < 0 || i >= len(c.GetDescriptor().Resources) {
- return nil, errors.ErrInvalid("resource index", strconv.Itoa(i))
- }
- r := c.GetDescriptor().Resources[i]
- return NewResourceAccess(c, r.Access, r.ResourceMeta), nil
-}
-
-func (c *componentVersionAccessView) GetResourcesByName(name string, selectors ...compdesc.IdentitySelector) ([]ResourceAccess, error) {
- resources, err := c.GetDescriptor().GetResourcesByName(name, selectors...)
- if err != nil {
- return nil, err
- }
-
- result := []ResourceAccess{}
- for _, resource := range resources {
- result = append(result, NewResourceAccess(c, resource.Access, resource.ResourceMeta))
- }
- return result, nil
-}
-
-func (c *componentVersionAccessView) GetResources() []ResourceAccess {
- result := []ResourceAccess{}
- for _, r := range c.GetDescriptor().Resources {
- result = append(result, NewResourceAccess(c, r.Access, r.ResourceMeta))
- }
- return result
-}
-
-// GetResourcesByIdentitySelectors returns resources that match the given identity selectors.
-func (c *componentVersionAccessView) GetResourcesByIdentitySelectors(selectors ...compdesc.IdentitySelector) ([]ResourceAccess, error) {
- return c.GetResourcesBySelectors(selectors, nil)
-}
-
-// GetResourcesByResourceSelectors returns resources that match the given resource selectors.
-func (c *componentVersionAccessView) GetResourcesByResourceSelectors(selectors ...compdesc.ResourceSelector) ([]ResourceAccess, error) {
- return c.GetResourcesBySelectors(nil, selectors)
-}
-
-// GetResourcesBySelectors returns resources that match the given selector.
-func (c *componentVersionAccessView) GetResourcesBySelectors(selectors []compdesc.IdentitySelector, resourceSelectors []compdesc.ResourceSelector) ([]ResourceAccess, error) {
- resources := make([]ResourceAccess, 0)
- rscs := c.GetDescriptor().Resources
- for i := range rscs {
- selctx := compdesc.NewResourceSelectionContext(i, rscs)
- if len(selectors) > 0 {
- ok, err := selector.MatchSelectors(selctx.Identity(), selectors...)
- if err != nil {
- return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
- }
- if !ok {
- continue
- }
- }
- ok, err := compdesc.MatchResourceByResourceSelector(selctx, resourceSelectors...)
- if err != nil {
- return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
- }
- if !ok {
- continue
- }
- r, err := c.GetResourceByIndex(i)
- if err != nil {
- return nil, err
- }
- resources = append(resources, r)
- }
- if len(resources) == 0 {
- return resources, compdesc.NotFound
- }
- return resources, nil
-}
-
-func (c *componentVersionAccessView) GetSource(id metav1.Identity) (SourceAccess, error) {
- r, err := c.GetDescriptor().GetSourceByIdentity(id)
- if err != nil {
- return nil, err
- }
- return NewSourceAccess(c, r.Access, r.SourceMeta), nil
-}
-
-func (c *componentVersionAccessView) GetSourceIndex(id metav1.Identity) int {
- return c.GetDescriptor().GetSourceIndexByIdentity(id)
-}
-
-func (c *componentVersionAccessView) GetSourceByIndex(i int) (SourceAccess, error) {
- if i < 0 || i >= len(c.GetDescriptor().Sources) {
- return nil, errors.ErrInvalid("source index", strconv.Itoa(i))
- }
- r := c.GetDescriptor().Sources[i]
- return NewSourceAccess(c, r.Access, r.SourceMeta), nil
-}
-
-func (c *componentVersionAccessView) GetSources() []SourceAccess {
- result := []SourceAccess{}
- for _, r := range c.GetDescriptor().Sources {
- result = append(result, NewSourceAccess(c, r.Access, r.SourceMeta))
- }
- return result
-}
-
-func (c *componentVersionAccessView) GetReferences() compdesc.References {
- return c.GetDescriptor().References
-}
-
-func (c *componentVersionAccessView) GetReference(id metav1.Identity) (ComponentReference, error) {
- return c.GetDescriptor().GetReferenceByIdentity(id)
-}
-
-func (c *componentVersionAccessView) GetReferenceIndex(id metav1.Identity) int {
- return c.GetDescriptor().GetReferenceIndexByIdentity(id)
-}
-
-func (c *componentVersionAccessView) GetReferenceByIndex(i int) (ComponentReference, error) {
- if i < 0 || i > len(c.GetDescriptor().References) {
- return ComponentReference{}, errors.ErrInvalid("reference index", strconv.Itoa(i))
- }
- return c.GetDescriptor().References[i], nil
-}
-
-func (c *componentVersionAccessView) GetReferencesByName(name string, selectors ...compdesc.IdentitySelector) (compdesc.References, error) {
- return c.GetDescriptor().GetReferencesByName(name, selectors...)
-}
-
-// GetReferencesByIdentitySelectors returns references that match the given identity selectors.
-func (c *componentVersionAccessView) GetReferencesByIdentitySelectors(selectors ...compdesc.IdentitySelector) (compdesc.References, error) {
- return c.GetReferencesBySelectors(selectors, nil)
-}
-
-// GetReferencesByReferenceSelectors returns references that match the given resource selectors.
-func (c *componentVersionAccessView) GetReferencesByReferenceSelectors(selectors ...compdesc.ReferenceSelector) (compdesc.References, error) {
- return c.GetReferencesBySelectors(nil, selectors)
-}
-
-// GetReferencesBySelectors returns references that match the given selector.
-func (c *componentVersionAccessView) GetReferencesBySelectors(selectors []compdesc.IdentitySelector, referenceSelectors []compdesc.ReferenceSelector) (compdesc.References, error) {
- references := make(compdesc.References, 0)
- refs := c.GetDescriptor().References
- for i := range refs {
- selctx := compdesc.NewReferenceSelectionContext(i, refs)
- if len(selectors) > 0 {
- ok, err := selector.MatchSelectors(selctx.Identity(), selectors...)
- if err != nil {
- return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
- }
- if !ok {
- continue
- }
- }
- ok, err := compdesc.MatchReferencesByReferenceSelector(selctx, referenceSelectors...)
- if err != nil {
- return nil, fmt.Errorf("unable to match selector for resource %s: %w", selctx.Name, err)
- }
- if !ok {
- continue
- }
- references = append(references, *selctx.ComponentReference)
- }
- if len(references) == 0 {
- return references, compdesc.NotFound
- }
- return references, nil
-}
diff --git a/pkg/contexts/ocm/cpi/view_rsc.go b/pkg/contexts/ocm/cpi/view_rsc.go
index 4b8868dafb..a88c2a5f40 100644
--- a/pkg/contexts/ocm/cpi/view_rsc.go
+++ b/pkg/contexts/ocm/cpi/view_rsc.go
@@ -158,6 +158,9 @@ func (b *accessAccessProvider) GetOCMContext() cpi.Context {
}
func (b *accessAccessProvider) ReferenceHint() string {
+ if h, ok := b.spec.(HintProvider); ok {
+ return h.GetReferenceHint(&DummyComponentVersionAccess{b.ctx})
+ }
return ""
}
@@ -180,32 +183,24 @@ func (b *accessAccessProvider) BlobAccess() (blobaccess.BlobAccess, error) {
////////////////////////////////////////////////////////////////////////////////
type (
- accessProvider = AccessProvider
- componentVersionProvider = ComponentVersionProvider
+ accessProvider = AccessProvider
)
type artifactAccessProvider[M any] struct {
accessProvider
- meta *M
+ componentVersionProvider ComponentVersionProvider
+ meta *M
}
var _ credentials.ConsumerIdentityProvider = (*artifactAccessProvider[any])(nil)
-type artifactCVAccessProvider[M any] struct {
- artifactAccessProvider[M]
- componentVersionProvider
-}
-
func NewArtifactAccessForProvider[M any](meta *M, prov AccessProvider) cpi.ArtifactAccess[M] {
aa := &artifactAccessProvider[M]{
accessProvider: prov,
meta: meta,
}
if p, ok := prov.(ComponentVersionProvider); ok {
- return &artifactCVAccessProvider[M]{
- artifactAccessProvider: *aa,
- componentVersionProvider: p,
- }
+ aa.componentVersionProvider = p
}
return aa
}
@@ -232,6 +227,13 @@ func (b *artifactAccessProvider[M]) GetIdentityMatcher() string {
return credentials.GetProvidedIdentityMatcher(m)
}
+func (b *artifactAccessProvider[M]) GetComponentVersion() (ComponentVersionAccess, error) {
+ if b.componentVersionProvider != nil {
+ return b.componentVersionProvider.GetComponentVersion()
+ }
+ return nil, nil
+}
+
////////////////////////////////////////////////////////////////////////////////
var _ ResourceAccess = (*artifactAccessProvider[ResourceMeta])(nil)
diff --git a/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go b/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go
index 963bbb27bf..34b7f4bb46 100644
--- a/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go
+++ b/pkg/contexts/ocm/elements/artifactaccess/genericaccess/resource_test.go
@@ -46,7 +46,7 @@ var _ = Describe("dir tree resource access", func() {
acc := Must(me.ResourceAccess(env.OCMContext(), compdesc.NewResourceMeta("test", resourcetypes.OCI_IMAGE, compdesc.LocalRelation), spec))
- Expect(acc.ReferenceHint()).To(Equal(""))
+ Expect(acc.ReferenceHint()).To(Equal(OCINAMESPACE + ":" + OCIVERSION))
Expect(acc.GlobalAccess()).To(BeNil())
Expect(acc.Meta().Type).To(Equal(resourcetypes.OCI_IMAGE))
diff --git a/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go b/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go
index cbb3130654..29c039f900 100644
--- a/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go
+++ b/pkg/contexts/ocm/elements/artifactblob/externalblob/resource.go
@@ -8,7 +8,8 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/elements/artifactaccess/genericaccess"
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/generics"
"github.com/open-component-model/ocm/pkg/optionutils"
)
@@ -24,43 +25,41 @@ func Access[M any, P compdesc.ArtifactMetaPointer[M]](ctx ocm.Context, meta P, a
global = ocm.GlobalAccess(access, ctx)
}
- a, err := genericaccess.Access(ctx, meta, access)
+ prov, err := cpi.NewAccessProviderForExternalAccessSpec(ctx, access)
if err != nil {
- return nil, err
+ return nil, errors.Wrapf(err, "invalid external access method %q", access.GetKind())
}
- return newAccessProvider[M](a, hint, global), nil
+ return cpi.NewArtifactAccessForProvider(generics.As[*M](meta), newAccessProvider(prov, hint, global)), nil
}
-type accessProvider[M any] struct {
- cpi.ArtifactAccess[M]
+type _accessProvider = cpi.AccessProvider
+
+type accessProvider struct {
+ _accessProvider
hint string
global cpi.AccessSpec
}
-func newAccessProvider[M any](prov cpi.ArtifactAccess[M], hint string, global cpi.AccessSpec) cpi.ArtifactAccess[M] {
- return &accessProvider[M]{
- ArtifactAccess: prov,
- hint: hint,
- global: global,
+func newAccessProvider(prov cpi.AccessProvider, hint string, global cpi.AccessSpec) cpi.AccessProvider {
+ return &accessProvider{
+ _accessProvider: prov,
+ hint: hint,
+ global: global,
}
}
-func (p *accessProvider[M]) AccessSpec() cpi.AccessSpec {
- return nil
-}
-
-func (p *accessProvider[M]) ReferenceHint() string {
+func (p *accessProvider) ReferenceHint() string {
if p.hint != "" {
return p.hint
}
- return p.ArtifactAccess.ReferenceHint()
+ return p._accessProvider.ReferenceHint()
}
-func (p *accessProvider[M]) GlobalAccess() cpi.AccessSpec {
+func (p *accessProvider) GlobalAccess() cpi.AccessSpec {
if p.global != nil {
return p.global
}
- return p.ArtifactAccess.GlobalAccess()
+ return p._accessProvider.GlobalAccess()
}
func ResourceAccess(ctx ocm.Context, meta *cpi.ResourceMeta, access cpi.AccessSpec, opts ...Option) (cpi.ResourceAccess, error) {
diff --git a/pkg/contexts/ocm/interface.go b/pkg/contexts/ocm/interface.go
index 4ad64ec641..3548bced5c 100644
--- a/pkg/contexts/ocm/interface.go
+++ b/pkg/contexts/ocm/interface.go
@@ -12,13 +12,14 @@ import (
metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/internal"
"github.com/open-component-model/ocm/pkg/runtime"
)
// ErrTempVersion indicates an ignored update in the backend because the
// current version has not yet been added to the repository.
-var ErrTempVersion = cpi.ErrTempVersion
+var ErrTempVersion = repocpi.ErrTempVersion
const (
KIND_COMPONENTVERSION = internal.KIND_COMPONENTVERSION
diff --git a/pkg/contexts/ocm/internal/accessspecref.go b/pkg/contexts/ocm/internal/accessspecref.go
index 5f30c43b39..c61665b96d 100644
--- a/pkg/contexts/ocm/internal/accessspecref.go
+++ b/pkg/contexts/ocm/internal/accessspecref.go
@@ -45,6 +45,16 @@ func NewRawAccessSpecRef(data []byte, unmarshaler runtime.Unmarshaler) (*AccessS
return &AccessSpecRef{generic: &spec}, nil
}
+func (a *AccessSpecRef) Get() AccessSpec {
+ if a == nil {
+ return nil
+ }
+ if a.evaluated != nil {
+ return a.evaluated
+ }
+ return a.generic
+}
+
func (a *AccessSpecRef) Set(spec AccessSpec) {
if g, ok := spec.(*GenericAccessSpec); ok {
a.evaluated = nil
diff --git a/pkg/contexts/ocm/internal/modopts.go b/pkg/contexts/ocm/internal/modopts.go
index 4c691a9360..c509cbba1c 100644
--- a/pkg/contexts/ocm/internal/modopts.go
+++ b/pkg/contexts/ocm/internal/modopts.go
@@ -5,6 +5,7 @@
package internal
import (
+ "github.com/open-component-model/ocm/pkg/optionutils"
"github.com/open-component-model/ocm/pkg/utils"
)
@@ -18,7 +19,7 @@ type BlobOptionImpl interface {
}
type BlobUploadOptions struct {
- UseNoDefaultIfNotSet bool `json:"noDefaultUpload,omitempty"`
+ UseNoDefaultIfNotSet *bool `json:"noDefaultUpload,omitempty"`
BlobHandlerProvider BlobHandlerProvider `json:"-"`
}
@@ -43,13 +44,31 @@ func (o *BlobUploadOptions) ApplyBlobModificationOption(opts *BlobModificationOp
}
func (o *BlobUploadOptions) ApplyBlobUploadOption(opts *BlobUploadOptions) {
+ optionutils.ApplyOption(o.UseNoDefaultIfNotSet, &opts.UseNoDefaultIfNotSet)
if o.BlobHandlerProvider != nil {
opts.BlobHandlerProvider = o.BlobHandlerProvider
- } else {
- opts.UseNoDefaultIfNotSet = true
+ opts.UseNoDefaultIfNotSet = utils.BoolP(true)
}
}
+////////////////////////////////////////////////////////////////////////////////
+
+type nodefaulthandler bool
+
+func (o nodefaulthandler) ApplyBlobModificationOption(opts *BlobModificationOptions) {
+ o.ApplyBlobUploadOption(&opts.BlobUploadOptions)
+}
+
+func (o nodefaulthandler) ApplyBlobUploadOption(opts *BlobUploadOptions) {
+ opts.UseNoDefaultIfNotSet = optionutils.PointerTo(bool(o))
+}
+
+func UseNoDefaultBlobHandlers(b ...bool) BlobOptionImpl {
+ return nodefaulthandler(utils.OptionalDefaultedBool(true, b...))
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
type handler struct {
blobHandlerProvider BlobHandlerProvider
}
@@ -131,10 +150,10 @@ func (m *ModificationOptions) ApplyBlobModificationOption(opts *BlobModification
}
func (m *ModificationOptions) ApplyModificationOption(opts *ModificationOptions) {
- applyBool(m.ModifyResource, &opts.ModifyResource)
- applyBool(m.AcceptExistentDigests, &opts.AcceptExistentDigests)
- applyBool(m.SkipDigest, &opts.SkipDigest)
- applyBool(m.SkipVerify, &opts.SkipVerify)
+ optionutils.ApplyOption(m.ModifyResource, &opts.ModifyResource)
+ optionutils.ApplyOption(m.AcceptExistentDigests, &opts.AcceptExistentDigests)
+ optionutils.ApplyOption(m.SkipDigest, &opts.SkipDigest)
+ optionutils.ApplyOption(m.SkipVerify, &opts.SkipVerify)
if m.HasherProvider != nil {
opts.HasherProvider = m.HasherProvider
}
@@ -143,12 +162,6 @@ func (m *ModificationOptions) ApplyModificationOption(opts *ModificationOptions)
}
}
-func applyBool(m *bool, t **bool) {
- if m != nil {
- *t = utils.BoolP(*m)
- }
-}
-
func (m *ModificationOptions) GetHasher(algo ...string) Hasher {
return m.HasherProvider.GetHasher(utils.OptionalDefaulted(m.DefaultHashAlgorithm, algo...))
}
@@ -299,3 +312,48 @@ func (o *BlobModificationOptions) ApplyBlobUploadOption(opts *BlobUploadOptions)
func (o *BlobModificationOptions) ApplyModificationOption(opts *ModificationOptions) {
o.ModificationOptions.ApplyModificationOption(opts)
}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// BlobModificationOption is used for option list allowing both,
+// blob upload and modification options.
+type AddVersionOption interface {
+ ApplyAddVersionOption(*AddVersionOptions)
+}
+
+type AddVersionOptions struct {
+ Overwrite *bool
+ BlobUploadOptions
+}
+
+func NewAddVersionOptions(list ...AddVersionOption) *AddVersionOptions {
+ var m AddVersionOptions
+ m.ApplyAddVersionOptions(list...)
+ return &m
+}
+
+func (m *AddVersionOptions) ApplyAddVersionOptions(list ...AddVersionOption) {
+ for _, o := range list {
+ if o != nil {
+ o.ApplyAddVersionOption(m)
+ }
+ }
+}
+
+func (o *AddVersionOptions) ApplyAddVersionOption(opts *AddVersionOptions) {
+ optionutils.ApplyOption(o.Overwrite, &opts.Overwrite)
+ o.BlobUploadOptions.ApplyBlobUploadOption(&opts.BlobUploadOptions)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type overwrite bool
+
+func (m overwrite) ApplyAddVersionOption(opts *AddVersionOptions) {
+ opts.Overwrite = utils.BoolP(m)
+}
+
+// Overwrite enabled the overwrite mode for adding a component version.
+func Overwrite(flag ...bool) AddVersionOption {
+ return overwrite(utils.OptionalDefaultedBool(true, flag...))
+}
diff --git a/pkg/contexts/ocm/internal/repository.go b/pkg/contexts/ocm/internal/repository.go
index 656fe981d8..e87fc06d04 100644
--- a/pkg/contexts/ocm/internal/repository.go
+++ b/pkg/contexts/ocm/internal/repository.go
@@ -51,7 +51,9 @@ type (
MimeType = blobaccess.MimeType
)
-type ComponentAccessImpl interface {
+type ComponentAccess interface {
+ resource.ResourceView[ComponentAccess]
+
GetContext() Context
GetName() string
@@ -59,15 +61,10 @@ type ComponentAccessImpl interface {
LookupVersion(version string) (ComponentVersionAccess, error)
HasVersion(vers string) (bool, error)
NewVersion(version string, overrides ...bool) (ComponentVersionAccess, error)
-
- Close() error
-}
-
-type ComponentAccess interface {
- resource.ResourceView[ComponentAccess]
-
- ComponentAccessImpl
AddVersion(cv ComponentVersionAccess, overrides ...bool) error
+ AddVersionOpt(cv ComponentVersionAccess, opts ...AddVersionOption) error
+
+ io.Closer
}
// AccessProvider assembled methods provided
@@ -92,6 +89,7 @@ type AccessProvider interface {
type ArtifactAccess[M any] interface {
Meta() *M
+ GetComponentVersion() (ComponentVersionAccess, error)
AccessProvider
}
diff --git a/pkg/contexts/ocm/modopts.go b/pkg/contexts/ocm/modopts.go
index b65f0f06dd..a03eba5cc6 100644
--- a/pkg/contexts/ocm/modopts.go
+++ b/pkg/contexts/ocm/modopts.go
@@ -20,10 +20,24 @@ type (
BlobUploadOption = internal.BlobUploadOption
BlobUploadOptions = internal.BlobUploadOptions
+
+ AddVersionOption = internal.AddVersionOption
+ AddVersionOptions = internal.AddVersionOptions
)
////////////////////////////////////////////////////////////////////////////////
+func NewAddVersionOptions(list ...AddVersionOption) *AddVersionOptions {
+ return internal.NewAddVersionOptions(list...)
+}
+
+// Overwrite enabled the overwrite mode for adding a component version.
+func Overwrite(flag ...bool) AddVersionOption {
+ return internal.Overwrite(flag...)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
func NewBlobModificationOptions(list ...BlobModificationOption) *BlobModificationOptions {
return internal.NewBlobModificationOptions(list...)
}
diff --git a/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go b/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go
index f980c949fc..db622c078d 100644
--- a/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go
+++ b/pkg/contexts/ocm/repositories/comparch/accessmethod_localfs.go
@@ -13,7 +13,7 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/refmgmt"
)
@@ -23,14 +23,14 @@ type localFilesystemBlobAccessMethod struct {
sync.Mutex
closed bool
spec *localblob.AccessSpec
- base support.ComponentVersionContainer
+ base repocpi.ComponentVersionAccessImpl
err error
blobAccess blobaccess.BlobAccess
}
var _ accspeccpi.AccessMethodImpl = (*localFilesystemBlobAccessMethod)(nil)
-func newLocalFilesystemBlobAccessMethod(a *localblob.AccessSpec, base support.ComponentVersionContainer, ref refmgmt.ExtendedAllocatable) (accspeccpi.AccessMethod, error) {
+func newLocalFilesystemBlobAccessMethod(a *localblob.AccessSpec, base repocpi.ComponentVersionAccessImpl, ref refmgmt.ExtendedAllocatable) (accspeccpi.AccessMethod, error) {
m := &localFilesystemBlobAccessMethod{
spec: a,
base: base,
@@ -86,7 +86,7 @@ func (m *localFilesystemBlobAccessMethod) Reader() (io.ReadCloser, error) {
func (m *localFilesystemBlobAccessMethod) getBlob() (blobaccess.BlobAccess, error) {
if m.blobAccess == nil {
- data, err := m.base.GetBlobData(m.spec.LocalReference)
+ data, err := m.base.GetBlob(m.spec.LocalReference)
if err != nil {
return nil, err
}
diff --git a/pkg/contexts/ocm/repositories/comparch/componentarchive.go b/pkg/contexts/ocm/repositories/comparch/componentarchive.go
index b05d267be9..1ee8f4cf25 100644
--- a/pkg/contexts/ocm/repositories/comparch/componentarchive.go
+++ b/pkg/contexts/ocm/repositories/comparch/componentarchive.go
@@ -16,20 +16,22 @@ import (
ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/refmgmt"
)
////////////////////////////////////////////////////////////////////////////////
+type _componentVersionAccess = cpi.ComponentVersionAccess
+
// ComponentArchive is the go representation for a component artifact.
type ComponentArchive struct {
+ _componentVersionAccess
spec *RepositorySpec
container *componentArchiveContainer
main cpi.Repository
nonref cpi.Repository
- cpi.ComponentVersionAccess
}
// New returns a new representation based element.
@@ -47,19 +49,20 @@ func _Wrap(ctx cpi.ContextProvider, obj *accessobj.AccessObject, spec *Repositor
return nil, err
}
s := &componentArchiveContainer{
- ctx: ctx.OCMContext(),
- base: accessobj.NewFileSystemBlobAccess(obj),
+ ctx: ctx.OCMContext(),
+ fsacc: accessobj.NewFileSystemBlobAccess(obj),
+ spec: spec,
}
- impl, err := support.NewComponentVersionAccessImpl(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true)
+ cv, err := repocpi.NewComponentVersionAccess(s.GetDescriptor().GetName(), s.GetDescriptor().GetVersion(), s, false, true, true)
if err != nil {
return nil, err
}
- s.spec = spec
+
arch := &ComponentArchive{
spec: spec,
container: s,
}
- arch.ComponentVersionAccess = cpi.NewComponentVersionAccess(impl)
+ arch._componentVersionAccess = cv
arch.main, arch.nonref = newRepository(arch)
s.repo = arch.nonref
return arch, nil
@@ -99,26 +102,26 @@ func (c *ComponentArchive) SetVersion(v string) {
////////////////////////////////////////////////////////////////////////////////
type componentArchiveContainer struct {
- ctx cpi.Context
- impl support.ComponentVersionAccessImpl
- base *accessobj.FileSystemBlobAccess
- spec *RepositorySpec
- repo cpi.Repository
+ ctx cpi.Context
+ base repocpi.ComponentVersionAccessBridge
+ fsacc *accessobj.FileSystemBlobAccess
+ spec *RepositorySpec
+ repo cpi.Repository
}
-var _ support.ComponentVersionContainer = (*componentArchiveContainer)(nil)
+var _ repocpi.ComponentVersionAccessImpl = (*componentArchiveContainer)(nil)
-func (c *componentArchiveContainer) SetImplementation(impl support.ComponentVersionAccessImpl) {
- c.impl = impl
+func (c *componentArchiveContainer) SetBridge(base repocpi.ComponentVersionAccessBridge) {
+ c.base = base
}
-func (c *componentArchiveContainer) GetParentViewManager() cpi.ComponentAccessViewManager {
+func (c *componentArchiveContainer) GetParentBridge() repocpi.ComponentAccessBridge {
return nil
}
func (c *componentArchiveContainer) Close() error {
var list errors.ErrorList
- return list.Add(c.Update(), c.base.Close()).Result()
+ return list.Add(c.Update(), c.fsacc.Close()).Result()
}
func (c *componentArchiveContainer) GetContext() cpi.Context {
@@ -130,26 +133,35 @@ func (c *componentArchiveContainer) Repository() cpi.Repository {
}
func (c *componentArchiveContainer) IsReadOnly() bool {
- return c.base.IsReadOnly()
+ return c.fsacc.IsReadOnly()
}
func (c *componentArchiveContainer) Update() error {
- return c.base.Update()
+ return c.fsacc.Update()
+}
+
+func (c *componentArchiveContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error {
+ if c.fsacc.IsReadOnly() {
+ return accessobj.ErrReadOnly
+ }
+ cur := c.fsacc.GetState().GetState().(*compdesc.ComponentDescriptor)
+ *cur = *cd.Copy()
+ return c.fsacc.Update()
}
func (c *componentArchiveContainer) GetDescriptor() *compdesc.ComponentDescriptor {
- if c.base.IsReadOnly() {
- return c.base.GetState().GetOriginalState().(*compdesc.ComponentDescriptor)
+ if c.fsacc.IsReadOnly() {
+ return c.fsacc.GetState().GetOriginalState().(*compdesc.ComponentDescriptor)
}
- return c.base.GetState().GetState().(*compdesc.ComponentDescriptor)
+ return c.fsacc.GetState().GetState().(*compdesc.ComponentDescriptor)
}
-func (c *componentArchiveContainer) GetBlobData(name string) (cpi.DataAccess, error) {
- return c.base.GetBlobDataByName(name)
+func (c *componentArchiveContainer) GetBlob(name string) (cpi.DataAccess, error) {
+ return c.fsacc.GetBlobDataByName(name)
}
func (c *componentArchiveContainer) GetStorageContext() cpi.StorageContext {
- return ocmhdlr.New(c.Repository(), c.impl.GetName(), &BlobSink{c.base}, Type)
+ return ocmhdlr.New(c.Repository(), c.base.GetName(), &BlobSink{c.fsacc}, Type)
}
type BlobSink struct {
@@ -164,11 +176,11 @@ func (s *BlobSink) AddBlob(blob blobaccess.BlobAccess) (string, error) {
return blob.Digest().String(), nil
}
-func (c *componentArchiveContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
+func (c *componentArchiveContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
if blob == nil {
return nil, errors.New("a resource has to be defined")
}
- err := c.base.AddBlob(blob)
+ err := c.fsacc.AddBlob(blob)
if err != nil {
return nil, err
}
diff --git a/pkg/contexts/ocm/repositories/comparch/format.go b/pkg/contexts/ocm/repositories/comparch/format.go
index 373d161f31..f4e7bb653b 100644
--- a/pkg/contexts/ocm/repositories/comparch/format.go
+++ b/pkg/contexts/ocm/repositories/comparch/format.go
@@ -135,5 +135,5 @@ func (h *formatHandler) Create(ctx cpi.ContextProvider, path string, opts access
// WriteToFilesystem writes the current object to a filesystem.
func (h *formatHandler) Write(obj *Object, path string, opts accessio.Options, mode vfs.FileMode) error {
- return h.FormatHandler.Write(obj.container.base.Access(), path, opts, mode)
+ return h.FormatHandler.Write(obj.container.fsacc.Access(), path, opts, mode)
}
diff --git a/pkg/contexts/ocm/repositories/comparch/repository.go b/pkg/contexts/ocm/repositories/comparch/repository.go
index 604e3bdeb2..7ab49eea54 100644
--- a/pkg/contexts/ocm/repositories/comparch/repository.go
+++ b/pkg/contexts/ocm/repositories/comparch/repository.go
@@ -15,25 +15,23 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localfsblob"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr"
ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/refmgmt"
"github.com/open-component-model/ocm/pkg/utils"
)
-type _RepositoryImplBase = cpi.RepositoryImplBase
-
type RepositoryImpl struct {
- _RepositoryImplBase
- lock sync.RWMutex
- arch *ComponentArchive
+ lock sync.RWMutex
+ bridge repocpi.RepositoryBridge
+ arch *ComponentArchive
+ nonref cpi.Repository
}
-var _ cpi.RepositoryImpl = (*RepositoryImpl)(nil)
+var _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil)
func NewRepository(ctx cpi.Context, s *RepositorySpec) (cpi.Repository, error) {
if s.GetPathFileSystem() == nil {
@@ -46,14 +44,26 @@ func NewRepository(ctx cpi.Context, s *RepositorySpec) (cpi.Repository, error) {
return a.AsRepository(), nil
}
-func newRepository(a *ComponentArchive) (main cpi.Repository, nonref cpi.Repository) {
+func newRepository(a *ComponentArchive) (main, nonref cpi.Repository) {
// close main cv abstraction on repository close -------v
- base := cpi.NewRepositoryImplBase(a.GetContext(), a.ComponentVersionAccess)
impl := &RepositoryImpl{
- _RepositoryImplBase: *base,
- arch: a,
+ arch: a,
}
- return cpi.NewRepository(impl), cpi.NewNoneRefRepositoryView(impl)
+ r := repocpi.NewRepository(impl, "comparch")
+ return r, impl.nonref
+}
+
+func (r *RepositoryImpl) Close() error {
+ return r.arch.container.Close()
+}
+
+func (r *RepositoryImpl) SetBridge(base repocpi.RepositoryBridge) {
+ r.bridge = base
+ r.nonref = repocpi.NewNoneRefRepositoryView(base)
+}
+
+func (r *RepositoryImpl) GetContext() cpi.Context {
+ return r.arch.GetContext()
}
func (r *RepositoryImpl) ComponentLister() cpi.ComponentLister {
@@ -118,25 +128,7 @@ func (r *RepositoryImpl) ExistsComponentVersion(name string, ref string) (bool,
return r.arch.GetName() == name && r.arch.GetVersion() == ref, nil
}
-func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) {
- r.lock.RLock()
- defer r.lock.RUnlock()
- ok, err := r.ExistsComponentVersion(name, version)
- if !ok {
- if err == nil {
- err = errors.ErrNotFound(cpi.KIND_COMPONENTVERSION, common.NewNameVersion(name, version).String(), Type)
- }
- return nil, err
- }
- c, err := newComponentAccess(r)
- if err != nil {
- return nil, err
- }
- defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed.
- return c.LookupVersion(version)
-}
-
-func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, error) {
+func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessInfo, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if r.arch == nil {
@@ -150,25 +142,38 @@ func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, erro
////////////////////////////////////////////////////////////////////////////////
-type _ComponentAccessImplBase = cpi.ComponentAccessImplBase
-
type ComponentAccessImpl struct {
- _ComponentAccessImplBase
+ base repocpi.ComponentAccessBridge
repo *RepositoryImpl
}
-var _ cpi.ComponentAccessImpl = (*ComponentAccessImpl)(nil)
+var _ repocpi.ComponentAccessImpl = (*ComponentAccessImpl)(nil)
-func newComponentAccess(r *RepositoryImpl) (cpi.ComponentAccess, error) {
- base, err := cpi.NewComponentAccessImplBase(r.GetContext(), r.arch.GetName(), r)
- if err != nil {
- return nil, err
- }
+func newComponentAccess(r *RepositoryImpl) (*repocpi.ComponentAccessInfo, error) {
impl := &ComponentAccessImpl{
- _ComponentAccessImplBase: *base,
- repo: r,
+ repo: r,
}
- return cpi.NewComponentAccess(impl, "component archive"), nil
+ return &repocpi.ComponentAccessInfo{impl, "component archive", true}, nil
+}
+
+func (c *ComponentAccessImpl) Close() error {
+ return nil
+}
+
+func (c *ComponentAccessImpl) SetBridge(base repocpi.ComponentAccessBridge) {
+ c.base = base
+}
+
+func (c *ComponentAccessImpl) GetParentBridge() repocpi.RepositoryViewManager {
+ return c.repo.bridge
+}
+
+func (c *ComponentAccessImpl) GetContext() cpi.Context {
+ return c.repo.GetContext()
+}
+
+func (c *ComponentAccessImpl) GetName() string {
+ return c.repo.arch.GetName()
}
func (c *ComponentAccessImpl) IsReadOnly() bool {
@@ -183,37 +188,14 @@ func (c *ComponentAccessImpl) HasVersion(vers string) (bool, error) {
return vers == c.repo.arch.GetVersion(), nil
}
-func (c *ComponentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) {
+func (c *ComponentAccessImpl) LookupVersion(version string) (*repocpi.ComponentVersionAccessInfo, error) {
if version != c.repo.arch.GetVersion() {
return nil, errors.ErrNotFound(cpi.KIND_COMPONENTVERSION, fmt.Sprintf("%s:%s", c.GetName(), c.repo.arch.GetVersion()))
}
return newComponentVersionAccess(c, version, false)
}
-func (c *ComponentAccessImpl) container(access cpi.ComponentVersionAccess) *componentArchiveContainer {
- mine, _ := support.GetComponentVersionContainer[*ComponentVersionContainer](access)
- if mine == nil || mine.comp != c {
- return nil
- }
- return mine.comp.repo.arch.container
-}
-
-func (c *ComponentAccessImpl) IsOwned(access cpi.ComponentVersionAccess) bool {
- return c.container(access) == c.repo.arch.container
-}
-
-func (c *ComponentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error {
- if access.GetName() != c.GetName() {
- return errors.ErrInvalid("component name", access.GetName())
- }
- mine := c.container(access)
- if mine == nil {
- return errors.Newf("component version not owned by component archive")
- }
- return nil
-}
-
-func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) {
+func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (*repocpi.ComponentVersionAccessInfo, error) {
if version != c.repo.arch.GetVersion() {
return nil, errors.ErrNotSupported(cpi.KIND_COMPONENTVERSION, version, fmt.Sprintf("component archive %s:%s", c.GetName(), c.repo.arch.GetVersion()))
}
@@ -226,26 +208,21 @@ func (c *ComponentAccessImpl) NewVersion(version string, overrides ...bool) (cpi
////////////////////////////////////////////////////////////////////////////////
type ComponentVersionContainer struct {
- impl support.ComponentVersionAccessImpl
+ impl repocpi.ComponentVersionAccessBridge
comp *ComponentAccessImpl
descriptor *compdesc.ComponentDescriptor
}
-var _ support.ComponentVersionContainer = (*ComponentVersionContainer)(nil)
+var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil)
-func newComponentVersionAccess(comp *ComponentAccessImpl, version string, persistent bool) (cpi.ComponentVersionAccess, error) {
+func newComponentVersionAccess(comp *ComponentAccessImpl, version string, persistent bool) (*repocpi.ComponentVersionAccessInfo, error) {
c, err := newComponentVersionContainer(comp)
if err != nil {
return nil, err
}
- impl, err := support.NewComponentVersionAccessImpl(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext()))
- if err != nil {
- c.Close()
- return nil, err
- }
- return cpi.NewComponentVersionAccess(impl), nil
+ return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil
}
func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionContainer, error) {
@@ -255,12 +232,12 @@ func newComponentVersionContainer(comp *ComponentAccessImpl) (*ComponentVersionC
}, nil
}
-func (c *ComponentVersionContainer) SetImplementation(impl support.ComponentVersionAccessImpl) {
+func (c *ComponentVersionContainer) SetBridge(impl repocpi.ComponentVersionAccessBridge) {
c.impl = impl
}
-func (c *ComponentVersionContainer) GetParentViewManager() cpi.ComponentAccessViewManager {
- return c.comp
+func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBridge {
+ return c.comp.base
}
func (c *ComponentVersionContainer) Close() error {
@@ -285,23 +262,28 @@ func (c *ComponentVersionContainer) Update() error {
return c.comp.repo.arch.container.Update()
}
+func (c *ComponentVersionContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error {
+ *c.descriptor = *cd
+ return c.Update()
+}
+
func (c *ComponentVersionContainer) GetDescriptor() *compdesc.ComponentDescriptor {
return c.descriptor
}
-func (c *ComponentVersionContainer) GetBlobData(name string) (cpi.DataAccess, error) {
- return c.comp.repo.arch.container.GetBlobData(name)
+func (c *ComponentVersionContainer) GetBlob(name string) (cpi.DataAccess, error) {
+ return c.comp.repo.arch.container.GetBlob(name)
}
func (c *ComponentVersionContainer) GetStorageContext() cpi.StorageContext {
- return ocmhdlr.New(c.Repository(), c.comp.GetName(), &BlobSink{c.comp.repo.arch.container.base}, Type)
+ return ocmhdlr.New(c.Repository(), c.comp.GetName(), &BlobSink{c.comp.repo.arch.container.fsacc}, Type)
}
-func (c *ComponentVersionContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
+func (c *ComponentVersionContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
if blob == nil {
return nil, errors.New("a resource has to be defined")
}
- err := c.comp.repo.arch.container.base.AddBlob(blob)
+ err := c.comp.repo.arch.container.fsacc.AddBlob(blob)
if err != nil {
return nil, err
}
diff --git a/pkg/contexts/ocm/repositories/ctf/format.go b/pkg/contexts/ocm/repositories/ctf/format.go
index 5b1bc27e76..f2bc385214 100644
--- a/pkg/contexts/ocm/repositories/ctf/format.go
+++ b/pkg/contexts/ocm/repositories/ctf/format.go
@@ -47,7 +47,7 @@ func Open(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode v
if err != nil {
return nil, err
}
- return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r)
+ return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r), nil
}
func Create(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode vfs.FileMode, opts ...accessio.Option) (cpi.Repository, error) {
@@ -55,7 +55,7 @@ func Create(ctx cpi.ContextProvider, acc accessobj.AccessMode, path string, mode
if err != nil {
return nil, err
}
- return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r)
+ return genericocireg.NewRepository(cpi.FromProvider(ctx), nil, r), nil
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/pkg/contexts/ocm/repositories/ctf/repo_test.go b/pkg/contexts/ocm/repositories/ctf/repo_test.go
index ee9651615d..6553239d65 100644
--- a/pkg/contexts/ocm/repositories/ctf/repo_test.go
+++ b/pkg/contexts/ocm/repositories/ctf/repo_test.go
@@ -23,6 +23,7 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ctf"
"github.com/open-component-model/ocm/pkg/contexts/ocm/resourcetypes"
"github.com/open-component-model/ocm/pkg/mime"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
)
const COMPONENT = "github.com/mandelsoft/ocm"
@@ -36,6 +37,64 @@ var _ = Describe("access method", func() {
fs = memoryfs.New()
})
+ It("adds naked component version and later lookup", func() {
+ final := Finalizer{}
+ defer Defer(final.Finalize)
+
+ a := Must(ctf.Create(ctx, accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, "ctf", 0o700, accessio.PathFileSystem(fs)))
+ final.Close(a, "repository")
+ c := Must(a.LookupComponent(COMPONENT))
+ final.Close(c, "component")
+
+ cv := Must(c.NewVersion(VERSION))
+ final.Close(cv, "version")
+
+ MustBeSuccessful(c.AddVersion(cv))
+ MustBeSuccessful(final.Finalize())
+
+ refmgmt.AllocLog.Trace("opening ctf")
+ a = Must(ctf.Open(ctx, accessobj.ACC_READONLY, "ctf", 0o700, accessio.PathFileSystem(fs)))
+ final.Close(a)
+
+ refmgmt.AllocLog.Trace("lookup component")
+ c = Must(a.LookupComponent(COMPONENT))
+ final.Close(c)
+
+ refmgmt.AllocLog.Trace("lookup version")
+ cv = Must(c.LookupVersion(VERSION))
+ final.Close(cv)
+
+ refmgmt.AllocLog.Trace("closing")
+ MustBeSuccessful(final.Finalize())
+ })
+
+ It("adds naked component version and later shortcut lookup", func() {
+ final := Finalizer{}
+ defer Defer(final.Finalize)
+
+ a := Must(ctf.Create(ctx, accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, "ctf", 0o700, accessio.PathFileSystem(fs)))
+ final.Close(a, "repository")
+ c := Must(a.LookupComponent(COMPONENT))
+ final.Close(c, "component")
+
+ cv := Must(c.NewVersion(VERSION))
+ final.Close(cv, "version")
+
+ MustBeSuccessful(c.AddVersion(cv))
+ MustBeSuccessful(final.Finalize())
+
+ refmgmt.AllocLog.Trace("opening ctf")
+ a = Must(ctf.Open(ctx, accessobj.ACC_READONLY, "ctf", 0o700, accessio.PathFileSystem(fs)))
+ final.Close(a)
+
+ refmgmt.AllocLog.Trace("lookup component version")
+ cv = Must(a.LookupComponentVersion(COMPONENT, VERSION))
+ final.Close(cv)
+
+ refmgmt.AllocLog.Trace("closing")
+ MustBeSuccessful(final.Finalize())
+ })
+
It("adds component version", func() {
final := Finalizer{}
defer Defer(final.Finalize)
diff --git a/pkg/contexts/ocm/repositories/genericocireg/component.go b/pkg/contexts/ocm/repositories/genericocireg/component.go
index aacadd39f1..9343493847 100644
--- a/pkg/contexts/ocm/repositories/genericocireg/component.go
+++ b/pkg/contexts/ocm/repositories/genericocireg/component.go
@@ -5,18 +5,17 @@
package genericocireg
import (
- "fmt"
"strings"
"github.com/Masterminds/semver/v3"
- "github.com/open-component-model/ocm/pkg/common/accessio"
"github.com/open-component-model/ocm/pkg/common/accessobj"
"github.com/open-component-model/ocm/pkg/contexts/oci"
"github.com/open-component-model/ocm/pkg/contexts/oci/artdesc"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/refmgmt"
"github.com/open-component-model/ocm/pkg/utils"
)
@@ -24,41 +23,51 @@ const META_SEPARATOR = ".build-"
////////////////////////////////////////////////////////////////////////////////
-type _ComponentAccessImplBase = cpi.ComponentAccessImplBase
-
type componentAccessImpl struct {
- _ComponentAccessImplBase
+ bridge repocpi.ComponentAccessBridge
repo *RepositoryImpl
name string
namespace oci.NamespaceAccess
}
-func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.ComponentAccess, error) {
+func newComponentAccess(repo *RepositoryImpl, name string, main bool) (*repocpi.ComponentAccessInfo, error) {
mapped, err := repo.MapComponentNameToNamespace(name)
if err != nil {
return nil, err
}
-
- base, err := cpi.NewComponentAccessImplBase(repo.GetContext(), name, repo)
- if err != nil {
- return nil, err
- }
namespace, err := repo.ocirepo.LookupNamespace(mapped)
if err != nil {
- base.Close()
return nil, err
}
impl := &componentAccessImpl{
- _ComponentAccessImplBase: *base,
- repo: repo,
- name: name,
- namespace: namespace,
+ repo: repo,
+ name: name,
+ namespace: namespace,
}
- return cpi.NewComponentAccess(impl, "OCM component[OCI]"), nil
+ return &repocpi.ComponentAccessInfo{impl, "OCM component[OCI]", main}, nil
}
func (c *componentAccessImpl) Close() error {
- return accessio.Close(c.namespace, c._ComponentAccessImplBase)
+ refmgmt.AllocLog.Trace("closing component [OCI]", "name", c.name)
+ err := c.namespace.Close()
+ refmgmt.AllocLog.Trace("closed component [OCI]", "name", c.name)
+ return err
+}
+
+func (c *componentAccessImpl) SetBridge(base repocpi.ComponentAccessBridge) {
+ c.bridge = base
+}
+
+func (c *componentAccessImpl) GetParentBridge() repocpi.RepositoryViewManager {
+ return c.repo.bridge
+}
+
+func (c *componentAccessImpl) GetContext() cpi.Context {
+ return c.repo.GetContext()
+}
+
+func (c *componentAccessImpl) GetName() string {
+ return c.name
}
////////////////////////////////////////////////////////////////////////////////
@@ -122,13 +131,7 @@ func (c *componentAccessImpl) HasVersion(vers string) (bool, error) {
return false, err
}
-func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) {
- v, err := c.repo.View()
- if err != nil {
- return nil, err
- }
- defer v.Close()
-
+func (c *componentAccessImpl) LookupVersion(version string) (*repocpi.ComponentVersionAccessInfo, error) {
acc, err := c.namespace.GetArtifact(toTag(version))
if err != nil {
if errors.IsErrNotFound(err) {
@@ -139,41 +142,7 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio
return newComponentVersionAccess(accessobj.ACC_WRITABLE, c, version, acc, true)
}
-func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess) *ComponentVersionContainer {
- mine, _ := support.GetComponentVersionContainer[*ComponentVersionContainer](access)
- if mine == nil || mine.comp != c {
- return nil
- }
- return mine
-}
-
-func (c *componentAccessImpl) IsOwned(access cpi.ComponentVersionAccess) bool {
- return c.versionContainer(access) != nil
-}
-
-func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error {
- if access.GetName() != c.GetName() {
- return errors.ErrInvalid("component name", access.GetName())
- }
- mine := c.versionContainer(access)
- if mine == nil {
- return fmt.Errorf("cannot add component version: component version access %s not created for target", access.GetName()+":"+access.GetVersion())
- }
- ok := mine.impl.EnablePersistence()
- if !ok {
- return fmt.Errorf("version has been discarded")
- }
- // delayed update in close is not done for composition mode
- return mine.impl.Update(!mine.impl.UseDirectAccess())
-}
-
-func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) {
- v, err := c.View(false)
- if err != nil {
- return nil, err
- }
- defer v.Close()
-
+func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (*repocpi.ComponentVersionAccessInfo, error) {
override := utils.Optional(overrides...)
acc, err := c.namespace.GetArtifact(toTag(version))
if err == nil {
diff --git a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go
index 9ea2eb38fa..808f737f2b 100644
--- a/pkg/contexts/ocm/repositories/genericocireg/componentversion.go
+++ b/pkg/contexts/ocm/repositories/genericocireg/componentversion.go
@@ -22,35 +22,29 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/ociartifact"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/relativeociref"
"github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compatattr"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr"
ocihdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/oci"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/generics"
"github.com/open-component-model/ocm/pkg/refmgmt"
)
-// newComponentVersionAccess creates an component access for the artifact access, if this fails the artifact acess is closed.
-func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess, persistent bool) (cpi.ComponentVersionAccess, error) {
+// newComponentVersionAccess creates a component access for the artifact access, if this fails the artifact acess is closed.
+func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess, persistent bool) (*repocpi.ComponentVersionAccessInfo, error) {
c, err := newComponentVersionContainer(mode, comp, version, access)
if err != nil {
return nil, err
}
- impl, err := support.NewComponentVersionAccessImpl(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext()))
- if err != nil {
- c.Close()
- return nil, err
- }
- return cpi.NewComponentVersionAccess(impl), nil
+ return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil
}
// //////////////////////////////////////////////////////////////////////////////
type ComponentVersionContainer struct {
- impl support.ComponentVersionAccessImpl
+ bridge repocpi.ComponentVersionAccessBridge
comp *componentAccessImpl
version string
@@ -59,7 +53,7 @@ type ComponentVersionContainer struct {
state accessobj.State
}
-var _ support.ComponentVersionContainer = (*ComponentVersionContainer)(nil)
+var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil)
func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess) (*ComponentVersionContainer, error) {
m := access.ManifestAccess()
@@ -81,12 +75,12 @@ func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAcce
}, nil
}
-func (c *ComponentVersionContainer) SetImplementation(impl support.ComponentVersionAccessImpl) {
- c.impl = impl
+func (c *ComponentVersionContainer) SetBridge(impl repocpi.ComponentVersionAccessBridge) {
+ c.bridge = impl
}
-func (c *ComponentVersionContainer) GetParentViewManager() cpi.ComponentAccessViewManager {
- return c.comp
+func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBridge {
+ return c.comp.bridge
}
func (c *ComponentVersionContainer) Close() error {
@@ -165,6 +159,12 @@ func (c *ComponentVersionContainer) GetInexpensiveContentVersionIdentity(a cpi.A
return ""
}
+func (c *ComponentVersionContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error {
+ cur := c.GetDescriptor()
+ *cur = *cd
+ return c.Update()
+}
+
func (c *ComponentVersionContainer) Update() error {
logger := Logger(c.GetContext()).WithValues("cv", common.NewNameVersion(c.comp.name, c.version))
err := c.Check()
@@ -264,7 +264,7 @@ func (c *ComponentVersionContainer) GetDescriptor() *compdesc.ComponentDescripto
return c.state.GetState().(*compdesc.ComponentDescriptor)
}
-func (c *ComponentVersionContainer) GetBlobData(name string) (cpi.DataAccess, error) {
+func (c *ComponentVersionContainer) GetBlob(name string) (cpi.DataAccess, error) {
return c.manifest.GetBlob(digest.Digest(name))
}
@@ -272,7 +272,7 @@ func (c *ComponentVersionContainer) GetStorageContext() cpi.StorageContext {
return ocihdlr.New(c.comp.GetName(), c.Repository(), c.comp.repo.ocirepo.GetSpecification().GetKind(), c.comp.repo.ocirepo, c.comp.namespace, c.manifest)
}
-func (c *ComponentVersionContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
+func (c *ComponentVersionContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
if blob == nil {
return nil, errors.New("a resource has to be defined")
}
diff --git a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go
index 27224863c5..7d96fc9b32 100644
--- a/pkg/contexts/ocm/repositories/genericocireg/repo_test.go
+++ b/pkg/contexts/ocm/repositories/genericocireg/repo_test.go
@@ -39,6 +39,7 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/digester/digesters/artifact"
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg"
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg/componentmapping"
@@ -83,7 +84,7 @@ var _ = Describe("component repository mapping", func() {
defer Defer(finalize.Finalize)
repo := finalizer.ClosingWith(&finalize, Must(DefaultContext.RepositoryForSpec(spec)))
- impl := Must(cpi.GetRepositoryImplementation(repo))
+ impl := Must(repocpi.GetRepositoryImplementation(repo))
Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl"))
comp := finalizer.ClosingWith(&finalize, Must(repo.LookupComponent(COMPONENT)))
@@ -123,7 +124,7 @@ var _ = Describe("component repository mapping", func() {
// create repository
repo := finalizer.ClosingWith(&finalize, Must(DefaultContext.RepositoryForSpec(spec)))
- impl := Must(cpi.GetRepositoryImplementation(repo))
+ impl := Must(repocpi.GetRepositoryImplementation(repo))
Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl"))
comp := finalizer.ClosingWith(&finalize, Must(repo.LookupComponent(COMPONENT)))
@@ -172,7 +173,7 @@ var _ = Describe("component repository mapping", func() {
// create repository
repo := finalizer.ClosingWith(&finalize, Must(ctx.RepositoryForSpec(spec)))
- impl := Must(cpi.GetRepositoryImplementation(repo))
+ impl := Must(repocpi.GetRepositoryImplementation(repo))
Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl"))
comp := finalizer.ClosingWith(&finalize, Must(repo.LookupComponent(COMPONENT)))
@@ -222,7 +223,7 @@ var _ = Describe("component repository mapping", func() {
// create repository
repo := finalizer.ClosingWith(&finalize, Must(ctx.RepositoryForSpec(spec)))
- impl := Must(cpi.GetRepositoryImplementation(repo))
+ impl := Must(repocpi.GetRepositoryImplementation(repo))
Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl"))
ocirepo := genericocireg.GetOCIRepository(repo)
Expect(ocirepo).NotTo(BeNil())
@@ -278,7 +279,7 @@ var _ = Describe("component repository mapping", func() {
defer Defer(finalize.Finalize, "finalize open elements")
repo := finalizer.ClosingWith(&finalize, Must(DefaultContext.RepositoryForSpec(spec)))
- impl := Must(cpi.GetRepositoryImplementation(repo))
+ impl := Must(repocpi.GetRepositoryImplementation(repo))
Expect(reflect.TypeOf(impl).String()).To(Equal("*genericocireg.RepositoryImpl"))
nested := finalize.Nested()
diff --git a/pkg/contexts/ocm/repositories/genericocireg/repository.go b/pkg/contexts/ocm/repositories/genericocireg/repository.go
index 05ec84b968..88a5824eb2 100644
--- a/pkg/contexts/ocm/repositories/genericocireg/repository.go
+++ b/pkg/contexts/ocm/repositories/genericocireg/repository.go
@@ -15,9 +15,9 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/oci"
ocicpi "github.com/open-component-model/ocm/pkg/contexts/oci/cpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/genericocireg/componentmapping"
"github.com/open-component-model/ocm/pkg/errors"
- "github.com/open-component-model/ocm/pkg/refmgmt"
"github.com/open-component-model/ocm/pkg/utils"
)
@@ -29,7 +29,7 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository {
if o, ok := r.(OCIBasedRepository); ok {
return o.OCIRepository()
}
- impl, err := cpi.GetRepositoryImplementation(r)
+ impl, err := repocpi.GetRepositoryImplementation(r)
if err != nil {
return nil
}
@@ -39,29 +39,39 @@ func GetOCIRepository(r cpi.Repository) ocicpi.Repository {
return nil
}
-type _RepositoryImplBase = cpi.RepositoryImplBase
-
type RepositoryImpl struct {
- _RepositoryImplBase
+ bridge repocpi.RepositoryBridge
+ ctx cpi.Context
meta ComponentRepositoryMeta
nonref cpi.Repository
ocirepo oci.Repository
}
var (
- _ cpi.RepositoryImpl = (*RepositoryImpl)(nil)
+ _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil)
_ credentials.ConsumerIdentityProvider = (*RepositoryImpl)(nil)
)
-func NewRepository(ctx cpi.Context, meta *ComponentRepositoryMeta, ocirepo oci.Repository) (cpi.Repository, error) {
+func NewRepository(ctx cpi.Context, meta *ComponentRepositoryMeta, ocirepo oci.Repository) cpi.Repository {
impl := &RepositoryImpl{
- _RepositoryImplBase: *cpi.NewRepositoryImplBase(ctx.OCMContext()),
- meta: *DefaultComponentRepositoryMeta(meta),
- ocirepo: ocirepo,
+ ctx: ctx,
+ meta: *DefaultComponentRepositoryMeta(meta),
+ ocirepo: ocirepo,
}
- impl.nonref = cpi.NewNoneRefRepositoryView(impl)
- r := cpi.NewRepository(impl, "OCM repo[OCI]")
- return r, nil
+ return repocpi.NewRepository(impl, "OCM repo[OCI]")
+}
+
+func (r *RepositoryImpl) Close() error {
+ return r.ocirepo.Close()
+}
+
+func (r *RepositoryImpl) SetBridge(base repocpi.RepositoryBridge) {
+ r.bridge = base
+ r.nonref = repocpi.NewNoneRefRepositoryView(base)
+}
+
+func (r *RepositoryImpl) GetContext() cpi.Context {
+ return r.ctx
}
func (r *RepositoryImpl) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity {
@@ -82,10 +92,6 @@ func (r *RepositoryImpl) GetIdentityMatcher() string {
return ""
}
-func (r *RepositoryImpl) Close() error {
- return r.ocirepo.Close()
-}
-
func (r *RepositoryImpl) OCIRepository() ocicpi.Repository {
return r.ocirepo
}
@@ -169,19 +175,10 @@ func (r *RepositoryImpl) ExistsComponentVersion(name string, version string) (bo
return false, nil
}
-func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, error) {
+func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessInfo, error) {
return newComponentAccess(r, name, true)
}
-func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) {
- c, err := newComponentAccess(r, name, false)
- if err != nil {
- return nil, err
- }
- defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed.
- return c.LookupVersion(version)
-}
-
func (r *RepositoryImpl) MapComponentNameToNamespace(name string) (string, error) {
switch r.meta.ComponentNameMapping {
case OCIRegistryURLPathMapping, "":
diff --git a/pkg/contexts/ocm/repositories/genericocireg/type.go b/pkg/contexts/ocm/repositories/genericocireg/type.go
index b90282880d..016cee5c51 100644
--- a/pkg/contexts/ocm/repositories/genericocireg/type.go
+++ b/pkg/contexts/ocm/repositories/genericocireg/type.go
@@ -162,7 +162,7 @@ func (s *RepositorySpec) Repository(ctx cpi.Context, creds credentials.Credentia
if err != nil {
return nil, err
}
- return NewRepository(ctx, &s.ComponentRepositoryMeta, r)
+ return NewRepository(ctx, &s.ComponentRepositoryMeta, r), nil
}
func DefaultComponentRepositoryMeta(meta *ComponentRepositoryMeta) *ComponentRepositoryMeta {
diff --git a/pkg/contexts/ocm/repositories/virtual/component.go b/pkg/contexts/ocm/repositories/virtual/component.go
index 3cd11b627e..b09ad99ee2 100644
--- a/pkg/contexts/ocm/repositories/virtual/component.go
+++ b/pkg/contexts/ocm/repositories/virtual/component.go
@@ -5,33 +5,47 @@
package virtual
import (
- "fmt"
-
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/utils"
)
-type _ComponentAccessImplBase = cpi.ComponentAccessImplBase
-
type componentAccessImpl struct {
- _ComponentAccessImplBase
+ bridge repocpi.ComponentAccessBridge
+
repo *RepositoryImpl
name string
}
-func newComponentAccess(repo *RepositoryImpl, name string, main bool) (cpi.ComponentAccess, error) {
- base, err := cpi.NewComponentAccessImplBase(repo.GetContext(), name, repo)
- if err != nil {
- return nil, err
- }
+var _ repocpi.ComponentAccessImpl = (*componentAccessImpl)(nil)
+
+func newComponentAccess(repo *RepositoryImpl, name string, main bool) (*repocpi.ComponentAccessInfo, error) {
impl := &componentAccessImpl{
- _ComponentAccessImplBase: *base,
- repo: repo,
- name: name,
+ repo: repo,
+ name: name,
}
- return cpi.NewComponentAccess(impl, "OCM component[Simple]"), nil
+ return &repocpi.ComponentAccessInfo{impl, "OCM component[Simple]", main}, nil
+}
+
+func (c *componentAccessImpl) Close() error {
+ return nil
+}
+
+func (c *componentAccessImpl) SetBridge(base repocpi.ComponentAccessBridge) {
+ c.bridge = base
+}
+
+func (c *componentAccessImpl) GetParentBridge() repocpi.RepositoryViewManager {
+ return c.repo.bridge
+}
+
+func (c *componentAccessImpl) GetContext() cpi.Context {
+ return c.repo.GetContext()
+}
+
+func (c *componentAccessImpl) GetName() string {
+ return c.name
}
func (c *componentAccessImpl) ListVersions() ([]string, error) {
@@ -46,7 +60,7 @@ func (c *componentAccessImpl) IsReadOnly() bool {
return c.repo.access.IsReadOnly()
}
-func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersionAccess, error) {
+func (c *componentAccessImpl) LookupVersion(version string) (*repocpi.ComponentVersionAccessInfo, error) {
ok, err := c.HasVersion(version)
if err != nil {
return nil, err
@@ -54,48 +68,10 @@ func (c *componentAccessImpl) LookupVersion(version string) (cpi.ComponentVersio
if !ok {
return nil, cpi.ErrComponentVersionNotFoundWrap(err, c.name, version)
}
- v, err := c._ComponentAccessImplBase.View()
- if err != nil {
- return nil, err
- }
- defer v.Close()
-
return newComponentVersionAccess(c, version, true)
}
-func (c *componentAccessImpl) versionContainer(access cpi.ComponentVersionAccess) *ComponentVersionContainer {
- mine, _ := support.GetComponentVersionContainer[*ComponentVersionContainer](access)
- if mine == nil || mine.comp != c {
- return nil
- }
- return mine
-}
-
-func (c *componentAccessImpl) IsOwned(access cpi.ComponentVersionAccess) bool {
- return c.versionContainer(access) != nil
-}
-
-func (c *componentAccessImpl) AddVersion(access cpi.ComponentVersionAccess) error {
- if access.GetName() != c.GetName() {
- return errors.ErrInvalid("component name", access.GetName())
- }
- mine := c.versionContainer(access)
- if mine == nil {
- return fmt.Errorf("cannot add component version: component version access %s not created for target", access.GetName()+":"+access.GetVersion())
- }
- mine.impl.EnablePersistence()
-
- // delayed update in close is not done for composition mode
- return mine.impl.Update(!mine.impl.UseDirectAccess())
-}
-
-func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (cpi.ComponentVersionAccess, error) {
- v, err := c.View(false)
- if err != nil {
- return nil, err
- }
- defer v.Close()
-
+func (c *componentAccessImpl) NewVersion(version string, overrides ...bool) (*repocpi.ComponentVersionAccessInfo, error) {
override := utils.Optional(overrides...)
ok, err := c.HasVersion(version)
if err == nil && ok {
diff --git a/pkg/contexts/ocm/repositories/virtual/componentversion.go b/pkg/contexts/ocm/repositories/virtual/componentversion.go
index 0e755e0a7f..a74f4c1dbd 100644
--- a/pkg/contexts/ocm/repositories/virtual/componentversion.go
+++ b/pkg/contexts/ocm/repositories/virtual/componentversion.go
@@ -8,18 +8,17 @@ import (
"github.com/open-component-model/ocm/pkg/common/accessio"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localblob"
"github.com/open-component-model/ocm/pkg/contexts/ocm/accessmethods/localfsblob"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs/compositionmodeattr"
ocmhdlr "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/handlers/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/accspeccpi"
- "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/support"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/refmgmt"
)
// newComponentVersionAccess creates a component access for the artifact access, if this fails the artifact acess is closed.
-func newComponentVersionAccess(comp *componentAccessImpl, version string, persistent bool) (cpi.ComponentVersionAccess, error) {
+func newComponentVersionAccess(comp *componentAccessImpl, version string, persistent bool) (*repocpi.ComponentVersionAccessInfo, error) {
access, err := comp.repo.access.GetComponentVersion(comp.GetName(), version)
if err != nil {
return nil, err
@@ -28,25 +27,20 @@ func newComponentVersionAccess(comp *componentAccessImpl, version string, persis
if err != nil {
return nil, err
}
- impl, err := support.NewComponentVersionAccessImpl(comp.GetName(), version, c, true, persistent, !compositionmodeattr.Get(comp.GetContext()))
- if err != nil {
- c.Close()
- return nil, err
- }
- return cpi.NewComponentVersionAccess(impl), nil
+ return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil
}
// //////////////////////////////////////////////////////////////////////////////
type ComponentVersionContainer struct {
- impl support.ComponentVersionAccessImpl
+ bridge repocpi.ComponentVersionAccessBridge
comp *componentAccessImpl
version string
access VersionAccess
}
-var _ support.ComponentVersionContainer = (*ComponentVersionContainer)(nil)
+var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil)
func newComponentVersionContainer(comp *componentAccessImpl, version string, access VersionAccess) (*ComponentVersionContainer, error) {
return &ComponentVersionContainer{
@@ -56,12 +50,12 @@ func newComponentVersionContainer(comp *componentAccessImpl, version string, acc
}, nil
}
-func (c *ComponentVersionContainer) SetImplementation(impl support.ComponentVersionAccessImpl) {
- c.impl = impl
+func (c *ComponentVersionContainer) SetBridge(base repocpi.ComponentVersionAccessBridge) {
+ c.bridge = base
}
-func (c *ComponentVersionContainer) GetParentViewManager() cpi.ComponentAccessViewManager {
- return c.comp
+func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBridge {
+ return c.comp.bridge
}
func (c *ComponentVersionContainer) Close() error {
@@ -140,11 +134,17 @@ func (c *ComponentVersionContainer) Update() error {
return c.access.Update()
}
+func (c *ComponentVersionContainer) SetDescriptor(cd *compdesc.ComponentDescriptor) error {
+ cur := c.access.GetDescriptor()
+ *cur = *cd
+ return c.access.Update()
+}
+
func (c *ComponentVersionContainer) GetDescriptor() *compdesc.ComponentDescriptor {
return c.access.GetDescriptor()
}
-func (c *ComponentVersionContainer) GetBlobData(name string) (cpi.DataAccess, error) {
+func (c *ComponentVersionContainer) GetBlob(name string) (cpi.DataAccess, error) {
return c.access.GetBlob(name)
}
@@ -152,7 +152,7 @@ func (c *ComponentVersionContainer) GetStorageContext() cpi.StorageContext {
return ocmhdlr.New(c.Repository(), c.comp.GetName(), c.access, Type, c.access)
}
-func (c *ComponentVersionContainer) AddBlobFor(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
+func (c *ComponentVersionContainer) AddBlob(blob cpi.BlobAccess, refName string, global cpi.AccessSpec) (cpi.AccessSpec, error) {
if c.IsReadOnly() {
return nil, accessio.ErrReadOnly
}
diff --git a/pkg/contexts/ocm/repositories/virtual/repository.go b/pkg/contexts/ocm/repositories/virtual/repository.go
index 69f5465161..a1cc262e70 100644
--- a/pkg/contexts/ocm/repositories/virtual/repository.go
+++ b/pkg/contexts/ocm/repositories/virtual/repository.go
@@ -6,41 +6,37 @@ package virtual
import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
- "github.com/open-component-model/ocm/pkg/refmgmt"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi/repocpi"
)
-type _RepositoryImplBase = cpi.RepositoryImplBase
-
type RepositoryImpl struct {
- _RepositoryImplBase
+ bridge repocpi.RepositoryBridge
+ ctx cpi.Context
access Access
nonref cpi.Repository
}
-var _ cpi.RepositoryImpl = (*RepositoryImpl)(nil)
+var _ repocpi.RepositoryImpl = (*RepositoryImpl)(nil)
func NewRepository(ctx cpi.Context, acc Access) cpi.Repository {
impl := &RepositoryImpl{
- _RepositoryImplBase: *cpi.NewRepositoryImplBase(ctx.OCMContext()),
- access: acc,
+ ctx: ctx,
+ access: acc,
}
- impl.nonref = cpi.NewNoneRefRepositoryView(impl)
- r := cpi.NewRepository(impl, "OCM repo[Simple]")
- return r
+ return repocpi.NewRepository(impl, "OCM repo[Simple]")
}
-/*
-func (r *RepositoryImpl) GetConsumerId(uctx ...credentials.UsageContext) credentials.ConsumerIdentity {
- return nil
+func (r *RepositoryImpl) Close() error {
+ return r.access.Close()
}
-func (r *RepositoryImpl) GetIdentityMatcher() string {
- return ""
+func (r *RepositoryImpl) SetBridge(base repocpi.RepositoryBridge) {
+ r.bridge = base
+ r.nonref = repocpi.NewNoneRefRepositoryView(base)
}
-*/
-func (r *RepositoryImpl) Close() error {
- return r.access.Close()
+func (r *RepositoryImpl) GetContext() cpi.Context {
+ return r.ctx
}
func (r *RepositoryImpl) GetSpecification() cpi.RepositorySpec {
@@ -58,15 +54,6 @@ func (r *RepositoryImpl) ExistsComponentVersion(name string, version string) (bo
return r.access.ExistsComponentVersion(name, version)
}
-func (r *RepositoryImpl) LookupComponent(name string) (cpi.ComponentAccess, error) {
+func (r *RepositoryImpl) LookupComponent(name string) (*repocpi.ComponentAccessInfo, error) {
return newComponentAccess(r, name, true)
}
-
-func (r *RepositoryImpl) LookupComponentVersion(name string, version string) (cpi.ComponentVersionAccess, error) {
- c, err := newComponentAccess(r, name, false)
- if err != nil {
- return nil, err
- }
- defer refmgmt.PropagateCloseTemporary(&err, c) // temporary component object not exposed.
- return c.LookupVersion(version)
-}
diff --git a/pkg/optionutils/pointer.go b/pkg/optionutils/pointer.go
index a982b1c6b6..ae5298b76d 100644
--- a/pkg/optionutils/pointer.go
+++ b/pkg/optionutils/pointer.go
@@ -16,3 +16,9 @@ func AsValue[T any](p *T) T {
}
return r
}
+
+func ApplyOption[T any](opt *T, tgt **T) {
+ if opt != nil {
+ *tgt = opt
+ }
+}
diff --git a/pkg/refmgmt/refcloser.go b/pkg/refmgmt/refcloser.go
index 904c5ad40b..4b1e1fea20 100644
--- a/pkg/refmgmt/refcloser.go
+++ b/pkg/refmgmt/refcloser.go
@@ -115,6 +115,7 @@ func CloseTemporary(c io.Closer) error {
if !Lazy(c) {
return errors.ErrNotSupported("lazy mode")
}
+ AllocLog.Trace("close temporary ref")
return c.Close()
}
diff --git a/pkg/refmgmt/refmgmt.go b/pkg/refmgmt/refmgmt.go
index 5326965b4d..211d4b1654 100644
--- a/pkg/refmgmt/refmgmt.go
+++ b/pkg/refmgmt/refmgmt.go
@@ -14,7 +14,7 @@ import (
var ALLOC_REALM = logging.DefineSubRealm("reference counting", "refcnt")
-var allocLog = logging.DynamicLogger(ALLOC_REALM)
+var AllocLog = logging.DynamicLogger(ALLOC_REALM)
type Allocatable interface {
Ref() error
@@ -83,7 +83,7 @@ func (c *refMgmt) Ref() error {
return ErrClosed
}
c.refcount++
- allocLog.Trace("ref", "name", c.name, "refcnt", c.refcount)
+ AllocLog.Trace("ref", "name", c.name, "refcnt", c.refcount)
return nil
}
@@ -97,7 +97,7 @@ func (c *refMgmt) Unref() error {
var err error
c.refcount--
- allocLog.Trace("unref", "name", c.name, "refcnt", c.refcount)
+ AllocLog.Trace("unref", "name", c.name, "refcnt", c.refcount)
if c.refcount <= 0 {
for _, f := range c.before {
f.Cleanup()
@@ -135,13 +135,14 @@ func (c *refMgmt) UnrefLast() error {
}
if c.refcount > 1 {
+ AllocLog.Trace("unref last failed", "name", c.name, "pending", c.refcount)
return errors.ErrStillInUseWrap(errors.Newf("%d reference(s) pending", c.refcount), c.name)
}
var err error
c.refcount--
- allocLog.Trace("unref last", "name", c.name, "refcnt", c.refcount)
+ AllocLog.Trace("unref last", "name", c.name, "refcnt", c.refcount)
if c.refcount <= 0 {
for _, f := range c.before {
f.Cleanup()
@@ -154,7 +155,7 @@ func (c *refMgmt) UnrefLast() error {
}
if err != nil {
- allocLog.Trace("cleanup last failed", "name", c.name, "error", err.Error())
+ AllocLog.Trace("cleanup last failed", "name", c.name, "error", err.Error())
return errors.Wrapf(err, "unable to cleanup %s while unref last", c.name)
}
diff --git a/pkg/refmgmt/resource/resource.go b/pkg/refmgmt/resource/resource.go
index 9b4f53c6fd..80626b44b7 100644
--- a/pkg/refmgmt/resource/resource.go
+++ b/pkg/refmgmt/resource/resource.go
@@ -244,6 +244,12 @@ func NewResourceImplBase[T any, M io.Closer](m ViewManager[M], closer ...io.Clos
}, nil
}
+func NewSimpleResourceImplBase[T any](closer ...io.Closer) *ResourceImplBase[T] {
+ return &ResourceImplBase[T]{
+ closer: closer,
+ }
+}
+
func (b *ResourceImplBase[T]) SetViewManager(m ViewManager[T]) {
b.refs = m
}
@@ -256,6 +262,10 @@ func (b *ResourceImplBase[T]) Allocatable() refmgmt.ExtendedAllocatable {
return b.refs.Allocatable()
}
+func (b *ResourceImplBase[T]) ViewManager() ViewManager[T] {
+ return b.refs
+}
+
func (b *ResourceImplBase[T]) View(main ...bool) (T, error) {
return b.refs.View(main...)
}