diff --git a/.github/workflows/buildcomponents.yaml b/.github/workflows/buildcomponents.yaml new file mode 100644 index 000000000..cb38ff549 --- /dev/null +++ b/.github/workflows/buildcomponents.yaml @@ -0,0 +1,65 @@ +name: BuildComponents + +on: + workflow_dispatch: + inputs: + ocm_push: + type: boolean + description: "Push to OCM Repository" + default: false + +jobs: +# lint-and-test: +# uses: ./.github/workflows/lint_and_test.yaml +# permissions: +# contents: read +# pull-requests: read + components: +# needs: lint-and-test + name: Trigger component build + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + packages: write + repository-projects: read + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version-file: '${{ github.workspace }}/go.mod' + + - name: Cache go-build and mod + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build/ + ~/go/pkg/mod/ + key: go-${{ hashFiles('go.sum') }} + restore-keys: | + go- + + - name: Push OCM Components + if: inputs.ocm_push == true + env: + GITHUBORG: ${{ github.repository_owner }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} + run: make push + + - name: Build OCM Components + if: inputs.ocm_push == false + env: + GITHUBORG: ${{ github.repository_owner }} + run: make ctf + + - name: Upload OCM Archive + uses: actions/upload-artifact@v3 + with: + name: ocm.ctf + path: gen/ctf \ No newline at end of file diff --git a/Makefile b/Makefile index 038cb3dd5..32fc37977 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,19 @@ # # SPDX-License-Identifier: Apache-2.0 +NAME := ocm REPO_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) VERSION := $(shell go run pkg/version/generate/release_generate.go print-version) +GITHUBORG ?= open-component-model +OCMREPO ?= ghcr.io/$(GITHUBORG)/ocm EFFECTIVE_VERSION := $(VERSION)+$(shell git rev-parse HEAD) GIT_TREE_STATE := $(shell [ -z "$$(git status --porcelain 2>/dev/null)" ] && echo clean || echo dirty) COMMIT := $(shell git rev-parse --verify HEAD) -REGISTRY := ghcr.io/mandelsoft/ocm -COMPONENT_CLI_IMAGE_REPOSITORY := $(REGISTRY)/cli +CREDS := $(shell $(REPO_ROOT)/hack/githubcreds.sh) +OCM := go run $(REPO_ROOT)/cmds/ocm $(CREDS) + +GEN := $(REPO_ROOT)/gen SOURCES := $(shell go list -f '{{$$I:=.Dir}}{{range .GoFiles }}{{$$I}}/{{.}} {{end}}' ./... ) GOPATH := $(shell go env GOPATH) @@ -83,4 +88,43 @@ $(LOCALBIN): generate-license: for f in $(shell find . -name "*.go" -o -name "*.sh"); do \ reuse addheader -r --copyright="SAP SE or an SAP affiliate company and Open Component Model contributors." --license="Apache-2.0" $$f --skip-unrecognised; \ - done \ No newline at end of file + done + + +$(GEN)/.exists: + @mkdir -p $(GEN) + @touch $@ + +.PHONY: components +components: $(GEN)/.comps + +$(GEN)/.comps: + @echo Helminstaller; cd components/helminstaller; make ctf + @echo HelmDemo; cd components/helmdemo; make ctf + @echo OCMCLI; cd components/ocmcli; make ctf + touch $@ + +.PHONY: ctf +ctf: $(GEN)/ctf + +$(GEN)/ctf: $(GEN)/.exists components + @rm -rf "$(GEN)"/ctf + $(OCM) transfer cv --type tgz -V $(GEN)/helminstaller/ctf $(GEN)/ctf + $(OCM) transfer cv -V $(GEN)/helmdemo/ctf $(GEN)/ctf + $(OCM) transfer cv -V $(GEN)/ocmcli/ctf $(GEN)/ctf + touch $@ + +.PHONY: push +push: $(GEN)/ctf $(GEN)/.push.$(NAME) + +$(GEN)/.push.$(NAME): $(GEN)/ctf + $(OCM) transfer ctf -f $(GEN)/ctf $(OCMREPO) + @touch $@ + +.PHONY: plain-ctf +plain-ctf: $(GEN) + @rm -rf "$(GEN)"/ctf + $(OCM) transfer cv -V $(GEN)/helminstaller/ctf $(GEN)/ctf + $(OCM) transfer cv -V $(GEN)/helmdemo/ctf $(GEN)/ctf + $(OCM) transfer cv -V $(GEN)/ocmcli/ctf $(GEN)/ctf + diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..d5109100e --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.3.0-dev diff --git a/cmds/helminstaller/README.md b/cmds/helminstaller/README.md new file mode 100644 index 000000000..25912eb5a --- /dev/null +++ b/cmds/helminstaller/README.md @@ -0,0 +1,61 @@ +# Helm Installer for the TOI Framework + +The helm installer provides an executor plugin for the [TOI framework](../../docs/reference/ocm_toi-bootstrapping.md). +It can be used to install a top level helm chart, which might optionally +by composed of a set of sub charts. + +## Component + +The helm installer plugin for the TOI framework if provided +by component `ocm.software/toi/installers/helminstaller`. + +It provides a single `toiExecutor` resource with the +name `toiexecutor`. + +## Configuration + +The executor configuration supports the following field: + +- `chart` *[ResourceReference](../../docs/reference/ocm_toi-bootstrapping.md#resourcereference)* (**required**) The resource containing the top level hemlm chart to work on. + +- `subCharts` *map[string]ResourceReference* (**optional**) A set of resources describing charts used as sub charts for the top level chart. The map key is the name of the folder created for the sub chart. + +- `release` *string* (**optional**) The default release name to use for installation. + +- `namespace` *string* (**optional**) The default namespace to use for the installation. + +- `createNamespace` *boolean* (**optional**) If set to true the namespace will be created. + +- `imageMapping` *[]ImageMapping* list of localization rules for images. + +- `values` **yaml* The default values used for installation. They will be overwritten by the given installation values (at top-level). + +- `kubeConfigName` *string* (**optional** default: `target`) The credential name to lookup for the kubeconfig used to access the target Kubernetes cluster. + +### Image Mappings + +Image meappings describe the injection of OCI image locations, names and versions +taken from the component version info dedicated properties of the installation +values. The hemlm chart MUST take all image locations used in the templates +from dedicated values. This is required to support the transport of +component versions into local repositories environments, which should be used +on the installation site. + +An image mapping consists of resource reference fields to refer to an OCI image resource used to extract the image location plus the following additional fields: + +- `tag` *string* (**optional**) The property of the values used to inject the tag/version of the image. + +- `repository` *string* (**optional**) The property of the values used to inject the base URL (location+repository) of the image. + +- `image` *string* (**optional**) The property of the values used to inject the complete image name. + +At least the `image` attribute or the `tag` and `repositories` attributes must be used to provide a complete image location. + +### Configuring Subcharts + +Subcharts are configured as usual with the values for the parent chart (see https://helm.sh/docs/chart_template_guide/subcharts_and_globals/). + +The key of the subchart is used as top-level values key to add settings for the subchart. +Similar to the parent chart, images used by subcharts must be localized via [image mappings](#image-mappings), also. The subchart values must accept tag, repository and/or image value +fields for used images. They are set ny preceeding the name of the value field by the key +of the subchart. diff --git a/cmds/helminstaller/app/config.go b/cmds/helminstaller/app/config.go index cd38dd379..5aa9cc2d3 100644 --- a/cmds/helminstaller/app/config.go +++ b/cmds/helminstaller/app/config.go @@ -13,13 +13,14 @@ import ( ) type Config struct { - Chart v1.ResourceReference `json:"chart,omitempty"` - Release string `json:"release,omitempty"` - Namespace string `json:"namespace,omitempty"` - CreateNamespace bool `json:"createNamespace,omitempty"` - ImageMapping []ImageMapping `json:"imageMapping"` - Values json.RawMessage `json:"values,omitempty"` - KubeConfigName string `json:"kubeConfigName,omitempty"` + Chart v1.ResourceReference `json:"chart,omitempty"` + SubCharts map[string]v1.ResourceReference `json:"subCharts,omitempty"` + Release string `json:"release,omitempty"` + Namespace string `json:"namespace,omitempty"` + CreateNamespace bool `json:"createNamespace,omitempty"` + ImageMapping []ImageMapping `json:"imageMapping"` + Values json.RawMessage `json:"values,omitempty"` + KubeConfigName string `json:"kubeConfigName,omitempty"` } type ImageMapping = localize.ImageMapping diff --git a/cmds/helminstaller/app/execute.go b/cmds/helminstaller/app/execute.go index 33bd913b4..a7bc452c4 100644 --- a/cmds/helminstaller/app/execute.go +++ b/cmds/helminstaller/app/execute.go @@ -5,21 +5,31 @@ package app import ( + "encoding/json" "fmt" - "os" "strings" - "github.com/mandelsoft/vfs/pkg/osfs" + . "github.com/open-component-model/ocm/pkg/exception" + . "github.com/open-component-model/ocm/pkg/finalizer" + + "github.com/mandelsoft/filepath/pkg/filepath" + "github.com/mandelsoft/vfs/pkg/projectionfs" + "github.com/mandelsoft/vfs/pkg/vfs" "github.com/open-component-model/ocm/cmds/helminstaller/app/driver" "github.com/open-component-model/ocm/pkg/common" - "github.com/open-component-model/ocm/pkg/contexts/ocm" + "github.com/open-component-model/ocm/pkg/common/compression" + "github.com/open-component-model/ocm/pkg/contexts/oci/ociutils/helm/loader" + v1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1" "github.com/open-component-model/ocm/pkg/contexts/ocm/download" "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/out" "github.com/open-component-model/ocm/pkg/runtime" + "github.com/open-component-model/ocm/pkg/toi" + "github.com/open-component-model/ocm/pkg/toi/support" + utils2 "github.com/open-component-model/ocm/pkg/utils" ) func Merge(values ...map[string]interface{}) map[string]interface{} { @@ -33,53 +43,160 @@ func Merge(values ...map[string]interface{}) map[string]interface{} { return result } -func Execute(d driver.Driver, action string, ctx ocm.Context, octx out.Context, cv ocm.ComponentVersionAccess, cfg *Config, values map[string]interface{}, kubeconfig []byte) error { - if action != "install" && action != "uninstall" { - return errors.ErrNotSupported("action", action) +type Execution struct { + driver driver.Driver + *support.ExecutorOptions + path string + fs vfs.FileSystem +} + +func (e *Execution) outf(msg string, args ...interface{}) { + out.Outf(e.OutputContext, msg, args...) +} + +func (e *Execution) unpackChart(dir string) { + e.outf("Unpacking chart archive to %s...\n", dir) + e.Logger.Debug("unpacking chart archive", "directory", dir) + r := Must1f(R1(e.fs.Open(e.path)), "cannot read downloaded chart archive %q", e.path) + defer r.Close() + + e.Logger.Debug("auto decompress downloaded chart") + reader, _ := Must2f(R2(compression.AutoDecompress(r)), "cannot uncompress downloaded chart archive %q", e.path) + e.Logger.Debug("preparing chart filesystem") + chartfs := Must1f(R1(projectionfs.New(e.fs, dir)), "cannot create projection %q", e.path) + e.Logger.Debug("extracting chart archive", "archive", e.path) + Mustf(utils2.ExtractTarToFs(chartfs, reader), "cannot extract downloaded chart archive %q", e.path) + e.Logger.Debug("lookup chart folder") + entries := Must1f(R1(vfs.ReadDir(e.fs, dir)), "cannot find chart folder in %q", dir) + if len(entries) != 1 { + Throw(fmt.Errorf("expected single chart folder in archive, but found %d folders", len(entries))) } - cfgv, err := cfg.GetValues() - if err != nil { - return err + e.Logger.Debug("found chart folder", "folder", entries[0].Name()) + e.path = filepath.Join(dir, entries[0].Name()) +} + +func (e *Execution) addSubCharts(finalize *Finalizer, subCharts map[string]v1.ResourceReference) { + dir := e.path + ".dir" + Mustf(e.fs.Mkdir(dir, 0o700), "cannot mkdir %q", dir) + finalize.With(Calling1(e.fs.RemoveAll, dir)) + + e.unpackChart(dir) + + // prepare dependencies + e.outf("Preparing dependencies...\n") + chartFile := filepath.Join(e.path, "Chart.yaml") + chartData := Must1f(R1(vfs.ReadFile(e.fs, chartFile)), "cannot read Chart.yaml") + + var chart map[string]interface{} + Mustf(runtime.DefaultYAMLEncoding.Unmarshal(chartData, &chart), "cannot parse Chart.yaml") + deps := []interface{}{} + if d := chart["dependencies"]; d != nil { + if a, ok := d.([]interface{}); ok { + deps = a + } } - values = Merge(cfgv, values) - acc, rcv, err := utils.ResolveResourceReference(cv, cfg.Chart, nil) - if err != nil { - return errors.ErrNotFoundWrap(err, "chart reference", cfg.Chart.String()) + var loop Finalizer + defer loop.Finalize() + + charts := filepath.Join(e.path, "charts") + Mustf(e.fs.Mkdir(charts, 0o700), "cannot mkdir %q", charts) + e.outf("Loading %d sub charts into %s...\n", len(subCharts), charts) + for n, r := range subCharts { + e.outf(" Loading sub chart %q from resource %s@%s\n", n, r, common.VersionedElementKey(e.ComponentVersion)) + acc, rcv := Must2f(R2(utils.ResolveResourceReference(e.ComponentVersion, r, nil)), "chart reference", r.String()) + loop.Close(rcv) + + if acc.Meta().Type != resourcetypes.HELM_CHART { + Throw(errors.Newf("%s: resource type %q required, but found %q", r, resourcetypes.HELM_CHART, acc.Meta().Type)) + } + + _, subpath := Must2f(R2(download.For(e.Context).Download(common.NewPrinter(e.OutputContext.StdOut()), acc, filepath.Join(charts, n), e.fs)), "downloading helm chart %s", r) + + chartObj := Must1f(R1(loader.Load(subpath, e.fs)), "cannot load subchart %q", subpath) + found := false + for _, dep := range deps { + m, ok := dep.(map[string]interface{}) + if ok { + if m["alias"] == n { + e.outf(" found dependency %q for subchart %q\n", n, chartObj.Name()) + m["name"] = chartObj.Name() + found = true + break + } + if m["name"] == chartObj.Name() { + if m["alias"] == nil { + e.outf(" setting alias %q for dependency for subchart %q\n", n, chartObj.Name()) + if n != chartObj.Name() { + m["alias"] = n + } + found = true + } + } + } + } + if !found { + e.outf(" adding dependency %q for subchart %q\n", n, chartObj.Name()) + m := map[string]interface{}{} + m["name"] = chartObj.Name() + if n != chartObj.Name() { + m["alias"] = n + } + deps = append(deps, m) + } + loop.Finalize() + } + + chart["dependencies"] = deps + chartData = Must1f(R1(runtime.DefaultYAMLEncoding.Marshal(chart)), "cannot marshal Chart.yaml") + Mustf(vfs.WriteFile(e.fs, chartFile, chartData, 0o600), "cannot write Chart.yaml") +} + +func (e *Execution) Execute(cfg *Config, values map[string]interface{}, kubeconfig []byte) (err error) { + var finalize Finalizer + defer finalize.CatchException().FinalizeWithErrorPropagation(&err) + + if e.Action != "install" && e.Action != "uninstall" { + return errors.ErrNotSupported("action", e.Action) } - defer rcv.Close() - fmt.Printf("Installing helm chart from resource %s@%s", cfg.Chart, common.VersionedElementKey(cv)) + values = Merge(Must1(cfg.GetValues()), values) + + e.outf("Loading helm chart from resource %s@%s\n", cfg.Chart, common.VersionedElementKey(e.ComponentVersion)) + acc, rcv := Must2f(R2(utils.ResolveResourceReference(e.ComponentVersion, cfg.Chart, nil)), "chart reference", cfg.Chart.String()) + finalize.Close(rcv) + if acc.Meta().Type != resourcetypes.HELM_CHART { return errors.Newf("resource type %q required, but found %q", resourcetypes.HELM_CHART, acc.Meta().Type) } // have to use the OS filesystem here for using the helm library - file, err := os.CreateTemp("", "helm-*") + file := Must1(vfs.TempFile(e.fs, "", "helm-*")) + path := file.Name() + file.Close() + e.fs.Remove(path) + + spec := Must1f(R1(acc.Access()), "getting access specification") + data, err := json.Marshal(spec) if err != nil { return err } + toi.Log.Info("starting download", "path", path, "access", string(data)) - path := file.Name() - file.Close() - os.Remove(path) + _, e.path = Must2f(R2(download.For(e.Context).Download(common.NewPrinter(e.OutputContext.StdOut()), acc, path, e.fs)), "downloading helm chart") - _, path, err = download.For(ctx).Download(common.NewPrinter(octx.StdOut()), acc, path, osfs.New()) - if err != nil { - return errors.Wrapf(err, "downloading helm chart") + finalize.With(Calling1(e.fs.Remove, e.path)) + + if len(cfg.SubCharts) > 0 { + e.addSubCharts(&finalize, cfg.SubCharts) } - defer os.Remove(path) + e.outf("Localizing helm chart...\n") + e.Logger.Debug("Localizing helm chart") for i, v := range cfg.ImageMapping { - acc, rcv, err := utils.ResolveResourceReference(cv, v.ResourceReference, nil) - if err != nil { - return errors.ErrNotFoundWrap(err, "mapping", fmt.Sprintf("%d (%s)", i+1, &v.ResourceReference)) - } + acc, rcv := Must2f(R2(utils.ResolveResourceReference(e.ComponentVersion, v.ResourceReference, nil)), "mapping", fmt.Sprintf("%d (%s)", i+1, &v.ResourceReference)) rcv.Close() - ref, err := utils.GetOCIArtifactRef(ctx, acc) - if err != nil { - return errors.Wrapf(err, "mapping %d: cannot resolve resource %s to an OCI Reference", i+1, v) - } + ref := Must1f(R1(utils.GetOCIArtifactRef(e.Context, acc)), "mapping %d: cannot resolve resource %s to an OCI Reference", i+1, v) ix := strings.Index(ref, ":") if ix < 0 { ix = strings.Index(ref, "@") @@ -90,25 +207,21 @@ func Execute(d driver.Driver, action string, ctx ocm.Context, octx out.Context, repo := ref[:ix] tag := ref[ix+1:] if v.Repository != "" { - err = Set(values, v.Repository, repo) - if err != nil { - return errors.Wrapf(err, "mapping %d: assigning repositry to property %q", v.Repository) - } + e.Logger.Debug("substitute image repository", "ref", ref, "target", v.Repository) + Mustf(Set(values, v.Repository, repo), "mapping %d: assigning repositry to property %q", v.Repository) } if v.Tag != "" { - err = Set(values, v.Tag, tag) - if err != nil { - return errors.Wrapf(err, "mapping %d: assigning tag to property %q", v.Tag) - } + e.Logger.Debug("substitute image tag", "ref", ref, "target", v.Tag) + Mustf(Set(values, v.Tag, tag), "mapping %d: assigning tag to property %q", v.Tag) } if v.Image != "" { - err = Set(values, v.Image, ref) - if err != nil { - return errors.Wrapf(err, "mapping %d: assigning image to property %q", v.Image) - } + e.Logger.Debug("substitute image ref", "ref", ref, "target", v.Image) + Mustf(Set(values, v.Image, ref), "mapping %d: assigning image to property %q", v.Image) } } + e.outf("Installing helm chart...\n") + ns := "default" if cfg.Namespace != "" { ns = cfg.Namespace @@ -120,25 +233,23 @@ func Execute(d driver.Driver, action string, ctx ocm.Context, octx out.Context, if s, ok := values["release"].(string); ok && s != "" { release = s } - valuesdata, err := runtime.DefaultYAMLEncoding.Marshal(values) - if err != nil { - return errors.Wrapf(err, "marshal values") - } + e.Logger.Debug("executing helm deployment", "action", e.Action, "namespace", ns, "release", release) + valuesdata := Must1f(R1(runtime.DefaultYAMLEncoding.Marshal(values)), "marshal values") dcfg := &driver.Config{ - ChartPath: path, + ChartPath: e.path, Release: release, Namespace: ns, CreateNamespace: cfg.CreateNamespace, Values: valuesdata, Kubeconfig: kubeconfig, } - switch action { + switch e.Action { case "install": - return d.Install(dcfg) + return e.driver.Install(dcfg) case "uninstall": - return d.Uninstall(dcfg) + return e.driver.Uninstall(dcfg) default: - return errors.ErrNotImplemented("action", action) + return errors.ErrNotImplemented("action", e.Action) } } diff --git a/cmds/helminstaller/app/executor.go b/cmds/helminstaller/app/executor.go index ba0b142ee..f585b72a8 100644 --- a/cmds/helminstaller/app/executor.go +++ b/cmds/helminstaller/app/executor.go @@ -5,6 +5,8 @@ package app import ( + "github.com/mandelsoft/vfs/pkg/osfs" + "github.com/open-component-model/ocm/cmds/helminstaller/app/driver" "github.com/open-component-model/ocm/pkg/errors" "github.com/open-component-model/ocm/pkg/runtime" @@ -39,5 +41,11 @@ func Executor(d driver.Driver, o *support.ExecutorOptions) error { if v == "" { return errors.Wrapf(err, "property KUBECONFIG missing in credential %q", cfg.KubeConfigName) } - return Execute(d, o.Action, o.Context, o.OutputContext, o.ComponentVersion, &cfg, values, []byte(v)) + exec := &Execution{ + driver: d, + ExecutorOptions: o, + path: "", + fs: osfs.New(), + } + return exec.Execute(&cfg, values, []byte(v)) } diff --git a/cmds/ocm/app/app.go b/cmds/ocm/app/app.go index a969a02f5..5860ed9cb 100644 --- a/cmds/ocm/app/app.go +++ b/cmds/ocm/app/app.go @@ -9,19 +9,15 @@ package app import ( "strings" - "github.com/open-component-model/ocm/cmds/ocm/commands/misccmds/action" _ "github.com/open-component-model/ocm/pkg/contexts/clictx/config" _ "github.com/open-component-model/ocm/pkg/contexts/ocm/attrs" - "github.com/mandelsoft/logging" - "github.com/mandelsoft/logging/config" - "github.com/mandelsoft/logging/logrusr" - "github.com/mandelsoft/vfs/pkg/vfs" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/open-component-model/ocm/cmds/ocm/commands/cachecmds" + "github.com/open-component-model/ocm/cmds/ocm/commands/misccmds/action" creds "github.com/open-component-model/ocm/cmds/ocm/commands/misccmds/credentials" "github.com/open-component-model/ocm/cmds/ocm/commands/ocicmds" "github.com/open-component-model/ocm/cmds/ocm/commands/ocmcmds" @@ -32,7 +28,6 @@ import ( "github.com/open-component-model/ocm/cmds/ocm/commands/ocmcmds/references" "github.com/open-component-model/ocm/cmds/ocm/commands/ocmcmds/resources" "github.com/open-component-model/ocm/cmds/ocm/commands/ocmcmds/sources" - "github.com/open-component-model/ocm/cmds/ocm/commands/toicmds" "github.com/open-component-model/ocm/cmds/ocm/commands/verbs/add" "github.com/open-component-model/ocm/cmds/ocm/commands/verbs/bootstrap" "github.com/open-component-model/ocm/cmds/ocm/commands/verbs/clean" @@ -51,11 +46,13 @@ import ( cmdutils "github.com/open-component-model/ocm/cmds/ocm/pkg/utils" "github.com/open-component-model/ocm/cmds/ocm/topics/common/attributes" topicconfig "github.com/open-component-model/ocm/cmds/ocm/topics/common/config" + topiclogging "github.com/open-component-model/ocm/cmds/ocm/topics/common/logging" topicocirefs "github.com/open-component-model/ocm/cmds/ocm/topics/oci/refs" topicocmaccessmethods "github.com/open-component-model/ocm/cmds/ocm/topics/ocm/accessmethods" topicocmrefs "github.com/open-component-model/ocm/cmds/ocm/topics/ocm/refs" topicbootstrap "github.com/open-component-model/ocm/cmds/ocm/topics/toi/bootstrapping" "github.com/open-component-model/ocm/pkg/cobrautils" + "github.com/open-component-model/ocm/pkg/cobrautils/logopts" "github.com/open-component-model/ocm/pkg/common" "github.com/open-component-model/ocm/pkg/contexts/clictx" "github.com/open-component-model/ocm/pkg/contexts/credentials" @@ -65,7 +62,6 @@ import ( "github.com/open-component-model/ocm/pkg/contexts/ocm/registration" "github.com/open-component-model/ocm/pkg/contexts/ocm/utils" "github.com/open-component-model/ocm/pkg/errors" - ocmlog "github.com/open-component-model/ocm/pkg/logging" "github.com/open-component-model/ocm/pkg/out" "github.com/open-component-model/ocm/pkg/version" ) @@ -77,12 +73,9 @@ type CLIOptions struct { Context clictx.Context Settings []string Verbose bool - LogLevel string - LogFile string - LogConfig string - Version bool + LogOpts logopts.Options - logFile vfs.File + Version bool } var desc = ` @@ -134,7 +127,7 @@ form
-X <attribute>=<value>
--keep-global-access
is given, all localized referential
+resources will preserve their original global access information.
+This behaviour can be further influenced by specifying a transfer script
+with the script
option family.
+`
+ return s
+}
+
+func (o *Option) ApplyTransferOption(opts transferhandler.TransferOptions) error {
+ return standard.KeepGlobalAccess(o.KeepGlobalAccess).ApplyTransferOption(opts)
+}
diff --git a/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go
index 3a69a860c..39bc41b3d 100644
--- a/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go
+++ b/cmds/ocm/commands/ocmcmds/common/options/lookupoption/option.go
@@ -74,6 +74,10 @@ references.
return s
}
+func (o *Option) IsGiven() bool {
+ return len(o.RepoSpecs) > 0
+}
+
func (o *Option) LookupComponentVersion(name string, vers string) (ocm.ComponentVersionAccess, error) {
if o == nil || o.Resolver == nil {
return nil, nil
diff --git a/cmds/ocm/commands/ocmcmds/common/options/scriptoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/scriptoption/option.go
index f8a47729e..8a1b0b3a3 100644
--- a/cmds/ocm/commands/ocmcmds/common/options/scriptoption/option.go
+++ b/cmds/ocm/commands/ocmcmds/common/options/scriptoption/option.go
@@ -38,7 +38,7 @@ func (o *Option) AddFlags(fs *pflag.FlagSet) {
fs.StringVarP(&o.ScriptFile, "script", "", "", "config name of transfer handler script")
}
-func (o *Option) Complete(ctx clictx.Context) error {
+func (o *Option) Configure(ctx clictx.Context) error {
o.FileSystem = ctx.FileSystem()
if o.ScriptFile != "" && o.Script != "" {
return errors.Newf("only one of --script or --scriptFile may be set")
diff --git a/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go
index c80b1f2f5..7cc3b98c8 100644
--- a/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go
+++ b/cmds/ocm/commands/ocmcmds/common/options/signoption/option.go
@@ -80,7 +80,7 @@ func (o *Option) AddFlags(fs *pflag.FlagSet) {
fs.StringArrayVarP(&o.rootca, "ca-cert", "", o.rootca, "Additional root certificates")
}
-func (o *Option) Complete(ctx clictx.Context) error {
+func (o *Option) Configure(ctx clictx.Context) error {
if len(o.SignatureNames) > 0 {
for i, n := range o.SignatureNames {
n = strings.TrimSpace(n)
@@ -96,7 +96,7 @@ func (o *Option) Complete(ctx clictx.Context) error {
o.Keys = signing.NewKeyRegistry()
}
if o.SignMode {
- err := o.Hash.Complete(ctx)
+ err := o.Hash.Configure(ctx)
if err != nil {
return err
}
diff --git a/cmds/ocm/commands/ocmcmds/common/options/stoponexistingoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/stoponexistingoption/option.go
new file mode 100644
index 000000000..743fdb402
--- /dev/null
+++ b/cmds/ocm/commands/ocmcmds/common/options/stoponexistingoption/option.go
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package stoponexistingoption
+
+import (
+ "github.com/spf13/pflag"
+
+ "github.com/open-component-model/ocm/cmds/ocm/pkg/options"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/transfer/transferhandler"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/transfer/transferhandler/standard"
+)
+
+func From(o options.OptionSetProvider) *Option {
+ var opt *Option
+ o.AsOptionSet().Get(&opt)
+ return opt
+}
+
+func New() *Option {
+ return &Option{}
+}
+
+type Option struct {
+ StopOnExistingVersion bool
+}
+
+var _ transferhandler.TransferOption = (*Option)(nil)
+
+func (o *Option) AddFlags(fs *pflag.FlagSet) {
+ fs.BoolVarP(&o.StopOnExistingVersion, "stop-on-existing", "E", false, "stop on existing component version in target repository")
+}
+
+func (o *Option) Usage() string {
+ s := `
+It the option --stop-on-existing
is given together with the --recursive
+option, the recursion is stopped for component versions already existing in the
+target repository. This behaviour can be further influenced by specifying a transfer script
+with the script
option family.
+`
+ return s
+}
+
+func (o *Option) ApplyTransferOption(opts transferhandler.TransferOptions) error {
+ return standard.StopOnExistingVersion(o.StopOnExistingVersion).ApplyTransferOption(opts)
+}
diff --git a/cmds/ocm/commands/ocmcmds/common/options/templateroption/option.go b/cmds/ocm/commands/ocmcmds/common/options/templateroption/option.go
index 9a5512507..dd7f20e2d 100644
--- a/cmds/ocm/commands/ocmcmds/common/options/templateroption/option.go
+++ b/cmds/ocm/commands/ocmcmds/common/options/templateroption/option.go
@@ -24,6 +24,6 @@ type Option struct {
template.Options
}
-func (o *Option) Complete(ctx clictx.Context) error {
+func (o *Option) Configure(ctx clictx.Context) error {
return o.Options.Complete(ctx.FileSystem())
}
diff --git a/cmds/ocm/commands/ocmcmds/common/options/uploaderoption/option.go b/cmds/ocm/commands/ocmcmds/common/options/uploaderoption/option.go
index e3d1737b6..36ef96eef 100644
--- a/cmds/ocm/commands/ocmcmds/common/options/uploaderoption/option.go
+++ b/cmds/ocm/commands/ocmcmds/common/options/uploaderoption/option.go
@@ -47,7 +47,7 @@ func (o *Option) AddFlags(fs *pflag.FlagSet) {
flag.StringToStringVarP(fs, &o.spec, "uploader", "", nil, "repository uploader (--create
is given, the archive is created first. An
additional option --force
will recreate an empty archive if it already exists.
+If option --complete
is given all component versions referenced by
+the added one, will be added, also. Therefore, the --lookup
is required
+to specify an OCM repository to lookup the missing component versions. If
+additionally the -V
is given, the resources of those additional
+components will be added by value.
+
The source, resource and reference list can be composed according the commands
` + toi.TypeTOIPackage + `
.
+This is a simple YAML file resource describing the bootstrapping of a dedicated kind
+of software. See also the topic <attr>=<value>
).
+
+If no credentials file name is provided (option -c) the file
+` + DEFAULT_CREDENTIALS_FILE + `
is used. If no parameter file name is
+provided (option -p) the file ` + DEFAULT_PARAMETER_FILE + `
is used.
+
+For more details about those files see ` + install.TypeTOIPackage + `
.
+The package resource must have the type ` + toi.TypeTOIPackage + `
.
This is a simple YAML file resource describing the bootstrapping of a dedicated kind
of software. See also the topic ` + DEFAULT_CREDENTIALS_FILE + `
is used, if present. If no parameter file name is
provided (option -p) the file ` + DEFAULT_PARAMETER_FILE + `
is used, if present.
+
+Using the credentials file it is possible to configure credentials required by
+the installation package or executor. Additionally arbitrary consumer ids
+can be forwarded to executor, which might be required by accessing blobs
+described by external access methods.
+
+The credentials file uses the following yaml format:
+- credentials
*map[string]CredentialsSpec*
+
+ The resolution of credentials requested by the package (by name).
+
+- forwardedConsumers
*[]ForwardSpec* (optional)
+
+ An optional list of consumer specifications to be forwarded to the OCM
+ configuration provided to the executor.
+
+The *CredentialsSpec* uses the following format:
+
+- consumerId
*map[string]string*
+
+ The consumer id used to look up the credentials.
+
+- consumerType
*string* (optional) (default: partial)
+
+ The type of the matcher used to match the consumer id.
+
+- reference
*yaml*
+
+ A generic credential specification as used in the ocm config file.
+
+- credentials
*map[string]string*
+
+ Direct credential fields.
+
+One of consumerId
, reference
or credentials
+must be configured.
+
+The *ForwardSpec* uses the following format:
+
+- consumerId
*map[string]string*
+
+ The consumer id to be forwarded.
+
+- consumerType
*string* (optional) (default: partial)
+
+ The type of the matcher used to match the consumer id.
+
+If provided by the package it is possible to download template versions
+for the parameter and credentials file using the command ` + toi.TypeTOIPackage + `
.
+This is a simple YAML file resource describing the bootstrapping of a dedicated kind
+of software. See also the topic <attr>=<value>
).
+`,
+ Example: `
+$ ocm toi describe package ghcr.io/mandelsoft/ocm//ocmdemoinstaller:0.0.1-dev
+`,
+ }
+ cmd.AddCommand(topicbootstrap.New(o.Context, "toi-bootstrapping"))
+ return cmd
+}
+
+func (o *Command) Complete(args []string) error {
+ o.Ref = args[0]
+ id, err := ocmcommon.MapArgsToIdentityPattern(args[1:]...)
+ if err != nil {
+ return errors.Wrapf(err, "bootstrap resource identity pattern")
+ }
+ o.Id = id
+ return nil
+}
+
+func (o *Command) Run() error {
+ session := ocm.NewSession(nil)
+ defer session.Close()
+
+ err := o.ProcessOnOptions(ocmcommon.CompleteOptionsWithSession(o, session))
+ if err != nil {
+ return err
+ }
+ handler := comphdlr.NewTypeHandler(o.Context.OCM(), session, repooption.From(o).Repository)
+ return utils.HandleOutput(&action{cmd: o}, handler, utils.StringElemSpecs(o.Ref)...)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type action struct {
+ data comphdlr.Objects
+ cmd *Command
+}
+
+var _ output.Output = (*action)(nil)
+
+func (a *action) Add(e interface{}) error {
+ o, ok := e.(*comphdlr.Object)
+ if !ok {
+ return fmt.Errorf("object of type %T is not a valid comphdlr.Object", e)
+ }
+ if o.ComponentVersion != nil && !ocireg.IsKind(o.Repository.GetSpecification().GetKind()) {
+ if ociuploadattr.Get(a.cmd.Context) == nil {
+ out.Outf(a.cmd, "Warning: repository is no OCI registry, consider importing it or use upload repository with option ' -X ociuploadrepo=...\n")
+ } else {
+ out.Outf(a.cmd, "Warning: repository is no OCI registry, consider importing.\n'")
+ }
+ }
+ a.data = append(a.data, o)
+ return nil
+}
+
+func (a *action) Close() error {
+ return nil
+}
+
+type Binary struct {
+ Binary []byte `json:"binary"`
+}
+
+func (a *action) Out() error {
+ cnt := 0
+ for i := range a.data {
+ if i > 0 {
+ a.Outf("\n")
+ }
+ nv := common.VersionedElementKey(a.data[i].ComponentVersion)
+ err := a.describe(a.data[i].ComponentVersion)
+ if err != nil {
+ out.Errf(a.cmd.Context, "%s: %s\n", nv, err)
+ cnt++
+ }
+ }
+ if cnt > 0 {
+ return fmt.Errorf("describe failed for %d packages", cnt)
+ }
+ return nil
+}
+
+func (a *action) Outf(msg string, args ...interface{}) {
+ out.Outf(a.cmd.Context, msg, args...)
+}
+
+type einfo struct {
+ index int
+ ectx *install.ExecutorContext
+}
+
+func (a *action) describe(cv ocm.ComponentVersionAccess) error {
+ nv := common.VersionedElementKey(cv)
+ rid := metav1.NewResourceRef(a.cmd.Id)
+ resolver := lookupoption.From(a.cmd)
+
+ ires, eff, err := utils2.MatchResourceReference(cv, toi.TypeTOIPackage, rid, resolver)
+ if err != nil {
+ return errors.Wrapf(err, "package resource in %s", nv)
+ }
+ defer eff.Close()
+
+ var spec toi.PackageSpecification
+ err = install.GetResource(ires, &spec)
+ if err != nil {
+ return errors.ErrInvalidWrap(err, "package spec")
+ }
+
+ a.Outf("TOI Package %s[%s]\n", nv, ires.Meta().GetName())
+
+ if spec.Description != "" {
+ a.Outf(" Package Description:\n%s\n\n", utils3.IndentLines(strings.TrimSpace(spec.Description), " ", false))
+ }
+ if len(spec.AdditionalResources) == 0 {
+ a.Outf(" no additional resources found\n")
+ } else {
+ keys := utils3.StringMapKeys(spec.AdditionalResources)
+ a.Outf(" Additional Resources:\n")
+ for _, k := range keys {
+ switch k {
+ case toi.AdditionalResourceCredentialsFile:
+ out.Outf(a.cmd.Context, " - %s: %s\n", k, "downloadable credential file template)")
+ case toi.AdditionalResourceConfigFile:
+ out.Outf(a.cmd.Context, " - %s: %s\n", k, "downloadable user configuration file template)")
+ default:
+ out.Outf(a.cmd.Context, " - %s\n", k)
+ }
+ }
+ }
+
+ actions := map[string]*einfo{}
+
+ for i, e := range spec.Executors {
+ eacts := e.Actions
+
+ ectx, err := install.DetermineExecutor(&e, a.cmd.OCMContext(), cv, resolver)
+ if err != nil {
+ a.Outf(" Warning: cannot determine executor %s: %s\n", e.Name(), err)
+ }
+ info := &einfo{i, ectx}
+ if len(eacts) == 0 && ectx != nil {
+ eacts = ectx.Spec.Actions
+ }
+ if len(eacts) == 0 {
+ eacts = []string{"` + install.TypeTOIPackage + `
is defined.
+Therefore, a dedicated resource type ` + toi.TypeTOIPackage + `
is defined.
It is selected by a resource identity pattern. The first resource matching the pattern
is used. A possible use case could be to provide different packages for
different environments. The resource can use an identity attribute
@@ -82,7 +88,7 @@ to perform the action. Finally, an executor is an image following the TOI
specification for passing information into the image execution and receiving
results from the execution. Such an image is described in two ways:
- it either describes a resource of type ` + resourcetypes.OCI_IMAGE + `
or
-- it describes a resource of type ` + install.TypeTOIExecutor + `
, which defines
+- it describes a resource of type ` + toi.TypeTOIExecutor + `
, which defines
the image to use and some default settings and further describes the features
and requirements of the executor image.
@@ -96,23 +102,27 @@ The execution of the container may do the needful to achieve the goal of the
requested action and provide some labeled output files, which will be passed
to the caller.
-### The ` + install.TypeTOIPackage + `
Resource
+### The ` + toi.TypeTOIPackage + `
Resource
This resource describes an installable software package, whose content is
contained in the component version, which contains the package resource.
It is a plain yaml resource with the media types media type ` + mime.MIME_YAML + `
,
` + mime.MIME_YAML_ALT + `
or
-` + install.PackageSpecificationMimeType + `
) containing
+` + toi.PackageSpecificationMimeType + `
) containing
information required to control the instantiation of an executor.
It has the following format:
+- **description
** (optional) *string*
+
+ A short description of the installation package and some configuration hints.
+
- **executors
** *[]ExecutorSpecification*
- **configTemplate
** (optional) *yaml*
- This a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ This is a [spiff](https://github.com/mandelsoft/spiff) template used to generate
The user config that is finally passed to the executor. If no template
is specified the user parameter input will be processed directly without template.
@@ -133,6 +143,17 @@ It has the following format:
requites the specification of a credentials file providing the information
how to satisfy those credential requests.
+- **additionalResources
** (optional) *map[string]ResourceReference*
+
+ A set of additional resources specified by OCM resource references.
+ The key describes the meaning of the resource. The following keys have
+ a special meaning:
+
+ - **configFile
**: an example template for a parameter file
+ - **credentialsFile
**: an example template for a credentials file
+
+ Those templates can be downloaded with name
, which
is the resource name field of the desired resource. If this resource
defines additional identity attributes, the complete set must be specified.
-### The ` + install.TypeTOIExecutor + `
Resource
+### The ` + toi.TypeTOIExecutor + `
Resource
Instead of directly describing an image resource i the package file, it is
-possible to refer to a resource of type ` + install.TypeTOIExecutor + `. This
+possible to refer to a resource of type ` + toi.TypeTOIExecutor + `. This
is a yaml file with the media type ` + mime.MIME_YAML + `
,
` + mime.MIME_YAML_ALT + `
or
-` + install.PackageSpecificationMimeType + `
) containing
+` + toi.PackageSpecificationMimeType + `
) containing
common information about the executor executor. If used by the package,
this information is used to validate settings in the package specification.
diff --git a/components/demoplugin/Makefile b/components/demoplugin/Makefile
index e4b9027cb..c91440493 100644
--- a/components/demoplugin/Makefile
+++ b/components/demoplugin/Makefile
@@ -14,8 +14,10 @@ GIT_TREE_STATE := $(shell [ -z "$$(git status --
CMDSRCS=$(shell find $(REPO_ROOT)/cmds/$(NAME) -type f)
OCMSRCS=$(shell find $(REPO_ROOT)/pkg -type f) $(REPO_ROOT)/go.*
+CREDS := $(shell $(REPO_ROOT)/hack/githubcreds.sh)
+OCM = go run $(REPO_ROOT)/cmds/ocm $(CREDS)
+
GEN = $(REPO_ROOT)/gen/$(NAME)
-OCM = go run $(REPO_ROOT)/cmds/ocm
NOW := $(shell date -u +%FT%T%z)
BUILD_FLAGS := "-s -w \
diff --git a/components/helmdemo/Makefile b/components/helmdemo/Makefile
index 8500f0297..00a4281f8 100644
--- a/components/helmdemo/Makefile
+++ b/components/helmdemo/Makefile
@@ -12,8 +12,10 @@ VERSION = $(shell git describe --tags --e
COMMIT = $(shell git rev-parse HEAD)
EFFECTIVE_VERSION = $(VERSION)-$(COMMIT)
-GEN = $(REPO_ROOT)/gen/$(NAME)
-OCM = go run $(REPO_ROOT)/cmds/ocm
+CREDS := $(shell $(REPO_ROOT)/hack/githubcreds.sh)
+OCM = go run $(REPO_ROOT)/cmds/ocm $(CREDS)
+
+GEN := $(REPO_ROOT)/gen/$(NAME)
CHARTSRCS=$(shell find echoserver -type f)
@@ -21,7 +23,7 @@ CHARTSRCS=$(shell find echoserver -type f)
ctf: $(GEN)/ctf
$(GEN)/ctf: $(GEN)/ca
- @rm -f $(GEN)/ctf
+ @rm -rf $(GEN)/ctf
$(OCM) transfer ca $(GEN)/ca $(GEN)/ctf
touch $(GEN)/ctf
@@ -32,7 +34,7 @@ version:
.PHONY: ca
ca: $(GEN)/ca
-$(GEN)/ca: $(GEN)/.exists sources.yaml resources.yaml references.yaml $(CHARTSRCS) packagespec.yaml helmconfig.yaml
+$(GEN)/ca: $(GEN)/.exists sources.yaml resources.yaml references.yaml $(CHARTSRCS) packagespec.yaml examples/* helmconfig.yaml
$(OCM) create ca -f $(COMPONENT) "$(VERSION)" --provider $(PROVIDER) --file $(GEN)/ca
$(OCM) add sources $(GEN)/ca VERSION="$(VERSION)" COMMIT="$(COMMIT)" IMAGE="$(IMAGE):$(VERSION)" sources.yaml
$(OCM) add resources $(GEN)/ca VERSION="$(VERSION)" COMMIT="$(COMMIT)" IMAGE="$(IMAGE):$(VERSION)" resources.yaml
@@ -69,15 +71,16 @@ $(GEN)/.exists:
info:
@echo "ROOT: $(REPO_ROOT)"
@echo "VERSION: $(VERSION)"
- @echo "COMMIT; $(COMMIT)"
+ @echo "COMMIT: $(COMMIT)"
+ @echo "CREDS: $(CREDS)"
.PHONY: describe
describe: $(GEN)/ctf
- ocm get resources --lookup $(OCMREPO) -c -o treewide $(GEN)/ctf
+ $(OCM) get resources --lookup $(OCMREPO) -c -o treewide $(GEN)/ctf
.PHONY: descriptor
descriptor: $(GEN)/ctf
- ocm get component -S v3alpha1 -o yaml $(GEN)/ctf
+ $(OCM) get component -S v3alpha1 -o yaml $(GEN)/ctf
.PHONY: clean
clean:
diff --git a/components/helmdemo/examples/config.yaml b/components/helmdemo/examples/config.yaml
new file mode 100644
index 000000000..d7cbe366e
--- /dev/null
+++ b/components/helmdemo/examples/config.yaml
@@ -0,0 +1 @@
+namespace: myechoserver
\ No newline at end of file
diff --git a/components/helmdemo/examples/creds.yaml b/components/helmdemo/examples/creds.yaml
new file mode 100644
index 000000000..1182ea6fc
--- /dev/null
+++ b/components/helmdemo/examples/creds.yaml
@@ -0,0 +1,4 @@
+credentials:
+ target:
+ credentials:
+ KUBECONFIG: (( read("~/k8s/CLUSTERS/test") ))
\ No newline at end of file
diff --git a/components/helmdemo/packagespec.yaml b/components/helmdemo/packagespec.yaml
index aecc59c41..e6135f185 100644
--- a/components/helmdemo/packagespec.yaml
+++ b/components/helmdemo/packagespec.yaml
@@ -1,3 +1,6 @@
+description: |
+ This package describes the deployment of a simple echoserver
+ into a namespace of a Kubernetes cluster.
executors:
- resourceRef:
resource:
@@ -20,3 +23,10 @@ configScheme:
type: string
namespace:
type: string
+additionalResources:
+ configFile:
+ resource:
+ name: config-example
+ credentialsFile:
+ resource:
+ name: creds-example
\ No newline at end of file
diff --git a/components/helmdemo/references.yaml b/components/helmdemo/references.yaml
index 56b7fc2b4..ced2e4a66 100644
--- a/components/helmdemo/references.yaml
+++ b/components/helmdemo/references.yaml
@@ -1,5 +1,5 @@
---
name: installer
componentName: ${HELMINSTCOMP}
-version: 0.1.0-dev
+version: ${VERSION}
diff --git a/components/helmdemo/resources.yaml b/components/helmdemo/resources.yaml
index d51df3a74..c1b1674e0 100644
--- a/components/helmdemo/resources.yaml
+++ b/components/helmdemo/resources.yaml
@@ -21,3 +21,23 @@ version: "1.0"
access:
type: ociArtifact
imageReference: gcr.io/google_containers/echoserver:1.10
+---
+name: config-example
+type: yaml
+labels:
+ - name: commit
+ value: ${COMMIT}
+input:
+ type: file
+ mediaType: application/vnd.toi.ocm.software.config.v1+yaml
+ path: examples/config.yaml
+---
+name: creds-example
+type: yaml
+labels:
+ - name: commit
+ value: ${COMMIT}
+input:
+ type: file
+ mediaType: application/vnd.toi.ocm.software.credentials.v1+yaml
+ path: examples/creds.yaml
diff --git a/components/helminstaller/Makefile b/components/helminstaller/Makefile
index 81f20fd54..0677b1017 100644
--- a/components/helminstaller/Makefile
+++ b/components/helminstaller/Makefile
@@ -12,10 +12,12 @@ VERSION := $(shell git describe --tags --
COMMIT := $(shell git rev-parse --verify HEAD)
EFFECTIVE_VERSION := $(VERSION)-$(COMMIT)
GIT_TREE_STATE := $(shell [ -z "$$(git status --porcelain 2>/dev/null)" ] && echo clean || echo dirty)
+PLATFORM := $(shell go env GOOS)/$(shell go env GOARCH)
+CREDS := $(shell $(REPO_ROOT)/hack/githubcreds.sh)
+OCM = go run $(REPO_ROOT)/cmds/ocm $(CREDS)
GEN = $(REPO_ROOT)/gen/$(NAME)
-OCM = go run $(REPO_ROOT)/cmds/ocm
CMDSRCS=$(shell find $(REPO_ROOT)/cmds/$(NAME) -type f)
OCMSRCS=$(shell find $(REPO_ROOT)/pkg -type f) $(REPO_ROOT)/go.*
@@ -66,12 +68,16 @@ eval-resources:
build: $(GEN)/image.$(NAME)
$(GEN)/image.$(NAME): $(GEN)/.exists Dockerfile $(CMDSRCS) $(OCMSRCS)
- docker build -t $(IMAGE):$(VERSION) --file Dockerfile $(REPO_ROOT) \
+ docker buildx build -t $(IMAGE):$(VERSION) --platform $(PLATFORM) --file Dockerfile $(REPO_ROOT) \
--build-arg COMMIT=$(COMMIT) \
--build-arg EFFECTIVE_VERSION=$(EFFECTIVE_VERSION) \
--build-arg GIT_TREE_STATE=$(GIT_TREE_STATE)
@touch $(GEN)/image.$(NAME)
+push-image:
+ docker tag $(IMAGE):$(VERSION) $(OCMREPO)/$(COMPONENT)/$(NAME):$(VERSION)
+ docker push $(OCMREPO)/$(COMPONENT)/$(NAME):$(VERSION)
+
.PHONY: multi
multi: $(GEN)/image.$(NAME).multi
@@ -110,9 +116,11 @@ $(GEN)/.exists:
.PHONY: info
info:
- @echo "ROOT: $(REPO_ROOT)"
- @echo "VERSION: $(VERSION)"
- @echo "COMMIT; $(COMMIT)"
+ @echo "ROOT: $(REPO_ROOT)"
+ @echo "VERSION: $(VERSION)"
+ @echo "COMMIT: $(COMMIT)"
+ @echo "GITHUBORG: $(GITHUBORG)"
+ @echo "PATFORM: $(PLATFORM)"
.PHONY: describe
describe: $(GEN)/ctf
diff --git a/components/helminstaller/executorspec.yaml b/components/helminstaller/executorspec.yaml
index 03ef82912..e9b0810a6 100644
--- a/components/helminstaller/executorspec.yaml
+++ b/components/helminstaller/executorspec.yaml
@@ -1,3 +1,4 @@
+
actions:
- install
- uninstall
@@ -5,18 +6,10 @@ imageRef:
resource:
name: toiimage
configScheme:
- type: object
- additionalProperties: false
- required:
- - chart
- - kubeConfigName
- - imageMapping
- properties:
- chart:
- additionalProperties: false
+ definitions:
+ resourceRef:
type: object
- required:
- - resource
+ description: resource reference for charts
properties:
resource:
type: object
@@ -28,7 +21,20 @@ configScheme:
type: object
additionalProperties:
type: string
-
+ type: object
+ additionalProperties: false
+ required:
+ - chart
+ - kubeConfigName
+ - imageMapping
+ properties:
+ chart:
+ $ref: '#/definitions/resourceRef'
+ additionalProperties: false
+ subcharts:
+ type: object
+ additionalProperties:
+ $ref: '#/definitions/resourceRef'
release:
type: string
createNamespace:
@@ -64,4 +70,3 @@ configScheme:
type: object
kubeConfigName:
type: string
-
diff --git a/components/ocmcli/Makefile b/components/ocmcli/Makefile
index 2246f86a5..0ea2a59ef 100644
--- a/components/ocmcli/Makefile
+++ b/components/ocmcli/Makefile
@@ -15,8 +15,10 @@ EFFECTIVE_VERSION = $(VERSION)+$(COMMIT)
CMDSRCS=$(shell find $(REPO_ROOT)/cmds/$(CMD) -type f) Makefile
OCMSRCS=$(shell find $(REPO_ROOT)/pkg -type f) $(REPO_ROOT)/go.*
+CREDS := $(shell $(REPO_ROOT)/hack/githubcreds.sh)
+OCM = go run $(REPO_ROOT)/cmds/ocm $(CREDS)
+
GEN = $(REPO_ROOT)/gen/$(shell basename $(realpath .))
-OCM = go run $(REPO_ROOT)/cmds/ocm
NOW := $(shell date -u +%FT%T%z)
BUILD_FLAGS := "-s -w \
diff --git a/docs/reference/ocm.md b/docs/reference/ocm.md
index bb8aeebb4..231836d88 100644
--- a/docs/reference/ocm.md
+++ b/docs/reference/ocm.md
@@ -15,8 +15,9 @@ ocm [-X <attribute>=<value>+The
--log*
options can be used to configure the logging behaviour.
+There is a quick config option --log-keys
to configure simple
+tag/realm based condition rules. The comma-separated names build an AND rule.
+Hereby, names starting with a slash (/
) denote a realm (without the leading slash).
+A realm is a slash separated sequence of identifiers, which matches all logging realms
+with the given realms as path prefix. A tag directly matches the logging tags.
+Used tags and realms can be found under topic [ocm logging](ocm_logging.md). The ocm coding basically
+uses the realm ocm
.
+The default level to enable is info
. Separated by an equal sign (=
)
+optiobally a dedicated level can be specified. Log levels can be (error
,
+warn
, info
, debug
and trace
.
+The default level is warn
.
+
The value can be a simple type or a json string for complex values. The following
attributes are supported:
+- github.com/mandelsoft/logforward
: *logconfig* Logging config structure used for config forwarding
+
+ THis attribute is used to specify a logging configuration intended
+ to be forwarded to other tool.
+ (For example: TOI passes this config to the executor)
+
- github.com/mandelsoft/oci/cache
[cache
]: *string*
Filesystem folder to use for caching OCI blobs
@@ -157,6 +177,7 @@ attributes are supported:
* [ocm attributes](ocm_attributes.md) — configuration attributes used to control the behaviour
* [ocm configfile](ocm_configfile.md) — configuration file
+* [ocm logging](ocm_logging.md) — Configured logging keys
* [ocm oci-references](ocm_oci-references.md) — notation for OCI references
* [ocm ocm-accessmethods](ocm_ocm-accessmethods.md) — List of all supported access methods
* [ocm ocm-references](ocm_ocm-references.md) — notation for OCM references
diff --git a/docs/reference/ocm_add_componentversions.md b/docs/reference/ocm_add_componentversions.md
index f82bba482..ddd3896bc 100644
--- a/docs/reference/ocm_add_componentversions.md
+++ b/docs/reference/ocm_add_componentversions.md
@@ -10,11 +10,14 @@ ocm add componentversions [--force
will recreate an empty archive if it already exists.
+If option --complete
is given all component versions referenced by
+the added one, will be added, also. Therefore, the --lookup
is required
+to specify an OCM repository to lookup the missing component versions. If
+additionally the -V
is given, the resources of those additional
+components will be added by value.
+
The source, resource and reference list can be composed according the commands
[ocm add sources](ocm_add_sources.md), [ocm add resources](ocm_add_resources.md), [ocm add references](ocm_add_references.md), respectively.
@@ -95,6 +104,22 @@ There are several templaters that can be selected by the --templater
+If a component lookup for building a reference closure is required
+the --lookup
option can be used to specify a fallback
+lookup repository.
+By default the component versions are searched in the repository
+holding the component version for which the closure is determined.
+For *Component Archives* this is never possible, because it only
+contains a single component version. Therefore, in this scenario
+this option must always be specified to be able to follow component
+references.
+
+It the option --copy-resources
is given, all referential
+resources will potentially be localized, mapped to component version local
+resources in the target repository.
+This behaviour can be further influenced by specifying a transfer script
+with the script
option family.
+
### Examples
diff --git a/docs/reference/ocm_add_source-configuration.md b/docs/reference/ocm_add_source-configuration.md
index 4daacc5d6..81abf52cf 100644
--- a/docs/reference/ocm_add_source-configuration.md
+++ b/docs/reference/ocm_add_source-configuration.md
@@ -96,7 +96,7 @@ or input
fields of the description file format.
Elements must follow the resource meta data
description scheme of the component descriptor.
-If not specified anywhere the artifact type will be defaulted to filesystem
.
+If not specified anywhere the artifact type will be defaulted to directoryTree
.
If expressions/templates are used in the specification file an appropriate
templater and the required settings might be required to provide
diff --git a/docs/reference/ocm_attributes.md b/docs/reference/ocm_attributes.md
index 5bd28a760..06d83c739 100644
--- a/docs/reference/ocm_attributes.md
+++ b/docs/reference/ocm_attributes.md
@@ -10,6 +10,12 @@ command line options of the main command (see [ocm](ocm.md)).
The following options are available in the currently used version of the
OCM library:
+- github.com/mandelsoft/logforward
: *logconfig* Logging config structure used for config forwarding
+
+ THis attribute is used to specify a logging configuration intended
+ to be forwarded to other tool.
+ (For example: TOI passes this config to the executor)
+
- github.com/mandelsoft/oci/cache
[cache
]: *string*
Filesystem folder to use for caching OCI blobs
diff --git a/docs/reference/ocm_bootstrap.md b/docs/reference/ocm_bootstrap.md
index 803f78a04..14ed4fb87 100644
--- a/docs/reference/ocm_bootstrap.md
+++ b/docs/reference/ocm_bootstrap.md
@@ -21,5 +21,6 @@ ocm bootstrap [toiPackage
.
This is a simple YAML file resource describing the bootstrapping of a dedicated kind
of software. See also the topic [ocm toi toi-bootstrapping](ocm_toi_toi-bootstrapping.md).
-THis resource finally describes an executor image, which will be executed in a
-container with the installation source and (instance specific) user settings.
-The container is just executed, the framework make no assumption about the
-meaning/outcome of the execution. Therefore, any kind of actions can be described and
-issued this way, not on installation handling.
-
The first matching resource of this type is selected. Optionally a set of
identity attribute can be specified used to refine the match. This can be the
resource name and/or other key/value pairs (<attr>=<value>
).
-If no output file is provided, the yaml representation of the outputs are
-printed to standard out. If the output file is a directory, for every output a
-dedicated file is created, otherwise the yaml representation is stored to the
-file.
-
If no credentials file name is provided (option -c) the file
-TOICredentials
is used, if present. If no parameter file name is
-provided (option -p) the file TOIParameters
is used, if present.
+TOICredentials
is used. If no parameter file name is
+provided (option -p) the file TOIParameters
is used.
+
+For more details about those files see [ ocm bootstrap package](_ocm_bootstrap_package.md).
If the --repo
option is specified, the given names are interpreted
relative to the specified repository using the syntax
@@ -104,7 +95,7 @@ references.
### Examples
```
-$ ocm toi bootstrap componentversion ghcr.io/mandelsoft/ocmdemoinstaller:0.0.1-dev
+$ ocm toi bootstrap config ghcr.io/mandelsoft/ocm//ocmdemoinstaller:0.0.1-dev
```
### SEE ALSO
@@ -118,10 +109,11 @@ $ ocm toi bootstrap componentversion ghcr.io/mandelsoft/ocmdemoinstaller:0.0.1-d
##### Additional Help Topics
-* [ocm bootstrap componentversions toi-bootstrapping](ocm_bootstrap_componentversions_toi-bootstrapping.md) — Tiny OCM Installer based on component versions
+* [ocm bootstrap configuration toi-bootstrapping](ocm_bootstrap_configuration_toi-bootstrapping.md) — Tiny OCM Installer based on component versions
##### Additional Links
* [ocm toi toi-bootstrapping](ocm_toi_toi-bootstrapping.md)
+* [ ocm bootstrap package](_ocm_bootstrap_package.md)
diff --git a/docs/reference/ocm_bootstrap_configuration_toi-bootstrapping.md b/docs/reference/ocm_bootstrap_configuration_toi-bootstrapping.md
new file mode 100644
index 000000000..09b69ccdb
--- /dev/null
+++ b/docs/reference/ocm_bootstrap_configuration_toi-bootstrapping.md
@@ -0,0 +1,383 @@
+## ocm bootstrap configuration toi-bootstrapping — Tiny OCM Installer Based On Component Versions
+
+### Description
+
+
+TOI is a small toolset on top of the Open Component Model. It provides
+a possibility to run images taken from a component version with user
+configuration and feed them with the content of this component version.
+It is some basic mechanism which can be used to execute simple installation
+steps based on content described by the Open Component Model
+(see [ocm bootstrap componentversions](ocm_bootstrap_componentversions.md)).
+
+Therefore, a dedicated resource type toiPackage
is defined.
+It is selected by a resource identity pattern. The first resource matching the pattern
+is used. A possible use case could be to provide different packages for
+different environments. The resource can use an identity attribute
+platform=<value>
. By specifying just the platform attribute,
+the appropriate package will be chosen.
+
+The bootstrap command uses this resource to determine a TOI executor together
+executor configuration and additional client specific settings to describe
+a dedicated installation.
+
+To do this the package describes dedicated actions that can be executed by
+the bootstrap command. Every action refers to an executor, which is executed
+to perform the action. Finally, an executor is an image following the TOI
+specification for passing information into the image execution and receiving
+results from the execution. Such an image is described in two ways:
+- it either describes a resource of type ociImage
or
+- it describes a resource of type toiExecutor
, which defines
+ the image to use and some default settings and further describes the features
+ and requirements of the executor image.
+
+The package described credentials requirements and required user configuration
+which must passed along with the bootstrap command. After validation of the
+input finally a container with the selected executor image is created, that
+contains the content of the initial component version in form of a Common
+Transport Archive and all the specified configuration data.
+
+The execution of the container may do the needful to achieve the goal of the
+requested action and provide some labeled output files, which will be passed
+to the caller.
+
+### The toiPackage
Resource
+
+This resource describes an installable software package, whose content is
+contained in the component version, which contains the package resource.
+
+It is a plain yaml resource with the media types media type application/x-yaml
,
+text/yaml
or
+application/vnd.toi.ocm.software.package.v1+yaml
) containing
+information required to control the instantiation of an executor.
+
+It has the following format:
+
+- **description
** (optional) *string*
+
+ A short description of the installation package and some configuration hints.
+
+- **executors
** *[]ExecutorSpecification*
+
+- **configTemplate
** (optional) *yaml*
+
+ This is a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ The user config that is finally passed to the executor. If no template
+ is specified the user parameter input will be processed directly without template.
+
+- **configScheme
** (optional) *yaml*
+
+ This is a [JSONSCHEMA](https://json-schema.org/) used to validate the user
+ input prior to merging with the template
+
+- **templateLibraries
** (optional) *[]ResourceReference*
+
+ This is a list of resources whose content is used as additional stubs
+ for the template processing.
+
+- **credentials
** (optional) *map[string]CredentialRequest*
+
+ Here the package may request the provisioning of some credentials with a
+ dedicated name/purpose and structure. If specified the bootstrap command
+ requites the specification of a credentials file providing the information
+ how to satisfy those credential requests.
+
+- **additionalResources
** (optional) *map[string]ResourceReference*
+
+ A set of additional resources specified by OCM resource references.
+ The key describes the meaning of the resource. The following keys have
+ a special meaning:
+
+ - **configFile
**: an example template for a parameter file
+ - **credentialsFile
**: an example template for a credentials file
+
+ Those templates can be downloaded with [ocm bootstrap config](ocm_bootstrap_config.md).
+
+#### *ExecutorSpecification*
+
+The executor specification describes the available actions and their mapping
+to executors. It uses the following fields:
+
+- **actions
** *[]string*
+
+ The list of actions this executor can be used for. If nothings is specified
+ the executor will be used for all actions. The first matching executor entry
+ will be used to execute an action by the bootstrap command
+
+- **resourceRef
** *[]ResourceReference*
+
+ An OCM resource reference describing a component version resource relative to
+ the component version containing the package resource.
+
+- **config
** (optional) *yaml*
+
+ This is optional static executor config passed to the executor as is. It is
+ to describe the set of elements on which the actual execution of the executor
+ should work on.
+
+- **parameterMapping
** (optional) *spiff yaml*
+
+ This is an optional spiff template used to process the actual parameter set
+ passed by the caller to transform it to the requirements of the actual executor.
+
+ A package has global parameter setting, but possibly multiple different
+ executors for different action. They might have different requirements/formats
+ concerning the parameter input. There the executor specification allows to
+ map the provided user input, accordingly
+
+- **credentialMapping
** (optional) *map[string]string*
+
+ This is an optional mapping to map credential names used by the package
+ to the needs of dedicated executors.
+
+ A package has global parameter setting, but possibly multiple different
+ executors for different action. They might have different requirements/formats
+ concerning the parameter input. There the executor specification allows to
+ map the provided user input, accordingly
+
+- **image
** (development) *object*
+
+ Instead of a resourceRef
it is possible to directly specify an
+ absolute image.
+
+ ATTENTION: this is intended for development purposes, ONLY. Do not use it
+ for final component versions.
+
+ It has the field ref
and the optional field digest
.
+
+- **outputs
** (optional) *map[string]string*
+
+ This field can be used to map the names of outputs provided by a dedicated
+ executor outputs to package outputs.
+
+#### *ResourceReference*
+
+An OCM resource reference describes a resource of a component version. It is
+always evaluated relative to the component version providing the resource
+that contains the resource reference. It uses the following fields:
+
+- **resourcePath
** (optional) *[]Identity*
+
+ This is sequence of reference identities used to follow a chain of
+ component version references starting with the actual component version.
+ If not specified the specified resource will be taken from the actual
+ component version.
+
+- **resource
** *Identity*
+
+ This is the identity of the resource in the selected component version.
+
+#### *Identity*
+
+An identity specification is a map[string]string
. It describes
+the identity attributes of a desired resource in a component version.
+It always has at least one identity attribute name
, which
+is the resource name field of the desired resource. If this resource
+defines additional identity attributes, the complete set must be specified.
+
+### The toiExecutor
Resource
+
+Instead of directly describing an image resource i the package file, it is
+possible to refer to a resource of type toiExecutor. This
+is a yaml file with the media type application/x-yaml
,
+text/yaml
or
+application/vnd.toi.ocm.software.package.v1+yaml
) containing
+common information about the executor executor. If used by the package,
+this information is used to validate settings in the package specification.
+
+It has the following format:
+
+- **imageRef
** *ResourceReference*
+
+ This field reference the image resource relative to the component version
+ providing the executor resource
+
+- **configTemplate
** (optional) *yaml*
+
+ This a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ The executor config from the package specification that is finally passed to
+ the executor. If no template is specified the executor config specified in
+ the package will be processed directly without template.
+
+- **configScheme
** (optional) *yaml*
+
+ This is a [JSONSCHEMA](https://json-schema.org/) used to validate the executor
+ config from the package prior to merging with the template
+
+- **templateLibraries
** (optional) *[]ResourceReference*
+
+ This is a list of resources whose content is used as additional stubs
+ for the template processing.
+
+- **credentials
** (optional) *map[string]CredentialRequest*
+
+ Here the executor may request the provisioning of some credentials with a
+ dedicated name/purpose and structure. If specified it will be propagated
+ to a using package. It this uses an own credentials section, this one
+ will be filtered and checked for the actual executor.
+
+- **outputs
** (optional) *map[string]OutputSpecification*
+
+ This field can be used to describe the provided outputs of this executor.
+ The *OutputSpecification* contains only the field description
,
+ so far. It is intended to be extended to contain further information to more
+ formally describe the type of output.
+
+- **image
** (development) *object*
+
+ Instead of an imageRef
it is possible to directly specify an
+ absolute image.
+
+ ATTENTION: this is intended for development purposes, ONLY. Do not use it
+ for final component versions.
+
+ It has the field ref
and the optional field digest
.
+
+### Client Parameters
+
+Common to all executors a parameter file can be provided by the caller. The
+package specification may provide a [spiff template](https://github.com/mandelsoft/spiff)
+for this parameter file. It can be used, for example to provide useful defaults.
+The actually provided content is merged with this template.
+
+To validate user configuration a JSON scheme can be provided. The user input is
+validated first against this scheme before the actual merge is done.
+
+### Credentials
+
+Additionally credentials can be requested to be provided by a client.
+This is done with the credentials
field. It is a map
+of credentials names and their meaning and/or handling.
+
+It uses the following fields:
+
+- **description
** *string*
+
+ This field should describe the purpose of the credential.
+
+- **properties
** *map[string]string*
+
+ This field should describe the used credential fields
+
+- **consumerId
** *map[string]*
+
+ This field can be used to optionally define a consumer id that should be set
+ in the OCM support library, if used by the executor. At least the field
+ type
and one additional field must be set.
+
+Credentials are provided in an ocm config file (see [ocm configfile](ocm_configfile.md)).
+It uses a memory credential repository with the name default
+to store the credentials under the given name. Additionally appropriate
+consumer ids will be propagated, if requested in the credentials request config.
+
+### Executor Image Contract
+
+The executor image is called with the action as additional argument. It is
+expected that is defines a default entry point and a potentially empty list of
+standard arguments.
+
+It is called with two arguments:
+- name of the action to execute
+- identity of the component version containing the package the executor
+ is executed for.
+
+ This can be used to access the component descriptor to get access to
+ further described resources in the executor config
+
+The container used to execute the executor image gets prepared a standard
+filesystem structure used to provide all the executor inputs before the
+execution and reading provided executor outputs after the execution.
+
++/ +└── toi + ├── inputs + │  ├── config configuration from package specification + │  ├── ocmrepo OCM filesystem repository containing the complete + │  │ component version of the package + │  └── parameters merged complete parameter file + ├── outputs + │  ├── <out> any number of arbitrary output data provided + │  │ by executor + │  └── ... + └── run good practice: typical location for the executed command ++ +After processing it is possible to return named outputs. The name of an output +must be a filename. The executor section in the package specification maps those +files to logical outputs in the
outputs
section.
+
+toiPackage
.
+This is a simple YAML file resource describing the bootstrapping of a dedicated kind
+of software. See also the topic [ocm toi toi-bootstrapping](ocm_toi_toi-bootstrapping.md).
+
+This resource finally describes an executor image, which will be executed in a
+container with the installation source and (instance specific) user settings.
+The container is just executed, the framework make no assumption about the
+meaning/outcome of the execution. Therefore, any kind of actions can be described and
+issued this way, not on installation handling.
+
+The first matching resource of this type is selected. Optionally a set of
+identity attribute can be specified used to refine the match. This can be the
+resource name and/or other key/value pairs (<attr>=<value>
).
+
+If no output file is provided, the yaml representation of the outputs are
+printed to standard out. If the output file is a directory, for every output a
+dedicated file is created, otherwise the yaml representation is stored to the
+file.
+
+If no credentials file name is provided (option -c) the file
+TOICredentials
is used, if present. If no parameter file name is
+provided (option -p) the file TOIParameters
is used, if present.
+
+Using the credentials file it is possible to configure credentials required by
+the installation package or executor. Additionally arbitrary consumer ids
+can be forwarded to executor, which might be required by accessing blobs
+described by external access methods.
+
+The credentials file uses the following yaml format:
+- credentials
*map[string]CredentialsSpec*
+
+ The resolution of credentials requested by the package (by name).
+
+- forwardedConsumers
*[]ForwardSpec* (optional)
+
+ An optional list of consumer specifications to be forwarded to the OCM
+ configuration provided to the executor.
+
+The *CredentialsSpec* uses the following format:
+
+- consumerId
*map[string]string*
+
+ The consumer id used to look up the credentials.
+
+- consumerType
*string* (optional) (default: partial)
+
+ The type of the matcher used to match the consumer id.
+
+- reference
*yaml*
+
+ A generic credential specification as used in the ocm config file.
+
+- credentials
*map[string]string*
+
+ Direct credential fields.
+
+One of consumerId
, reference
or credentials
+must be configured.
+
+The *ForwardSpec* uses the following format:
+
+- consumerId
*map[string]string*
+
+ The consumer id to be forwarded.
+
+- consumerType
*string* (optional) (default: partial)
+
+ The type of the matcher used to match the consumer id.
+
+If provided by the package it is possible to download template versions
+for the parameter and credentials file using the command [ocm bootstrap configuration](ocm_bootstrap_configuration.md).
+
+If the --repo
option is specified, the given names are interpreted
+relative to the specified repository using the syntax
+
+<component>[:<version>]+
--repo
option is specified the given names are interpreted
+as located OCM component version references:
+
+[<repo type>::]<host>[:<port>][/<base path>]//<component>[:<version>]+
[<repo type>::]<filepath>|<spec json>[//<component>[:<version>]]+
--repo
option takes an OCM repository specification:
+
+[<repo type>::]<configured name>|<file path>|<spec json>+
directory
,
+tar
or tgz
is possible.
+
+Using the JSON variant any repository type supported by the
+linked library can be used:
+
+Dedicated OCM repository types:
+- `ComponentArchive`
+
+OCI Repository types (using standard component repository to OCI mapping):
+- `ArtifactSet`
+- `CommonTransportFormat`
+- `DockerDaemon`
+- `Empty`
+- `OCIRegistry`
+- `oci`
+- `ociRegistry`
+
+If a component lookup for building a reference closure is required
+the --lookup
option can be used to specify a fallback
+lookup repository.
+By default the component versions are searched in the repository
+holding the component version for which the closure is determined.
+For *Component Archives* this is never possible, because it only
+contains a single component version. Therefore, in this scenario
+this option must always be specified to be able to follow component
+references.
+
+
+### Examples
+
+```
+$ ocm toi bootstrap package ghcr.io/mandelsoft/ocm//ocmdemoinstaller:0.0.1-dev
+```
+
+### SEE ALSO
+
+##### Parents
+
+* [ocm bootstrap](ocm_bootstrap.md) — bootstrap components
+* [ocm](ocm.md) — Open Component Model command line client
+
+
+
+##### Additional Help Topics
+
+* [ocm bootstrap package toi-bootstrapping](ocm_bootstrap_package_toi-bootstrapping.md) — Tiny OCM Installer based on component versions
+
+
+##### Additional Links
+
+* [ocm toi toi-bootstrapping](ocm_toi_toi-bootstrapping.md)
+* [ocm bootstrap configuration](ocm_bootstrap_configuration.md) — bootstrap TOI configuration files
+
diff --git a/docs/reference/ocm_bootstrap_componentversions_toi-bootstrapping.md b/docs/reference/ocm_bootstrap_package_toi-bootstrapping.md
similarity index 92%
rename from docs/reference/ocm_bootstrap_componentversions_toi-bootstrapping.md
rename to docs/reference/ocm_bootstrap_package_toi-bootstrapping.md
index d5f60ee94..c84bf68b2 100644
--- a/docs/reference/ocm_bootstrap_componentversions_toi-bootstrapping.md
+++ b/docs/reference/ocm_bootstrap_package_toi-bootstrapping.md
@@ -1,4 +1,4 @@
-## ocm bootstrap componentversions toi-bootstrapping — Tiny OCM Installer Based On Component Versions
+## ocm bootstrap package toi-bootstrapping — Tiny OCM Installer Based On Component Versions
### Description
@@ -53,11 +53,15 @@ information required to control the instantiation of an executor.
It has the following format:
+- **description
** (optional) *string*
+
+ A short description of the installation package and some configuration hints.
+
- **executors
** *[]ExecutorSpecification*
- **configTemplate
** (optional) *yaml*
- This a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ This is a [spiff](https://github.com/mandelsoft/spiff) template used to generate
The user config that is finally passed to the executor. If no template
is specified the user parameter input will be processed directly without template.
@@ -78,6 +82,17 @@ It has the following format:
requites the specification of a credentials file providing the information
how to satisfy those credential requests.
+- **additionalResources
** (optional) *map[string]ResourceReference*
+
+ A set of additional resources specified by OCM resource references.
+ The key describes the meaning of the resource. The following keys have
+ a special meaning:
+
+ - **configFile
**: an example template for a parameter file
+ - **credentialsFile
**: an example template for a credentials file
+
+ Those templates can be downloaded with [ocm bootstrap config](ocm_bootstrap_config.md).
+
#### *ExecutorSpecification*
The executor specification describes the available actions and their mapping
@@ -304,6 +319,8 @@ by the TOI toolset.
### Examples
```
+description: |
+ This package is just an example.
executors:
- actions:
- install
@@ -342,13 +359,17 @@ configScheme:
type: string
password:
type: string
+additionalResources:
+ configFile:
+ resource:
+ name: config-file
```
### SEE ALSO
##### Parents
-* [ocm bootstrap componentversions](ocm_bootstrap_componentversions.md) — bootstrap component version
+* [ocm bootstrap package](ocm_bootstrap_package.md) — bootstrap component version
* [ocm bootstrap](ocm_bootstrap.md) — bootstrap components
* [ocm](ocm.md) — Open Component Model command line client
@@ -356,5 +377,7 @@ configScheme:
##### Additional Links
+* [ocm bootstrap componentversions](ocm_bootstrap_componentversions.md)
+* [ocm bootstrap config](ocm_bootstrap_config.md)
* [ocm configfile](ocm_configfile.md) — configuration file
diff --git a/docs/reference/ocm_describe.md b/docs/reference/ocm_describe.md
index 11de60e71..527ba3bcd 100644
--- a/docs/reference/ocm_describe.md
+++ b/docs/reference/ocm_describe.md
@@ -22,5 +22,6 @@ ocm describe [toiPackage
.
+This is a simple YAML file resource describing the bootstrapping of a dedicated kind
+of software. See also the topic [ocm toi toi-bootstrapping](ocm_toi_toi-bootstrapping.md).
+
+The first matching resource of this type is selected. Optionally a set of
+identity attribute can be specified used to refine the match. This can be the
+resource name and/or other key/value pairs (<attr>=<value>
).
+
+If the --repo
option is specified, the given names are interpreted
+relative to the specified repository using the syntax
+
+<component>[:<version>]+
--repo
option is specified the given names are interpreted
+as located OCM component version references:
+
+[<repo type>::]<host>[:<port>][/<base path>]//<component>[:<version>]+
[<repo type>::]<filepath>|<spec json>[//<component>[:<version>]]+
--repo
option takes an OCM repository specification:
+
+[<repo type>::]<configured name>|<file path>|<spec json>+
directory
,
+tar
or tgz
is possible.
+
+Using the JSON variant any repository type supported by the
+linked library can be used:
+
+Dedicated OCM repository types:
+- `ComponentArchive`
+
+OCI Repository types (using standard component repository to OCI mapping):
+- `ArtifactSet`
+- `CommonTransportFormat`
+- `DockerDaemon`
+- `Empty`
+- `OCIRegistry`
+- `oci`
+- `ociRegistry`
+
+If a component lookup for building a reference closure is required
+the --lookup
option can be used to specify a fallback
+lookup repository.
+By default the component versions are searched in the repository
+holding the component version for which the closure is determined.
+For *Component Archives* this is never possible, because it only
+contains a single component version. Therefore, in this scenario
+this option must always be specified to be able to follow component
+references.
+
+
+### Examples
+
+```
+$ ocm toi describe package ghcr.io/mandelsoft/ocm//ocmdemoinstaller:0.0.1-dev
+```
+
+### SEE ALSO
+
+##### Parents
+
+* [ocm describe](ocm_describe.md) — Describe various elements by using appropriate sub commands.
+* [ocm](ocm.md) — Open Component Model command line client
+
+
+
+##### Additional Help Topics
+
+* [ocm describe package toi-bootstrapping](ocm_describe_package_toi-bootstrapping.md) — Tiny OCM Installer based on component versions
+
+
+##### Additional Links
+
+* [ocm toi toi-bootstrapping](ocm_toi_toi-bootstrapping.md)
+
diff --git a/docs/reference/ocm_describe_package_toi-bootstrapping.md b/docs/reference/ocm_describe_package_toi-bootstrapping.md
new file mode 100644
index 000000000..2e038fffa
--- /dev/null
+++ b/docs/reference/ocm_describe_package_toi-bootstrapping.md
@@ -0,0 +1,383 @@
+## ocm describe package toi-bootstrapping — Tiny OCM Installer Based On Component Versions
+
+### Description
+
+
+TOI is a small toolset on top of the Open Component Model. It provides
+a possibility to run images taken from a component version with user
+configuration and feed them with the content of this component version.
+It is some basic mechanism which can be used to execute simple installation
+steps based on content described by the Open Component Model
+(see [ocm bootstrap componentversions](ocm_bootstrap_componentversions.md)).
+
+Therefore, a dedicated resource type toiPackage
is defined.
+It is selected by a resource identity pattern. The first resource matching the pattern
+is used. A possible use case could be to provide different packages for
+different environments. The resource can use an identity attribute
+platform=<value>
. By specifying just the platform attribute,
+the appropriate package will be chosen.
+
+The bootstrap command uses this resource to determine a TOI executor together
+executor configuration and additional client specific settings to describe
+a dedicated installation.
+
+To do this the package describes dedicated actions that can be executed by
+the bootstrap command. Every action refers to an executor, which is executed
+to perform the action. Finally, an executor is an image following the TOI
+specification for passing information into the image execution and receiving
+results from the execution. Such an image is described in two ways:
+- it either describes a resource of type ociImage
or
+- it describes a resource of type toiExecutor
, which defines
+ the image to use and some default settings and further describes the features
+ and requirements of the executor image.
+
+The package described credentials requirements and required user configuration
+which must passed along with the bootstrap command. After validation of the
+input finally a container with the selected executor image is created, that
+contains the content of the initial component version in form of a Common
+Transport Archive and all the specified configuration data.
+
+The execution of the container may do the needful to achieve the goal of the
+requested action and provide some labeled output files, which will be passed
+to the caller.
+
+### The toiPackage
Resource
+
+This resource describes an installable software package, whose content is
+contained in the component version, which contains the package resource.
+
+It is a plain yaml resource with the media types media type application/x-yaml
,
+text/yaml
or
+application/vnd.toi.ocm.software.package.v1+yaml
) containing
+information required to control the instantiation of an executor.
+
+It has the following format:
+
+- **description
** (optional) *string*
+
+ A short description of the installation package and some configuration hints.
+
+- **executors
** *[]ExecutorSpecification*
+
+- **configTemplate
** (optional) *yaml*
+
+ This is a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ The user config that is finally passed to the executor. If no template
+ is specified the user parameter input will be processed directly without template.
+
+- **configScheme
** (optional) *yaml*
+
+ This is a [JSONSCHEMA](https://json-schema.org/) used to validate the user
+ input prior to merging with the template
+
+- **templateLibraries
** (optional) *[]ResourceReference*
+
+ This is a list of resources whose content is used as additional stubs
+ for the template processing.
+
+- **credentials
** (optional) *map[string]CredentialRequest*
+
+ Here the package may request the provisioning of some credentials with a
+ dedicated name/purpose and structure. If specified the bootstrap command
+ requites the specification of a credentials file providing the information
+ how to satisfy those credential requests.
+
+- **additionalResources
** (optional) *map[string]ResourceReference*
+
+ A set of additional resources specified by OCM resource references.
+ The key describes the meaning of the resource. The following keys have
+ a special meaning:
+
+ - **configFile
**: an example template for a parameter file
+ - **credentialsFile
**: an example template for a credentials file
+
+ Those templates can be downloaded with [ocm bootstrap config](ocm_bootstrap_config.md).
+
+#### *ExecutorSpecification*
+
+The executor specification describes the available actions and their mapping
+to executors. It uses the following fields:
+
+- **actions
** *[]string*
+
+ The list of actions this executor can be used for. If nothings is specified
+ the executor will be used for all actions. The first matching executor entry
+ will be used to execute an action by the bootstrap command
+
+- **resourceRef
** *[]ResourceReference*
+
+ An OCM resource reference describing a component version resource relative to
+ the component version containing the package resource.
+
+- **config
** (optional) *yaml*
+
+ This is optional static executor config passed to the executor as is. It is
+ to describe the set of elements on which the actual execution of the executor
+ should work on.
+
+- **parameterMapping
** (optional) *spiff yaml*
+
+ This is an optional spiff template used to process the actual parameter set
+ passed by the caller to transform it to the requirements of the actual executor.
+
+ A package has global parameter setting, but possibly multiple different
+ executors for different action. They might have different requirements/formats
+ concerning the parameter input. There the executor specification allows to
+ map the provided user input, accordingly
+
+- **credentialMapping
** (optional) *map[string]string*
+
+ This is an optional mapping to map credential names used by the package
+ to the needs of dedicated executors.
+
+ A package has global parameter setting, but possibly multiple different
+ executors for different action. They might have different requirements/formats
+ concerning the parameter input. There the executor specification allows to
+ map the provided user input, accordingly
+
+- **image
** (development) *object*
+
+ Instead of a resourceRef
it is possible to directly specify an
+ absolute image.
+
+ ATTENTION: this is intended for development purposes, ONLY. Do not use it
+ for final component versions.
+
+ It has the field ref
and the optional field digest
.
+
+- **outputs
** (optional) *map[string]string*
+
+ This field can be used to map the names of outputs provided by a dedicated
+ executor outputs to package outputs.
+
+#### *ResourceReference*
+
+An OCM resource reference describes a resource of a component version. It is
+always evaluated relative to the component version providing the resource
+that contains the resource reference. It uses the following fields:
+
+- **resourcePath
** (optional) *[]Identity*
+
+ This is sequence of reference identities used to follow a chain of
+ component version references starting with the actual component version.
+ If not specified the specified resource will be taken from the actual
+ component version.
+
+- **resource
** *Identity*
+
+ This is the identity of the resource in the selected component version.
+
+#### *Identity*
+
+An identity specification is a map[string]string
. It describes
+the identity attributes of a desired resource in a component version.
+It always has at least one identity attribute name
, which
+is the resource name field of the desired resource. If this resource
+defines additional identity attributes, the complete set must be specified.
+
+### The toiExecutor
Resource
+
+Instead of directly describing an image resource i the package file, it is
+possible to refer to a resource of type toiExecutor. This
+is a yaml file with the media type application/x-yaml
,
+text/yaml
or
+application/vnd.toi.ocm.software.package.v1+yaml
) containing
+common information about the executor executor. If used by the package,
+this information is used to validate settings in the package specification.
+
+It has the following format:
+
+- **imageRef
** *ResourceReference*
+
+ This field reference the image resource relative to the component version
+ providing the executor resource
+
+- **configTemplate
** (optional) *yaml*
+
+ This a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ The executor config from the package specification that is finally passed to
+ the executor. If no template is specified the executor config specified in
+ the package will be processed directly without template.
+
+- **configScheme
** (optional) *yaml*
+
+ This is a [JSONSCHEMA](https://json-schema.org/) used to validate the executor
+ config from the package prior to merging with the template
+
+- **templateLibraries
** (optional) *[]ResourceReference*
+
+ This is a list of resources whose content is used as additional stubs
+ for the template processing.
+
+- **credentials
** (optional) *map[string]CredentialRequest*
+
+ Here the executor may request the provisioning of some credentials with a
+ dedicated name/purpose and structure. If specified it will be propagated
+ to a using package. It this uses an own credentials section, this one
+ will be filtered and checked for the actual executor.
+
+- **outputs
** (optional) *map[string]OutputSpecification*
+
+ This field can be used to describe the provided outputs of this executor.
+ The *OutputSpecification* contains only the field description
,
+ so far. It is intended to be extended to contain further information to more
+ formally describe the type of output.
+
+- **image
** (development) *object*
+
+ Instead of an imageRef
it is possible to directly specify an
+ absolute image.
+
+ ATTENTION: this is intended for development purposes, ONLY. Do not use it
+ for final component versions.
+
+ It has the field ref
and the optional field digest
.
+
+### Client Parameters
+
+Common to all executors a parameter file can be provided by the caller. The
+package specification may provide a [spiff template](https://github.com/mandelsoft/spiff)
+for this parameter file. It can be used, for example to provide useful defaults.
+The actually provided content is merged with this template.
+
+To validate user configuration a JSON scheme can be provided. The user input is
+validated first against this scheme before the actual merge is done.
+
+### Credentials
+
+Additionally credentials can be requested to be provided by a client.
+This is done with the credentials
field. It is a map
+of credentials names and their meaning and/or handling.
+
+It uses the following fields:
+
+- **description
** *string*
+
+ This field should describe the purpose of the credential.
+
+- **properties
** *map[string]string*
+
+ This field should describe the used credential fields
+
+- **consumerId
** *map[string]*
+
+ This field can be used to optionally define a consumer id that should be set
+ in the OCM support library, if used by the executor. At least the field
+ type
and one additional field must be set.
+
+Credentials are provided in an ocm config file (see [ocm configfile](ocm_configfile.md)).
+It uses a memory credential repository with the name default
+to store the credentials under the given name. Additionally appropriate
+consumer ids will be propagated, if requested in the credentials request config.
+
+### Executor Image Contract
+
+The executor image is called with the action as additional argument. It is
+expected that is defines a default entry point and a potentially empty list of
+standard arguments.
+
+It is called with two arguments:
+- name of the action to execute
+- identity of the component version containing the package the executor
+ is executed for.
+
+ This can be used to access the component descriptor to get access to
+ further described resources in the executor config
+
+The container used to execute the executor image gets prepared a standard
+filesystem structure used to provide all the executor inputs before the
+execution and reading provided executor outputs after the execution.
+
++/ +└── toi + ├── inputs + │  ├── config configuration from package specification + │  ├── ocmrepo OCM filesystem repository containing the complete + │  │ component version of the package + │  └── parameters merged complete parameter file + ├── outputs + │  ├── <out> any number of arbitrary output data provided + │  │ by executor + │  └── ... + └── run good practice: typical location for the executed command ++ +After processing it is possible to return named outputs. The name of an output +must be a filename. The executor section in the package specification maps those +files to logical outputs in the
outputs
section.
+
+blobhandler
: execution of blob handler used to upload resource blobs to an ocm repository.
+
+
+
+The following *realms* are used by the command line tool:
+
+ - ocm
: general realm used for the ocm go library.
+ - ocm/accessmethod/ociartifact
: access method ociArtifact
+ - ocm/credentials/dockerconfig
: docker config handling as credential repository
+ - ocm/oci.ocireg
: OCI repository handling
+ - ocm/ocm/toi
: TOI logging
+ - ocm/plugins
: OCM plugin handling
+ - ocm/processing
: output processing chains
+ - ocm/transfer
: OCM transfer handling
+
+
+
+### Examples
+
+```
+type: logging.config.ocm.software
+ contextType: attributes.context.ocm.software
+ settings:
+ defaultLevel: Info
+ rules:
+ - ...
+```
+
+### SEE ALSO
+
+##### Parents
+
+* [ocm](ocm.md) — Open Component Model command line client
+
+
+
+##### Additional Links
+
+* [ocm configfile](ocm_configfile.md) — configuration file
+
diff --git a/docs/reference/ocm_toi-bootstrapping.md b/docs/reference/ocm_toi-bootstrapping.md
index 1ad69d95a..73bec311c 100644
--- a/docs/reference/ocm_toi-bootstrapping.md
+++ b/docs/reference/ocm_toi-bootstrapping.md
@@ -53,11 +53,15 @@ information required to control the instantiation of an executor.
It has the following format:
+- **description
** (optional) *string*
+
+ A short description of the installation package and some configuration hints.
+
- **executors
** *[]ExecutorSpecification*
- **configTemplate
** (optional) *yaml*
- This a [spiff](https://github.com/mandelsoft/spiff) template used to generate
+ This is a [spiff](https://github.com/mandelsoft/spiff) template used to generate
The user config that is finally passed to the executor. If no template
is specified the user parameter input will be processed directly without template.
@@ -78,6 +82,17 @@ It has the following format:
requites the specification of a credentials file providing the information
how to satisfy those credential requests.
+- **additionalResources
** (optional) *map[string]ResourceReference*
+
+ A set of additional resources specified by OCM resource references.
+ The key describes the meaning of the resource. The following keys have
+ a special meaning:
+
+ - **configFile
**: an example template for a parameter file
+ - **credentialsFile
**: an example template for a credentials file
+
+ Those templates can be downloaded with [ocm bootstrap config](ocm_bootstrap_config.md).
+
#### *ExecutorSpecification*
The executor specification describes the available actions and their mapping
@@ -304,6 +319,8 @@ by the TOI toolset.
### Examples
```
+description: |
+ This package is just an example.
executors:
- actions:
- install
@@ -342,6 +359,10 @@ configScheme:
type: string
password:
type: string
+additionalResources:
+ configFile:
+ resource:
+ name: config-file
```
### SEE ALSO
@@ -354,6 +375,7 @@ configScheme:
##### Additional Links
-* [ocm bootstrap componentversions](ocm_bootstrap_componentversions.md) — bootstrap component version
+* [ocm bootstrap componentversions](ocm_bootstrap_componentversions.md)
+* [ocm bootstrap config](ocm_bootstrap_config.md)
* [ocm configfile](ocm_configfile.md) — configuration file
diff --git a/docs/reference/ocm_transfer_commontransportarchive.md b/docs/reference/ocm_transfer_commontransportarchive.md
index 91a465c7b..9d3e840cb 100644
--- a/docs/reference/ocm_transfer_commontransportarchive.md
+++ b/docs/reference/ocm_transfer_commontransportarchive.md
@@ -11,9 +11,12 @@ ocm transfer commontransportarchive [--recursive
the complete reference tree of a component reference is traversed.
+
+If a component lookup for building a reference closure is required
+the --lookup
option can be used to specify a fallback
+lookup repository.
+By default the component versions are searched in the repository
+holding the component version for which the closure is determined.
+For *Component Archives* this is never possible, because it only
+contains a single component version. Therefore, in this scenario
+this option must always be specified to be able to follow component
+references.
+
The --type
option accepts a file format for the
target archive to use. The following formats are supported:
- directory
@@ -40,6 +55,11 @@ resources in the target repository.
This behaviour can be further influenced by specifying a transfer script
with the script
option family.
+It the option --stop-on-existing
is given together with the --recursive
+option, the recursion is stopped for component versions already existing in the
+target repository. This behaviour can be further influenced by specifying a transfer script
+with the script
option family.
+
If the --uploader
option is specified, appropriate uploaders
are configured for the transport target. It has the following format
diff --git a/docs/reference/ocm_transfer_componentversions.md b/docs/reference/ocm_transfer_componentversions.md
index 79797b2e6..005330c55 100644
--- a/docs/reference/ocm_transfer_componentversions.md
+++ b/docs/reference/ocm_transfer_componentversions.md
@@ -19,6 +19,7 @@ ocm transfer componentversions [--stop-on-existing
is given together with the --recursive
+option, the recursion is stopped for component versions already existing in the
+target repository. This behaviour can be further influenced by specifying a transfer script
+with the script
option family.
+
If the --uploader
option is specified, appropriate uploaders
are configured for the transport target. It has the following format
diff --git a/examples/lib/config.md b/examples/lib/config.md
index 351159bb6..7780c22ab 100644
--- a/examples/lib/config.md
+++ b/examples/lib/config.md
@@ -73,7 +73,7 @@ context, which is used to apply the configuration object.
After the object has been applied, the result can be observed on the
intended target object. For a credential configuration this is the
credential context. Here, the credentials for the configured consumer id can
-be queried. In the example this is a crednetials object valid for an
+be queried. In the example this is a credentials object valid for an
OCI registry.
```go
diff --git a/go.mod b/go.mod
index da817318a..7d199d1a9 100644
--- a/go.mod
+++ b/go.mod
@@ -11,7 +11,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/go-logr/logr v1.2.3
github.com/mandelsoft/filepath v0.0.0-20220503095057-4432a2285b68
- github.com/mandelsoft/spiff v1.7.0-beta-3
+ github.com/mandelsoft/spiff v1.3.0-beta-7.0.20230328214234-980037cf1bd7
github.com/modern-go/reflect2 v1.0.2
github.com/onsi/gomega v1.26.0
github.com/opencontainers/go-digest v1.0.0
@@ -45,7 +45,7 @@ require (
github.com/google/go-github/v45 v45.2.0
github.com/klauspost/compress v1.15.11
github.com/klauspost/pgzip v1.2.5
- github.com/mandelsoft/logging v0.0.0-20221114215048-ab754b164dd6
+ github.com/mandelsoft/logging v0.0.0-20230331123830-36542ef18f6f
github.com/mandelsoft/vfs v0.0.0-20220805210647-bf14a11bfe31
github.com/marstr/guid v1.1.0
github.com/mitchellh/copystructure v1.2.0
@@ -110,6 +110,7 @@ require (
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/fatih/color v1.13.0 // indirect
+ github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
@@ -131,6 +132,7 @@ require (
github.com/gorilla/mux v1.8.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
@@ -142,6 +144,7 @@ require (
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.7 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
+ github.com/magiconair/properties v1.8.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
@@ -149,6 +152,7 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
@@ -174,7 +178,9 @@ require (
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
- github.com/spf13/viper v1.13.0 // indirect
+ github.com/spf13/jwalterweatherman v1.1.0 // indirect
+ github.com/spf13/viper v1.14.0 // indirect
+ github.com/subosito/gotenv v1.4.1 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
@@ -189,12 +195,13 @@ require (
golang.org/x/term v0.5.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.4.0 // indirect
- golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
+ golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 // indirect
- google.golang.org/grpc v1.49.0 // indirect
+ google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e // indirect
+ google.golang.org/grpc v1.50.1 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apiserver v0.26.1 // indirect
k8s.io/client-go v0.26.1 // indirect
diff --git a/go.sum b/go.sum
index 25a0a3f59..239f74062 100644
--- a/go.sum
+++ b/go.sum
@@ -501,6 +501,7 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
@@ -855,6 +856,7 @@ github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.3/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -869,6 +871,28 @@ github.com/mandelsoft/filepath v0.0.0-20220503095057-4432a2285b68 h1:99GWPlKVS11
github.com/mandelsoft/filepath v0.0.0-20220503095057-4432a2285b68/go.mod h1:n4xEiUD2HNHnn2w5ZKF0qgjDecHVCWAl5DxZ7+pcFU8=
github.com/mandelsoft/logging v0.0.0-20221114215048-ab754b164dd6 h1:xlUJPmMJo1IVQVn42ELLDUNY3sfwzKVp4kyuy7Q2QwI=
github.com/mandelsoft/logging v0.0.0-20221114215048-ab754b164dd6/go.mod h1:vxGpzOesdqLGRSWb4fz5DaH0ekJvkISst4qq6UZ2xFA=
+github.com/mandelsoft/logging v0.0.0-20230325172347-f3cf57b57a3b h1:tXUSiD3moa3MNo2LYcExdtGFeIS9q/c2GfrIYAp7F4A=
+github.com/mandelsoft/logging v0.0.0-20230325172347-f3cf57b57a3b/go.mod h1:vxGpzOesdqLGRSWb4fz5DaH0ekJvkISst4qq6UZ2xFA=
+github.com/mandelsoft/logging v0.0.0-20230325174853-f20defb47faf h1:7Gvyx+6u7HjY43AB4nfoFVooHHn0fQTd6/2amJO7zeE=
+github.com/mandelsoft/logging v0.0.0-20230325174853-f20defb47faf/go.mod h1:vxGpzOesdqLGRSWb4fz5DaH0ekJvkISst4qq6UZ2xFA=
+github.com/mandelsoft/logging v0.0.0-20230325214959-131c060883d4 h1:nLdjq79y8T8CLiq8YEYNMA/2HxlwgcraU2zrMHlK2Yc=
+github.com/mandelsoft/logging v0.0.0-20230325214959-131c060883d4/go.mod h1:vxGpzOesdqLGRSWb4fz5DaH0ekJvkISst4qq6UZ2xFA=
+github.com/mandelsoft/logging v0.0.0-20230326142042-d490c9985c67 h1:SC3plxm4LxgO6chfw+MgkiLqt0G+rla0CbKo5rBCoKk=
+github.com/mandelsoft/logging v0.0.0-20230326142042-d490c9985c67/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/logging v0.0.0-20230327224233-24ba6742855a h1:QqNkiyg3nNJPlE5K/wYFNzRJSGWyhRNlO3vYUmGmXps=
+github.com/mandelsoft/logging v0.0.0-20230327224233-24ba6742855a/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/logging v0.0.0-20230328095932-6b1146a14c17 h1:uOqccIJP7pm5+Wo7x9APJhVqUH6M31pARjAz5enxHJ4=
+github.com/mandelsoft/logging v0.0.0-20230328095932-6b1146a14c17/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/logging v0.0.0-20230328100121-5dc635fd1fa2 h1:Spc+g9G9nVXf83AWCLSfA4hzlhtI8LF4mvEMHoDErgk=
+github.com/mandelsoft/logging v0.0.0-20230328100121-5dc635fd1fa2/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/logging v0.0.0-20230328101239-150fce59b495 h1:WmsLafaYjuxZs+KK5ANPE0oT1b+taoZ6q0nJty5WxJo=
+github.com/mandelsoft/logging v0.0.0-20230328101239-150fce59b495/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/logging v0.0.0-20230328114449-3d7d8773f059 h1:0wlHM3kOWz/WzbGCqHR8golLW+HPf364+iFhEXC/hn4=
+github.com/mandelsoft/logging v0.0.0-20230328114449-3d7d8773f059/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/logging v0.0.0-20230331123830-36542ef18f6f h1:ZoqAURpfhE54QFLpcRot25aZEA0dfLVcGh0DmG5qLm4=
+github.com/mandelsoft/logging v0.0.0-20230331123830-36542ef18f6f/go.mod h1:HjtBPH5tsJygvyYEu2r/ZwOOBGcu+oR2sKH73VEnHtg=
+github.com/mandelsoft/spiff v1.3.0-beta-7.0.20230328214234-980037cf1bd7 h1:0dHDK+SxCJHTCLIDuafGyeoo50BPYlfjQqihgUA8e6E=
+github.com/mandelsoft/spiff v1.3.0-beta-7.0.20230328214234-980037cf1bd7/go.mod h1:TwEeOPuRZxlzQBCLEyVTlHmBSruSGGNdiQ2fovVJ8ao=
github.com/mandelsoft/spiff v1.7.0-beta-3 h1:AvZldpnctpyfQqtAA5uxokD5rlCK52mGAXxg7tnW5Ag=
github.com/mandelsoft/spiff v1.7.0-beta-3/go.mod h1:3Kg6qrggWO4oc1k++acd6xhcWisJ2YkzxpVZpuYONGg=
github.com/mandelsoft/vfs v0.0.0-20201002080026-d03d33d5889a/go.mod h1:74aV7kulg9C434HiI3zNALN79QHc9IZMN+SI4UdLn14=
@@ -934,6 +958,7 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
@@ -1195,6 +1220,8 @@ github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
+github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
+github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -1217,6 +1244,7 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
+github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/sylabs/sif/v2 v2.7.0/go.mod h1:TiyBWsgWeh5yBeQFNuQnvROwswqK7YJT8JA1L53bsXQ=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@@ -1607,6 +1635,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1717,6 +1746,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -1804,6 +1835,8 @@ google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 h1:mmbq5q8M1t7dhkLw320YK4PsOXm6jdnUAkErImaIqOg=
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
+google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e h1:S9GbmC1iCgvbLyAokVCwiO6tVIrU9Y7c5oMx1V/ki/Y=
+google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1836,6 +1869,8 @@ google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
+google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
+google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1871,6 +1906,7 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544/go.mod h1:UhTeH/yXCK/KY7TX24mqPkaQ7gZeqmWd/8SSS8B3aHw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
diff --git a/hack/githubcreds.sh b/hack/githubcreds.sh
new file mode 100755
index 000000000..0fd96ab22
--- /dev/null
+++ b/hack/githubcreds.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+createAuth() {
+ if [ -n "$GITHUB_REPOSITORY_OWNER" -a -n "$GITHUB_TOKEN" ]; then
+ ocm_comprepo="ghcr.io/$GITHUB_REPOSITORY_OWNER/ocm"
+ ocm_comprepouser=$GITHUB_REPOSITORY_OWNER
+ ocm_comprepopassword="$GITHUB_TOKEN"
+ comprepourl="${ocm_comprepo#*//}"
+ repohost="${comprepourl%%/*}"
+ comprepourl="${ocm_comprepo%$comprepourl}${comprepourl%%/*}"
+ creds=(--cred :type=OCIRegistry --cred ":hostname=$repohost" --cred "username=$ocm_comprepouser" --cred "password=$ocm_comprepopassword")
+
+ echo "${creds[@]}"
+ fi
+}
+
+createAuth
diff --git a/pkg/cobrautils/logopts/options.go b/pkg/cobrautils/logopts/options.go
new file mode 100644
index 000000000..139d07bda
--- /dev/null
+++ b/pkg/cobrautils/logopts/options.go
@@ -0,0 +1,137 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package logopts
+
+import (
+ "strings"
+
+ "github.com/mandelsoft/logging"
+ "github.com/mandelsoft/logging/config"
+ "github.com/mandelsoft/logging/logrusr"
+ "github.com/mandelsoft/vfs/pkg/vfs"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/pflag"
+
+ "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/logforward"
+ "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm"
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+var Description = `
+The --log*
options can be used to configure the logging behaviour.
+There is a quick config option --log-keys
to configure simple
+tag/realm based condition rules. The comma-separated names build an AND rule.
+Hereby, names starting with a slash (/
) denote a realm (without the leading slash).
+A realm is a slash separated sequence of identifiers, which matches all logging realms
+with the given realms as path prefix. A tag directly matches the logging tags.
+Used tags and realms can be found under topic ocm
.
+The default level to enable is info
. Separated by an equal sign (=
)
+optiobally a dedicated level can be specified. Log levels can be (error
,
+warn
, info
, debug
and trace
.
+The default level is warn
.
+`
+
+type Options struct {
+ LogLevel string
+ LogFileName string
+ LogConfig string
+ LogKeys []string
+
+ LogFile vfs.File
+ LogForward *config.Config
+}
+
+func (o *Options) AddFlags(fs *pflag.FlagSet) {
+ fs.StringVarP(&o.LogLevel, "loglevel", "l", "", "set log level")
+ fs.StringVarP(&o.LogFileName, "logfile", "L", "", "set log file")
+ fs.StringVarP(&o.LogConfig, "logconfig", "", "", "log config")
+ fs.StringArrayVarP(&o.LogKeys, "logkeys", "", nil, "log tags/realms(.) to be enabled ([.]name{,[.]name}[=level])")
+}
+
+func (o *Options) Close() error {
+ if o.LogFile == nil {
+ return nil
+ }
+ return o.LogFile.Close()
+}
+
+func (o *Options) Configure(ctx ocm.Context, logctx logging.Context) error {
+ var err error
+
+ if logctx == nil {
+ // by default: always configure the root logging context used for the actual ocm context.
+ logctx = ctx.LoggingContext()
+ for logctx.Tree().GetBaseContext() != nil {
+ logctx = logctx.Tree().GetBaseContext()
+ }
+ }
+
+ if o.LogLevel != "" {
+ l, err := logging.ParseLevel(o.LogLevel)
+ if err != nil {
+ return errors.Wrapf(err, "invalid log level %q", o.LogLevel)
+ }
+ logctx.SetDefaultLevel(l)
+ } else {
+ logctx.SetDefaultLevel(logging.ErrorLevel)
+ }
+ logcfg := &config.Config{DefaultLevel: logging.LevelName(logctx.GetDefaultLevel())}
+
+ fs := vfsattr.Get(ctx)
+ if o.LogFileName != "" {
+ o.LogFile, err = fs.OpenFile(o.LogFileName, vfs.O_CREATE|vfs.O_WRONLY, 0o600)
+ if err != nil {
+ return errors.Wrapf(err, "cannot open log file %q", o.LogFile)
+ }
+ log := logrus.New()
+ log.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
+ log.SetOutput(o.LogFile)
+ logctx.SetBaseLogger(logrusr.New(log))
+ }
+
+ if o.LogConfig != "" {
+ cfg, err := vfs.ReadFile(fs, o.LogConfig)
+ if err != nil {
+ return errors.Wrapf(err, "cannot read logging config %q", o.LogFile)
+ }
+ if err = config.ConfigureWithData(logctx, cfg); err != nil {
+ return errors.Wrapf(err, "invalid logging config: %q", o.LogFile)
+ }
+ }
+
+ for _, t := range o.LogKeys {
+ level := logging.InfoLevel
+ i := strings.Index(t, "=")
+ if i >= 0 {
+ level, err = logging.ParseLevel(t[i+1:])
+ if err != nil {
+ return errors.Wrapf(err, "invalid log tag setting")
+ }
+ t = t[:i]
+ }
+ var cond []logging.Condition
+ var cfgcond []config.Condition
+
+ for _, tag := range strings.Split(t, ",") {
+ tag = strings.TrimSpace(tag)
+ if strings.HasPrefix(tag, "/") {
+ cond = append(cond, logging.NewRealmPrefix(tag[1:]))
+ cfgcond = append(cfgcond, config.RealmPrefix(tag[1:]))
+ } else {
+ cond = append(cond, logging.NewTag(tag))
+ cfgcond = append(cfgcond, config.Tag(tag))
+ }
+ }
+ rule := logging.NewConditionRule(level, cond...)
+ logcfg.Rules = append(logcfg.Rules, config.ConditionalRule(logging.LevelName(level), cfgcond...))
+ logctx.AddRule(rule)
+ }
+ o.LogForward = logcfg
+ logforward.Set(ctx.AttributesContext(), logcfg)
+
+ return nil
+}
diff --git a/pkg/cobrautils/logopts/options_test.go b/pkg/cobrautils/logopts/options_test.go
new file mode 100644
index 000000000..186f102f5
--- /dev/null
+++ b/pkg/cobrautils/logopts/options_test.go
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package logopts
+
+import (
+ "fmt"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ . "github.com/open-component-model/ocm/pkg/testutils"
+
+ "github.com/mandelsoft/logging"
+ "github.com/mandelsoft/logging/config"
+ "sigs.k8s.io/yaml"
+
+ "github.com/open-component-model/ocm/pkg/contexts/clictx"
+)
+
+var _ = Describe("log configuration", func() {
+ It("provides forward config", func() {
+ ctx := clictx.New()
+
+ opts := Options{
+ LogLevel: "debug",
+ LogKeys: []string{
+ "tag=trace",
+ "/realm=info",
+ },
+ }
+
+ MustBeSuccessful(opts.Configure(ctx.OCMContext(), ctx.LoggingContext()))
+ Expect(opts.LogForward).NotTo(BeNil())
+
+ data := Must(yaml.Marshal(opts.LogForward))
+ fmt.Printf("%s\n", string(data))
+ logctx := logging.NewWithBase(nil)
+ MustBeSuccessful(config.Configure(logctx, opts.LogForward))
+
+ Expect(logctx.GetDefaultLevel()).To(Equal(logging.DebugLevel))
+ Expect(logctx.Logger(logging.NewTag("tag")).Enabled(logging.TraceLevel)).To(BeTrue())
+ Expect(logctx.Logger(logging.NewRealm("realm/test")).Enabled(logging.InfoLevel)).To(BeTrue())
+ Expect(logctx.Logger(logging.NewRealm("realm/test")).Enabled(logging.DebugLevel)).To(BeFalse())
+ })
+})
diff --git a/pkg/cobrautils/logopts/suite_test.go b/pkg/cobrautils/logopts/suite_test.go
new file mode 100644
index 000000000..9b4c99c00
--- /dev/null
+++ b/pkg/cobrautils/logopts/suite_test.go
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package logopts
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Logging Options")
+}
diff --git a/pkg/contexts/credentials/config/type.go b/pkg/contexts/credentials/config/type.go
index 92e1329a1..da71c6153 100644
--- a/pkg/contexts/credentials/config/type.go
+++ b/pkg/contexts/credentials/config/type.go
@@ -27,7 +27,7 @@ func init() {
type Config struct {
runtime.ObjectVersionedType `json:",inline"`
// Consumers describe predefine logical cosumer specs mapped to credentials
- // These will (potentially) be evaluated if access objects requiring crednetials
+ // These will (potentially) be evaluated if access objects requiring credentials
// are provided by other modules (e.g. oci repo access) without
// specifying crednentials. Then this module can request credentials here by passing
// an appropriate consumer spec.
diff --git a/pkg/contexts/credentials/interface.go b/pkg/contexts/credentials/interface.go
index b58209494..ef2bb26b1 100644
--- a/pkg/contexts/credentials/interface.go
+++ b/pkg/contexts/credentials/interface.go
@@ -9,6 +9,7 @@ import (
"github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/credentials/internal"
+ "github.com/open-component-model/ocm/pkg/contexts/oci/identity"
"github.com/open-component-model/ocm/pkg/runtime"
)
@@ -106,3 +107,15 @@ var (
NoMatch = internal.NoMatch
PartialMatch = internal.PartialMatch
)
+
+func NewConsumerIdentity(typ string, attrs ...string) ConsumerIdentity {
+ r := map[string]string{}
+ r[identity.ID_TYPE] = typ
+
+ i := 0
+ for len(attrs) > i {
+ r[attrs[i]] = r[attrs[i+1]]
+ i += 2
+ }
+ return r
+}
diff --git a/pkg/contexts/credentials/repositories/dockerconfig/logging.go b/pkg/contexts/credentials/repositories/dockerconfig/logging.go
new file mode 100644
index 000000000..4f24fc88e
--- /dev/null
+++ b/pkg/contexts/credentials/repositories/dockerconfig/logging.go
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package dockerconfig
+
+import (
+ ocmlog "github.com/open-component-model/ocm/pkg/logging"
+)
+
+var REALM = ocmlog.DefineSubRealm("docker config handling as credential repository", "credentials/dockerconfig")
diff --git a/pkg/contexts/credentials/repositories/dockerconfig/repository.go b/pkg/contexts/credentials/repositories/dockerconfig/repository.go
index 15bddcfb7..d36bd12cb 100644
--- a/pkg/contexts/credentials/repositories/dockerconfig/repository.go
+++ b/pkg/contexts/credentials/repositories/dockerconfig/repository.go
@@ -94,7 +94,7 @@ func (r *Repository) Read(force bool) error {
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
}
- log := r.ctx.Logger()
+ log := r.ctx.Logger(REALM)
defaultStore := dockercred.DetectDefaultStore(cfg.CredentialsStore)
store := dockercred.NewNativeStore(cfg, defaultStore)
// get default native credential store
diff --git a/pkg/contexts/datacontext/attrs/init.go b/pkg/contexts/datacontext/attrs/init.go
index 45f3b7012..4c9dc6bf8 100644
--- a/pkg/contexts/datacontext/attrs/init.go
+++ b/pkg/contexts/datacontext/attrs/init.go
@@ -5,6 +5,7 @@
package attrs
import (
+ _ "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/logforward"
_ "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/tmpcache"
_ "github.com/open-component-model/ocm/pkg/contexts/datacontext/attrs/vfsattr"
)
diff --git a/pkg/contexts/datacontext/attrs/logforward/attr.go b/pkg/contexts/datacontext/attrs/logforward/attr.go
new file mode 100644
index 000000000..1732bd3c6
--- /dev/null
+++ b/pkg/contexts/datacontext/attrs/logforward/attr.go
@@ -0,0 +1,70 @@
+// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package logforward
+
+import (
+ "encoding/json"
+ "fmt"
+
+ logcfg "github.com/mandelsoft/logging/config"
+ "sigs.k8s.io/yaml"
+
+ "github.com/open-component-model/ocm/pkg/contexts/datacontext"
+ "github.com/open-component-model/ocm/pkg/runtime"
+)
+
+const (
+ ATTR_KEY = "github.com/mandelsoft/logforward"
+ ATTR_SHORT = "logfwd"
+)
+
+func init() {
+ datacontext.RegisterAttributeType(ATTR_KEY, AttributeType{})
+}
+
+type AttributeType struct{}
+
+func (a AttributeType) Name() string {
+ return ATTR_KEY
+}
+
+func (a AttributeType) Description() string {
+ return `
+*logconfig* Logging config structure used for config forwarding
+THis attribute is used to specify a logging configuration intended
+to be forwarded to other tool.
+(For example: TOI passes this config to the executor)
+`
+}
+
+func (a AttributeType) Encode(v interface{}, marshaller runtime.Marshaler) ([]byte, error) {
+ if _, ok := v.(*logcfg.Config); !ok {
+ return nil, fmt.Errorf("logging config required")
+ }
+ return json.Marshal(v)
+}
+
+func (a AttributeType) Decode(data []byte, unmarshaller runtime.Unmarshaler) (interface{}, error) {
+ var c logcfg.Config
+ err := yaml.Unmarshal(data, &c)
+ if err != nil {
+ return nil, err
+ }
+ return &c, nil
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+func Get(ctx datacontext.Context) *logcfg.Config {
+ v := ctx.GetAttributes().GetAttribute(ATTR_KEY)
+ if v == nil {
+ return nil
+ }
+ return v.(*logcfg.Config)
+}
+
+func Set(ctx datacontext.Context, c *logcfg.Config) {
+ ctx.GetAttributes().SetAttribute(ATTR_KEY, c)
+}
diff --git a/pkg/contexts/datacontext/config/logging/type.go b/pkg/contexts/datacontext/config/logging/type.go
index e7f8a69e2..984024053 100644
--- a/pkg/contexts/datacontext/config/logging/type.go
+++ b/pkg/contexts/datacontext/config/logging/type.go
@@ -5,14 +5,11 @@
package logging
import (
- "encoding/json"
-
"github.com/mandelsoft/logging"
logcfg "github.com/mandelsoft/logging/config"
"github.com/open-component-model/ocm/pkg/contexts/config/cpi"
"github.com/open-component-model/ocm/pkg/contexts/datacontext"
- "github.com/open-component-model/ocm/pkg/errors"
local "github.com/open-component-model/ocm/pkg/logging"
"github.com/open-component-model/ocm/pkg/runtime"
)
@@ -37,39 +34,35 @@ type Config struct {
ContextType string `json:"contextType,omitempty"`
Settings logcfg.Config `json:"settings"`
- // ExtraId is used to the context type "default" to be able
+ // ExtraId is used to the context type "default" or "global" to be able
// to reapply the same config again using a different
// identity given by the settings hash + the id.
ExtraId string `json:"extraId,omitempty"`
}
-// NewConfigSpec creates a new memory ConfigSpec.
+// New creates a logging config specification.
func New(ctxtype string, deflvl int) *Config {
return &Config{
ObjectVersionedType: runtime.NewVersionedObjectType(ConfigType),
ContextType: ctxtype,
Settings: logcfg.Config{
DefaultLevel: logging.LevelName(deflvl),
- Rules: []json.RawMessage{},
},
}
}
-func (c *Config) AddRuleSpec(spec interface{}) error {
- var err error
-
- data, ok := spec.([]byte)
- if !ok {
- data, err = json.Marshal(spec)
- if err != nil {
- errors.Wrapf(err, "invalid logging rule specification")
- }
- }
- _, err = logcfg.DefaultRegistry().CreateRule(data)
- if err != nil {
- return errors.Wrapf(err, "invalid logging rule specification")
+// NewWithConfig creates a logging config specification from a
+// logging config object.
+func NewWithConfig(ctxtype string, cfg *logcfg.Config) *Config {
+ return &Config{
+ ObjectVersionedType: runtime.NewVersionedObjectType(ConfigType),
+ ContextType: ctxtype,
+ Settings: *cfg,
}
- c.Settings.Rules = append(c.Settings.Rules, data)
+}
+
+func (c *Config) AddRuleSpec(r logcfg.Rule) error {
+ c.Settings.Rules = append(c.Settings.Rules, r)
return nil
}
@@ -90,7 +83,10 @@ func (c *Config) ApplyTo(ctx cpi.Context, target interface{}) error {
case "default":
return local.Configure(&c.Settings, c.ExtraId)
- // configure loogging context providers.
+ case "global":
+ return local.ConfigureGlobal(&c.Settings, c.ExtraId)
+
+ // configure logging context providers.
case "":
if _, ok := target.(datacontext.AttributesContext); !ok {
return cpi.ErrNoContext("attribute context")
diff --git a/pkg/contexts/datacontext/context.go b/pkg/contexts/datacontext/context.go
index 7070a790b..18b06fa7f 100644
--- a/pkg/contexts/datacontext/context.go
+++ b/pkg/contexts/datacontext/context.go
@@ -173,6 +173,7 @@ type delegates = Delegates
type contextBase struct {
ctxtype string
+ id int64
key interface{}
effective Context
attributes Attributes
@@ -227,6 +228,7 @@ func NewWithActions(parentAttrs Attributes, actions handlers.Registry) Attribute
c.Context = &contextBase{
ctxtype: CONTEXT_TYPE,
+ id: contextrange.NextId(),
key: key,
effective: c,
attributes: newAttributes(c, parentAttrs, &c.updater),
@@ -271,8 +273,26 @@ func (c *_context) Logger(messageContext ...logging.MessageContext) logging.Logg
////////////////////////////////////////////////////////////////////////////////
+// NumberRange can be used as source for successive id numbers to tag
+// elements, since debuggers not always sow object addresses.
+type NumberRange struct {
+ id int64
+ lock sync.Mutex
+}
+
+func (n *NumberRange) NextId() int64 {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+
+ n.id++
+ return n.id
+}
+
+var contextrange, attrsrange = NumberRange{}, NumberRange{}
+
type _attributes struct {
sync.RWMutex
+ id int64
ctx Context
parent Attributes
updater *Updater
@@ -287,6 +307,7 @@ func NewAttributes(ctx Context, parent Attributes, updater *Updater) Attributes
func newAttributes(ctx Context, parent Attributes, updater *Updater) *_attributes {
return &_attributes{
+ id: attrsrange.NextId(),
ctx: ctx,
parent: parent,
updater: updater,
diff --git a/pkg/contexts/oci/repositories/artifactset/utils_synthesis.go b/pkg/contexts/oci/repositories/artifactset/utils_synthesis.go
index 6d86484dc..ce92f9ef6 100644
--- a/pkg/contexts/oci/repositories/artifactset/utils_synthesis.go
+++ b/pkg/contexts/oci/repositories/artifactset/utils_synthesis.go
@@ -7,6 +7,8 @@ package artifactset
import (
"fmt"
+ . "github.com/open-component-model/ocm/pkg/finalizer"
+
"github.com/mandelsoft/vfs/pkg/osfs"
"github.com/mandelsoft/vfs/pkg/vfs"
"github.com/opencontainers/go-digest"
@@ -17,7 +19,6 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/oci/cpi"
"github.com/open-component-model/ocm/pkg/contexts/oci/transfer"
"github.com/open-component-model/ocm/pkg/errors"
- "github.com/open-component-model/ocm/pkg/utils"
)
const SynthesizedBlobFormat = "+tar+gzip"
@@ -105,9 +106,9 @@ type ArtifactIterator func() (ArtifactFactory, bool, error)
type ArtifactFeedback func(blob accessio.BlobAccess, art cpi.ArtifactAccess) error
// ArtifactTransferCreator provides an ArtifactFactory transferring the given artifact.
-func ArtifactTransferCreator(art cpi.ArtifactAccess, finalizer *utils.Finalizer, feedback ...ArtifactFeedback) ArtifactFactory {
+func ArtifactTransferCreator(art cpi.ArtifactAccess, finalizer *Finalizer, feedback ...ArtifactFeedback) ArtifactFactory {
return func(set *ArtifactSet) (digest.Digest, string, error) {
- var f utils.Finalizer
+ var f Finalizer
defer f.Finalize()
f.Include(finalizer)
diff --git a/pkg/contexts/oci/repositories/ocireg/logging.go b/pkg/contexts/oci/repositories/ocireg/logging.go
index 87f99e62a..70eae4512 100644
--- a/pkg/contexts/oci/repositories/ocireg/logging.go
+++ b/pkg/contexts/oci/repositories/ocireg/logging.go
@@ -5,7 +5,7 @@
package ocireg
import (
- "github.com/mandelsoft/logging"
+ ocmlog "github.com/open-component-model/ocm/pkg/logging"
)
-var REALM = logging.NewRealm("ocireg")
+var REALM = ocmlog.DefineSubRealm("OCI repository handling", "oci.ocireg")
diff --git a/pkg/contexts/oci/repositories/ocireg/repository.go b/pkg/contexts/oci/repositories/ocireg/repository.go
index 9d3de8203..6491fcd7a 100644
--- a/pkg/contexts/oci/repositories/ocireg/repository.go
+++ b/pkg/contexts/oci/repositories/ocireg/repository.go
@@ -11,6 +11,7 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/remotes/docker/config"
+ "github.com/mandelsoft/logging"
"github.com/open-component-model/ocm/pkg/contexts/credentials"
"github.com/open-component-model/ocm/pkg/contexts/oci/artdesc"
@@ -18,7 +19,7 @@ import (
"github.com/open-component-model/ocm/pkg/docker"
"github.com/open-component-model/ocm/pkg/docker/resolve"
"github.com/open-component-model/ocm/pkg/errors"
- "github.com/open-component-model/ocm/pkg/logging"
+ ocmlog "github.com/open-component-model/ocm/pkg/logging"
"github.com/open-component-model/ocm/pkg/utils"
)
@@ -43,18 +44,21 @@ func (r *RepositoryInfo) HostInfo() (string, string, string) {
}
type Repository struct {
- ctx cpi.Context
- spec *RepositorySpec
- info *RepositoryInfo
+ ctx cpi.Context
+ logger logging.UnboundLogger
+ spec *RepositorySpec
+ info *RepositoryInfo
}
var _ cpi.Repository = &Repository{}
func NewRepository(ctx cpi.Context, spec *RepositorySpec, info *RepositoryInfo) (*Repository, error) {
+ urs := spec.UniformRepositorySpec()
return &Repository{
- ctx: ctx,
- spec: spec,
- info: info,
+ ctx: ctx,
+ logger: logging.DynamicLogger(ctx, REALM, logging.NewAttribute(ocmlog.ATTR_HOST, urs.Host)),
+ spec: spec,
+ info: info,
}, nil
}
@@ -84,8 +88,9 @@ func (r *Repository) getResolver(comp string) (resolve.Resolver, error) {
return nil, err
}
}
+ logger := r.logger.BoundLogger().WithValues(ocmlog.ATTR_NAMESPACE, comp)
if creds == nil {
- logging.Logger(REALM).Trace("no credentials", "comp", comp, "base", r.spec.BaseURL)
+ logger.Trace("no credentials")
}
opts := docker.ResolverOptions{
@@ -100,10 +105,10 @@ func (r *Repository) getResolver(comp string) (resolve.Resolver, error) {
if pw != "" {
pw = "***"
}
- logging.Logger(REALM).Trace("query credentials", "host", host, "user", creds.GetProperty(credentials.ATTR_USERNAME), "pass", pw)
+ logger.Trace("query credentials", ocmlog.ATTR_USER, creds.GetProperty(credentials.ATTR_USERNAME), "pass", pw)
return creds.GetProperty(credentials.ATTR_USERNAME), p, nil
}
- logging.Logger(REALM).Trace("no credentials", "host", host)
+ logger.Trace("no credentials")
return "", "", nil
},
DefaultScheme: r.info.Scheme,
diff --git a/pkg/contexts/ocm/accessmethods/github/method.go b/pkg/contexts/ocm/accessmethods/github/method.go
index 8db9d022d..b56dddc4d 100644
--- a/pkg/contexts/ocm/accessmethods/github/method.go
+++ b/pkg/contexts/ocm/accessmethods/github/method.go
@@ -115,6 +115,10 @@ func (_ *AccessSpec) IsLocal(cpi.Context) bool {
return false
}
+func (a *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return a
+}
+
func (_ *AccessSpec) GetType() string {
return Type
}
diff --git a/pkg/contexts/ocm/accessmethods/localblob/method.go b/pkg/contexts/ocm/accessmethods/localblob/method.go
index 7fee5346f..0c346a588 100644
--- a/pkg/contexts/ocm/accessmethods/localblob/method.go
+++ b/pkg/contexts/ocm/accessmethods/localblob/method.go
@@ -80,6 +80,10 @@ func (a *AccessSpec) IsLocal(cpi.Context) bool {
return true
}
+func (a *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return a.GlobalAccess
+}
+
func (a *AccessSpec) GetMimeType() string {
return a.MediaType
}
diff --git a/pkg/contexts/ocm/accessmethods/localociblob/method.go b/pkg/contexts/ocm/accessmethods/localociblob/method.go
index 8c234fce1..7c1c0c985 100644
--- a/pkg/contexts/ocm/accessmethods/localociblob/method.go
+++ b/pkg/contexts/ocm/accessmethods/localociblob/method.go
@@ -52,6 +52,10 @@ func (s AccessSpec) IsLocal(context cpi.Context) bool {
return true
}
+func (a *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return nil
+}
+
func (s *AccessSpec) GetMimeType() string {
return ""
}
diff --git a/pkg/contexts/ocm/accessmethods/none/method.go b/pkg/contexts/ocm/accessmethods/none/method.go
index f6f075e10..3bdd71116 100644
--- a/pkg/contexts/ocm/accessmethods/none/method.go
+++ b/pkg/contexts/ocm/accessmethods/none/method.go
@@ -45,10 +45,14 @@ func (a *AccessSpec) Describe(ctx cpi.Context) string {
return "none"
}
-func (s AccessSpec) IsLocal(context cpi.Context) bool {
+func (s *AccessSpec) IsLocal(context cpi.Context) bool {
return false
}
+func (s *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return nil
+}
+
func (s *AccessSpec) GetMimeType() string {
return ""
}
diff --git a/pkg/contexts/ocm/accessmethods/npm/method.go b/pkg/contexts/ocm/accessmethods/npm/method.go
index d2aeb5bb2..f6f35516c 100644
--- a/pkg/contexts/ocm/accessmethods/npm/method.go
+++ b/pkg/contexts/ocm/accessmethods/npm/method.go
@@ -73,6 +73,10 @@ func (_ *AccessSpec) IsLocal(cpi.Context) bool {
return false
}
+func (a *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return a
+}
+
func (a *AccessSpec) GetReferenceHint(cv cpi.ComponentVersionAccess) string {
return a.Package + ":" + a.Version
}
diff --git a/pkg/contexts/ocm/accessmethods/ociartifact/logging.go b/pkg/contexts/ocm/accessmethods/ociartifact/logging.go
new file mode 100644
index 000000000..68b4b3471
--- /dev/null
+++ b/pkg/contexts/ocm/accessmethods/ociartifact/logging.go
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package ociartifact
+
+import (
+ "github.com/mandelsoft/logging"
+
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+ ocmlog "github.com/open-component-model/ocm/pkg/logging"
+)
+
+var REALM = ocmlog.DefineSubRealm("access method ociArtifact", "accessmethod/ociartifact")
+
+type ContextProvider interface {
+ GetContext() cpi.Context
+}
+
+func Logger(c ContextProvider, keyValuePairs ...interface{}) logging.Logger {
+ return c.GetContext().Logger(REALM).WithValues(keyValuePairs...)
+}
diff --git a/pkg/contexts/ocm/accessmethods/ociartifact/method.go b/pkg/contexts/ocm/accessmethods/ociartifact/method.go
index 6b7de1ebd..b9980d776 100644
--- a/pkg/contexts/ocm/accessmethods/ociartifact/method.go
+++ b/pkg/contexts/ocm/accessmethods/ociartifact/method.go
@@ -10,6 +10,8 @@ import (
"strings"
"sync"
+ . "github.com/open-component-model/ocm/pkg/finalizer"
+
"github.com/opencontainers/go-digest"
"github.com/open-component-model/ocm/pkg/common/accessio"
@@ -21,7 +23,6 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
"github.com/open-component-model/ocm/pkg/logging"
"github.com/open-component-model/ocm/pkg/runtime"
- "github.com/open-component-model/ocm/pkg/utils"
)
// Type is the access type of a oci registry.
@@ -76,6 +77,10 @@ func (_ *AccessSpec) IsLocal(cpi.Context) bool {
return false
}
+func (a *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return a
+}
+
func (a *AccessSpec) GetReferenceHint(cv cpi.ComponentVersionAccess) string {
ref, err := oci.ParseRef(a.ImageReference)
if err != nil {
@@ -111,7 +116,7 @@ type accessMethod struct {
comp cpi.ComponentVersionAccess
spec *AccessSpec
- finalizer utils.Finalizer
+ finalizer Finalizer
err error
art oci.ArtifactAccess
ref *oci.RefSpec
@@ -160,7 +165,7 @@ func (m *accessMethod) eval() (oci.Repository, *oci.RefSpec, error) {
return repo, &ref, err
}
-func (m *accessMethod) GetArtifact(finalizer *utils.Finalizer) (oci.ArtifactAccess, *oci.RefSpec, error) {
+func (m *accessMethod) GetArtifact(finalizer *Finalizer) (oci.ArtifactAccess, *oci.RefSpec, error) {
repo, ref, err := m.eval()
if err != nil {
return nil, nil, err
@@ -240,9 +245,10 @@ func (m *accessMethod) getBlob() (artifactset.ArtifactBlob, error) {
if err != nil {
return nil, err
}
- m.comp.GetContext().Logger().Info("synthesize artifact blob", "ref", m.spec.ImageReference)
+ logger := Logger(m.comp)
+ logger.Info("synthesize artifact blob", "ref", m.spec.ImageReference)
m.blob, err = artifactset.SynthesizeArtifactBlobForArtifact(art, ref.Version())
- m.comp.GetContext().Logger().Info("synthesize artifact blob done", "ref", m.spec.ImageReference, "error", logging.ErrorMessage(err))
+ logger.Info("synthesize artifact blob done", "ref", m.spec.ImageReference, "error", logging.ErrorMessage(err))
if err != nil {
return nil, err
}
diff --git a/pkg/contexts/ocm/accessmethods/ociblob/method.go b/pkg/contexts/ocm/accessmethods/ociblob/method.go
index 8565cd76d..fcba686c9 100644
--- a/pkg/contexts/ocm/accessmethods/ociblob/method.go
+++ b/pkg/contexts/ocm/accessmethods/ociblob/method.go
@@ -64,10 +64,14 @@ func (a *AccessSpec) Describe(ctx cpi.Context) string {
return fmt.Sprintf("OCI blob %s in repository %s", a.Digest, a.Reference)
}
-func (s AccessSpec) IsLocal(context cpi.Context) bool {
+func (s *AccessSpec) IsLocal(context cpi.Context) bool {
return false
}
+func (s *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return s
+}
+
func (s *AccessSpec) GetMimeType() string {
return s.MediaType
}
diff --git a/pkg/contexts/ocm/accessmethods/plugin/method.go b/pkg/contexts/ocm/accessmethods/plugin/method.go
index 62c9b7672..1a60809ff 100644
--- a/pkg/contexts/ocm/accessmethods/plugin/method.go
+++ b/pkg/contexts/ocm/accessmethods/plugin/method.go
@@ -41,6 +41,10 @@ func (_ *AccessSpec) IsLocal(cpi.Context) bool {
return false
}
+func (s *AccessSpec) GlobalAccessSpec(cpi.Context) cpi.AccessSpec {
+ return s
+}
+
func (s *AccessSpec) GetMimeType() string {
return s.handler.GetMimeType(s)
}
diff --git a/pkg/contexts/ocm/accessmethods/s3/method.go b/pkg/contexts/ocm/accessmethods/s3/method.go
index a1e94cc11..30ac324a0 100644
--- a/pkg/contexts/ocm/accessmethods/s3/method.go
+++ b/pkg/contexts/ocm/accessmethods/s3/method.go
@@ -81,6 +81,10 @@ func (_ *AccessSpec) IsLocal(cpi.Context) bool {
return false
}
+func (a *AccessSpec) GlobalAccessSpec(ctx cpi.Context) cpi.AccessSpec {
+ return a
+}
+
func (_ *AccessSpec) GetType() string {
return Type
}
diff --git a/pkg/contexts/ocm/blobhandler/oci/ocirepo/blobhandler.go b/pkg/contexts/ocm/blobhandler/oci/ocirepo/blobhandler.go
index 65e2ac45d..158fa743f 100644
--- a/pkg/contexts/ocm/blobhandler/oci/ocirepo/blobhandler.go
+++ b/pkg/contexts/ocm/blobhandler/oci/ocirepo/blobhandler.go
@@ -9,6 +9,8 @@ import (
"path"
"strings"
+ . "github.com/open-component-model/ocm/pkg/finalizer"
+
"github.com/opencontainers/go-digest"
"github.com/open-component-model/ocm/pkg/common/accessio"
@@ -27,7 +29,6 @@ import (
storagecontext "github.com/open-component-model/ocm/pkg/contexts/ocm/blobhandler/oci"
"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/utils"
)
func init() {
@@ -138,7 +139,7 @@ func (b *artifactHandler) StoreBlob(blob cpi.BlobAccess, artType, hint string, g
var art oci.ArtifactAccess
var err error
- var finalizer utils.Finalizer
+ var finalizer Finalizer
defer finalizer.Finalize()
keep := keepblobattr.Get(ctx.GetContext())
diff --git a/pkg/contexts/ocm/compdesc/meta/v1/resourceref.go b/pkg/contexts/ocm/compdesc/meta/v1/resourceref.go
index 6445e9c4f..f0c049471 100644
--- a/pkg/contexts/ocm/compdesc/meta/v1/resourceref.go
+++ b/pkg/contexts/ocm/compdesc/meta/v1/resourceref.go
@@ -19,7 +19,7 @@ func NewNestedResourceRef(id Identity, path []Identity) ResourceReference {
return ResourceReference{Resource: id, ReferencePath: path}
}
-func (r *ResourceReference) String() string {
+func (r ResourceReference) String() string {
s := r.Resource.String()
for i := 1; i <= len(r.ReferencePath); i++ {
diff --git a/pkg/contexts/ocm/compdesc/versions/v2/validation_test.go b/pkg/contexts/ocm/compdesc/versions/v2/validation_test.go
index 772399302..66955e5a6 100644
--- a/pkg/contexts/ocm/compdesc/versions/v2/validation_test.go
+++ b/pkg/contexts/ocm/compdesc/versions/v2/validation_test.go
@@ -10,7 +10,6 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
-
. "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/versions/v2"
"k8s.io/apimachinery/pkg/util/validation/field"
diff --git a/pkg/contexts/ocm/cpi/interface.go b/pkg/contexts/ocm/cpi/interface.go
index 6fa1511ac..ec0f5607e 100644
--- a/pkg/contexts/ocm/cpi/interface.go
+++ b/pkg/contexts/ocm/cpi/interface.go
@@ -18,7 +18,7 @@ const CONTEXT_TYPE = internal.CONTEXT_TYPE
const CommonTransportFormat = internal.CommonTransportFormat
-var TAG_BLOBHANDLER = logging.NewTag("blobhandler")
+var TAG_BLOBHANDLER = logging.DefineTag("blobhandler", "execution of blob handler used to upload resource blobs to an ocm repository.")
func BlobHandlerLogger(ctx Context, messageContext ...logging.MessageContext) logging.Logger {
if len(messageContext) > 0 {
diff --git a/pkg/contexts/ocm/cpi/support/compversaccess.go b/pkg/contexts/ocm/cpi/support/compversaccess.go
index 15cbbdf0e..ea7f8c5e6 100644
--- a/pkg/contexts/ocm/cpi/support/compversaccess.go
+++ b/pkg/contexts/ocm/cpi/support/compversaccess.go
@@ -348,7 +348,7 @@ func (c *componentVersionAccessImpl) SetSource(meta *cpi.SourceMeta, acc compdes
// AddResource adds a blob resource to the current archive.
func (c *componentVersionAccessImpl) SetResourceBlob(meta *cpi.ResourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec) error {
- c.GetContext().Logger().Info("adding resource blob", "resource", meta.Name)
+ Logger(c).Info("adding resource blob", "resource", meta.Name)
acc, err := c.AddBlob(blob, meta.Type, refName, global)
if err != nil {
return fmt.Errorf("unable to add blob: %w", err)
@@ -362,7 +362,7 @@ func (c *componentVersionAccessImpl) SetResourceBlob(meta *cpi.ResourceMeta, blo
}
func (c *componentVersionAccessImpl) SetSourceBlob(meta *cpi.SourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec) error {
- c.GetContext().Logger().Info("adding source blob", "source", meta.Name)
+ Logger(c).Info("adding source blob", "source", meta.Name)
acc, err := c.AddBlob(blob, meta.Type, refName, global)
if err != nil {
return fmt.Errorf("unable to add blob: %w", err)
diff --git a/pkg/contexts/ocm/cpi/support/logging.go b/pkg/contexts/ocm/cpi/support/logging.go
new file mode 100644
index 000000000..4c79675d0
--- /dev/null
+++ b/pkg/contexts/ocm/cpi/support/logging.go
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package support
+
+import (
+ "github.com/mandelsoft/logging"
+
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/cpi"
+)
+
+type ContextProvider interface {
+ GetContext() cpi.Context
+}
+
+func Logger(c ContextProvider, keyValuePairs ...interface{}) logging.Logger {
+ return c.GetContext().Logger().WithValues(keyValuePairs...)
+}
diff --git a/pkg/contexts/ocm/internal/accessspecref.go b/pkg/contexts/ocm/internal/accessspecref.go
index efc2c620d..76ac96b65 100644
--- a/pkg/contexts/ocm/internal/accessspecref.go
+++ b/pkg/contexts/ocm/internal/accessspecref.go
@@ -95,6 +95,14 @@ func (a *AccessSpecRef) IsLocal(ctx Context) bool {
return false
}
+func (a *AccessSpecRef) GlobalAccessSpec(ctx Context) AccessSpec {
+ a.assure(ctx)
+ if a.evaluated != nil {
+ return a.evaluated.GlobalAccessSpec(ctx)
+ }
+ return nil
+}
+
func (a *AccessSpecRef) AccessMethod(access ComponentVersionAccess) (AccessMethod, error) {
if err := a.assure(access.GetContext()); err != nil {
return nil, err
diff --git a/pkg/contexts/ocm/internal/accesstypes.go b/pkg/contexts/ocm/internal/accesstypes.go
index c6ece96c5..18087a067 100644
--- a/pkg/contexts/ocm/internal/accesstypes.go
+++ b/pkg/contexts/ocm/internal/accesstypes.go
@@ -39,6 +39,7 @@ type AccessSpec interface {
compdesc.AccessSpec
Describe(Context) string
IsLocal(Context) bool
+ GlobalAccessSpec(Context) AccessSpec
AccessMethod(access ComponentVersionAccess) (AccessMethod, error)
}
@@ -197,6 +198,10 @@ func (_ *UnknownAccessSpec) IsLocal(Context) bool {
return false
}
+func (_ *UnknownAccessSpec) GlobalAccessSpec(Context) AccessSpec {
+ return nil
+}
+
var _ AccessSpec = &UnknownAccessSpec{}
////////////////////////////////////////////////////////////////////////////////
@@ -257,4 +262,15 @@ func (s *GenericAccessSpec) IsLocal(ctx Context) bool {
return spec.IsLocal(ctx)
}
+func (s *GenericAccessSpec) GlobalAccessSpec(ctx Context) AccessSpec {
+ spec, err := s.Evaluate(ctx)
+ if err != nil {
+ return nil
+ }
+ if _, ok := spec.(*GenericAccessSpec); ok {
+ return nil
+ }
+ return spec.GlobalAccessSpec(ctx)
+}
+
var _ AccessSpec = &GenericAccessSpec{}
diff --git a/pkg/contexts/ocm/internal/context.go b/pkg/contexts/ocm/internal/context.go
index 19baa5421..67d0c114c 100644
--- a/pkg/contexts/ocm/internal/context.go
+++ b/pkg/contexts/ocm/internal/context.go
@@ -9,6 +9,8 @@ import (
"reflect"
"strings"
+ . "github.com/open-component-model/ocm/pkg/finalizer"
+
"github.com/open-component-model/ocm/pkg/contexts/config"
cfgcpi "github.com/open-component-model/ocm/pkg/contexts/config/cpi"
"github.com/open-component-model/ocm/pkg/contexts/credentials"
@@ -17,7 +19,6 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/oci/repositories/ctf"
"github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc"
"github.com/open-component-model/ocm/pkg/runtime"
- "github.com/open-component-model/ocm/pkg/utils"
)
const CONTEXT_TYPE = "ocm" + datacontext.OCM_CONTEXT_SUFFIX
@@ -61,10 +62,10 @@ type Context interface {
// elements should be added to the finalzer, which can be reopened/created
// on-the fly whenever required.
Finalize() error
- Finalizer() *utils.Finalizer
+ Finalizer() *Finalizer
}
-////////////////////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////////////////
var key = reflect.TypeOf(_context{})
@@ -86,7 +87,7 @@ func DefinedForContext(ctx context.Context) (Context, bool) {
return nil, ok
}
-////////////////////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////////////////
type _context struct {
datacontext.Context
@@ -103,7 +104,7 @@ type _context struct {
blobHandlers BlobHandlerRegistry
blobDigesters BlobDigesterRegistry
aliases map[string]RepositorySpec
- finalizer utils.Finalizer
+ finalizer Finalizer
}
var _ Context = &_context{}
@@ -137,7 +138,7 @@ func (c *_context) Finalize() error {
return c.finalizer.Finalize()
}
-func (c *_context) Finalizer() *utils.Finalizer {
+func (c *_context) Finalizer() *Finalizer {
return &c.finalizer
}
diff --git a/pkg/contexts/ocm/plugin/descriptor/const.go b/pkg/contexts/ocm/plugin/descriptor/const.go
index 4acdeff95..3ead1874e 100644
--- a/pkg/contexts/ocm/plugin/descriptor/const.go
+++ b/pkg/contexts/ocm/plugin/descriptor/const.go
@@ -5,10 +5,9 @@
package descriptor
import (
- "github.com/mandelsoft/logging"
-
"github.com/open-component-model/ocm/pkg/contexts/datacontext/action"
"github.com/open-component-model/ocm/pkg/errors"
+ ocmlog "github.com/open-component-model/ocm/pkg/logging"
)
const (
@@ -19,4 +18,4 @@ const (
KIND_ACTION = action.KIND_ACTION
)
-var TAG = logging.NewTag("plugins")
+var REALM = ocmlog.DefineSubRealm("OCM plugin handling", "plugins")
diff --git a/pkg/contexts/ocm/plugin/interface.go b/pkg/contexts/ocm/plugin/interface.go
index e1f661f94..1253cbdd0 100644
--- a/pkg/contexts/ocm/plugin/interface.go
+++ b/pkg/contexts/ocm/plugin/interface.go
@@ -16,7 +16,7 @@ const (
KIND_ACTION = descriptor.KIND_ACTION
)
-var TAG = descriptor.TAG
+var TAG = descriptor.REALM
type (
Descriptor = descriptor.Descriptor
diff --git a/pkg/contexts/ocm/plugin/plugins/plugins.go b/pkg/contexts/ocm/plugin/plugins/plugins.go
index ca72670e4..99c7a1ffc 100644
--- a/pkg/contexts/ocm/plugin/plugins/plugins.go
+++ b/pkg/contexts/ocm/plugin/plugins/plugins.go
@@ -53,7 +53,7 @@ func (pi *pluginsImpl) GetContext() cpi.Context {
func (pi *pluginsImpl) Update() {
err := pi.updater.Update()
if err != nil {
- pi.ctx.Logger(descriptor.TAG).Error("config update failed", "error", err.Error())
+ pi.ctx.Logger(descriptor.REALM).Error("config update failed", "error", err.Error())
}
}
diff --git a/pkg/contexts/ocm/plugin/ppi/interface.go b/pkg/contexts/ocm/plugin/ppi/interface.go
index 97f2816ff..43bb7eb9a 100644
--- a/pkg/contexts/ocm/plugin/ppi/interface.go
+++ b/pkg/contexts/ocm/plugin/ppi/interface.go
@@ -30,7 +30,7 @@ type (
UploadTargetSpecInfo = internal.UploadTargetSpecInfo
)
-var TAG = descriptor.TAG
+var REALM = descriptor.REALM
type Plugin interface {
Name() string
diff --git a/pkg/contexts/ocm/registration/logging.go b/pkg/contexts/ocm/registration/logging.go
new file mode 100644
index 000000000..0ecf52baf
--- /dev/null
+++ b/pkg/contexts/ocm/registration/logging.go
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package registration
+
+import (
+ "github.com/mandelsoft/logging"
+
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/plugin/descriptor"
+)
+
+func Logger(c logging.ContextProvider, keyValuePairs ...interface{}) logging.Logger {
+ return c.LoggingContext().Logger(descriptor.REALM).WithValues(keyValuePairs...)
+}
diff --git a/pkg/contexts/ocm/registration/registration.go b/pkg/contexts/ocm/registration/registration.go
index 420652d0f..2eb1d9d0a 100644
--- a/pkg/contexts/ocm/registration/registration.go
+++ b/pkg/contexts/ocm/registration/registration.go
@@ -5,8 +5,6 @@
package registration
import (
- "github.com/mandelsoft/logging"
-
"github.com/open-component-model/ocm/pkg/contexts/datacontext/action"
"github.com/open-component-model/ocm/pkg/contexts/datacontext/action/handlers"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
@@ -18,13 +16,11 @@ import (
"github.com/open-component-model/ocm/pkg/runtime"
)
-var TAG = logging.NewTag("plugins")
-
-// RegisterExtensions registers all the extension provided by the found plugin
-// at the given context. If no context is given, the cache context is used.
+// RegisterExtensions registers all the extension provided by the found plugin.
func RegisterExtensions(ctx ocm.Context) error {
pi := plugincacheattr.Get(ctx)
+ logger := Logger(ctx)
for _, n := range pi.PluginNames() {
p := pi.Get(n)
if !p.IsValid() {
@@ -33,12 +29,12 @@ func RegisterExtensions(ctx ocm.Context) error {
for _, a := range p.GetDescriptor().Actions {
h, err := plugin2.New(p, a.Name)
if err != nil {
- ctx.Logger(TAG).Error("cannot create action handler for plugin", "plugin", p.Name(), "handler", a.Name)
+ logger.Error("cannot create action handler for plugin", "plugin", p.Name(), "handler", a.Name)
} else {
for _, s := range a.DefaultSelectors {
err := ctx.AttributesContext().GetActions().Register(h, handlers.ForAction(a.Name), action.Selector(s))
if err != nil {
- ctx.Logger(TAG).LogError(err, "cannot register action handler for plugin", "plugin", p.Name(), "handler", a.Name, "selector", s)
+ logger.LogError(err, "cannot register action handler for plugin", "plugin", p.Name(), "handler", a.Name, "selector", s)
}
}
}
@@ -48,7 +44,7 @@ func RegisterExtensions(ctx ocm.Context) error {
if m.Version != "" {
name = name + runtime.VersionSeparator + m.Version
}
- ctx.Logger(TAG).Info("registering access method",
+ logger.Info("registering access method",
"plugin", p.Name(),
"type", name)
pi.GetContext().AccessMethods().Register(name, access.NewType(name, p, &m))
@@ -59,9 +55,9 @@ func RegisterExtensions(ctx ocm.Context) error {
if c.ContextType != "" && c.RepositoryType != "" && c.MediaType != "" {
hdlr, err := plugin.New(p, u.Name, nil)
if err != nil {
- ctx.Logger(TAG).Error("cannot create blob handler for plugin", "plugin", p.Name(), "handler", u.Name)
+ logger.Error("cannot create blob handler fpr plugin", "plugin", p.Name(), "handler", u.Name)
} else {
- ctx.Logger(TAG).Info("registering repository blob handler",
+ logger.Info("registering repository blob handler",
"context", c.ContextType+":"+c.RepositoryType,
"plugin", p.Name(),
"handler", u.Name)
diff --git a/pkg/contexts/ocm/repositories/ctf/repo_test.go b/pkg/contexts/ocm/repositories/ctf/repo_test.go
index c36db0f11..4e2d1a018 100644
--- a/pkg/contexts/ocm/repositories/ctf/repo_test.go
+++ b/pkg/contexts/ocm/repositories/ctf/repo_test.go
@@ -7,6 +7,7 @@ package ctf_test
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
+ . "github.com/open-component-model/ocm/pkg/finalizer"
. "github.com/open-component-model/ocm/pkg/testutils"
"github.com/mandelsoft/vfs/pkg/memoryfs"
@@ -16,7 +17,6 @@ import (
"github.com/open-component-model/ocm/pkg/common/accessobj"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
"github.com/open-component-model/ocm/pkg/contexts/ocm/repositories/ctf"
- "github.com/open-component-model/ocm/pkg/utils"
)
const COMPONENT = "github.com/mandelsoft/ocm"
@@ -31,7 +31,7 @@ var _ = Describe("access method", func() {
})
It("adds component version", func() {
- final := utils.Finalizer{}
+ final := Finalizer{}
defer Defer(final.Finalize)
a := Must(ctf.Create(ctx, accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, "ctf", 0o700, accessio.PathFileSystem(fs)))
@@ -53,7 +53,7 @@ var _ = Describe("access method", func() {
})
It("adds omits unadded new component version", func() {
- final := utils.Finalizer{}
+ final := Finalizer{}
defer Defer(final.Finalize)
a := Must(ctf.Create(ctx, accessobj.ACC_WRITABLE|accessobj.ACC_CREATE, "ctf", 0o700, accessio.PathFileSystem(fs)))
diff --git a/pkg/contexts/ocm/resourcetypes/const.go b/pkg/contexts/ocm/resourcetypes/const.go
index 7605912ff..cbb37d757 100644
--- a/pkg/contexts/ocm/resourcetypes/const.go
+++ b/pkg/contexts/ocm/resourcetypes/const.go
@@ -16,11 +16,21 @@ const (
// BLOB describes any anonymous untyped blob data.
BLOB = "blob"
// FILESYSTEM describes a directory structure stored as archive (tar, tgz).
- FILESYSTEM = "filesystem"
+ FILESYSTEM = "directoryTree"
+ FILESYSTEM_LEGACY = "filesystem"
// EXECUTABLE describes an OS executable.
EXECUTABLE = "executable"
// PLAIN_TEXT describes plain text.
PLAIN_TEXT = "plainText"
// OCM_PLUGIN describes an OS executable OCM plugin.
OCM_PLUGIN = "ocmPlugin"
+
+ // OCM_FILE describes a generic file or unspecified byte stream.
+ OCM_FILE = "file"
+ // OCM_YAML describes a YAML file.
+ OCM_YAML = "yaml"
+ // OCM_JSON describes a JSON file.
+ OCM_JSON = "json"
+ // OCM_XML describes a XML file.
+ OCM_XML = "xml"
)
diff --git a/pkg/contexts/ocm/transfer/utils.go b/pkg/contexts/ocm/transfer/logging.go
similarity index 63%
rename from pkg/contexts/ocm/transfer/utils.go
rename to pkg/contexts/ocm/transfer/logging.go
index 25ebf6550..0b88e1795 100644
--- a/pkg/contexts/ocm/transfer/utils.go
+++ b/pkg/contexts/ocm/transfer/logging.go
@@ -8,18 +8,15 @@ import (
"github.com/mandelsoft/logging"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
+ ocmlog "github.com/open-component-model/ocm/pkg/logging"
)
-var TAG = logging.NewTag("transfer")
+var REALM = ocmlog.DefineSubRealm("OCM transfer handling", "transfer")
type ContextProvider interface {
GetContext() ocm.Context
}
-func LogInfo(c ContextProvider, msg string, keyValuePairs ...interface{}) {
- c.GetContext().Logger(TAG).Info(msg, keyValuePairs...)
-}
-
func Logger(c ContextProvider, keyValuePairs ...interface{}) logging.Logger {
- return c.GetContext().Logger(TAG).WithValues(keyValuePairs...)
+ return c.GetContext().Logger(REALM).WithValues(keyValuePairs...)
}
diff --git a/pkg/contexts/ocm/transfer/transfer.go b/pkg/contexts/ocm/transfer/transfer.go
index 1fe13d927..b07d1ab6a 100644
--- a/pkg/contexts/ocm/transfer/transfer.go
+++ b/pkg/contexts/ocm/transfer/transfer.go
@@ -86,7 +86,7 @@ func transferVersion(printer common.Printer, log logging.Logger, state WalkingSt
list := errors.ErrListf("component references for %s", nv)
log.Info(" transferring references")
for _, r := range d.References {
- cv, shdlr, err := handler.TransferVersion(src.Repository(), src, &r)
+ cv, shdlr, err := handler.TransferVersion(src.Repository(), src, &r, tgt)
if err != nil {
return errors.Wrapf(err, "%s: nested component %s[%s:%s]", state.History, r.GetName(), r.ComponentName, r.GetVersion())
}
diff --git a/pkg/contexts/ocm/transfer/transferhandler/spiff/README.md b/pkg/contexts/ocm/transfer/transferhandler/spiff/README.md
index 89776a018..acc1db6b0 100644
--- a/pkg/contexts/ocm/transfer/transferhandler/spiff/README.md
+++ b/pkg/contexts/ocm/transfer/transferhandler/spiff/README.md
@@ -25,6 +25,7 @@ following bindings:
- `labels` *<map[string]>*: labels of the component version (deep)
- `element` *<map>*: the meta data of the resource and the field `type` containing the element type.
- `access` *<map>*: the access specification of the resource
+ - `target` *<map>*: the respository specification of the target resource
The result value (field `process`) must be a boolean describing whether the
resource should be transported ny-value.
diff --git a/pkg/contexts/ocm/transfer/transferhandler/spiff/handler.go b/pkg/contexts/ocm/transfer/transferhandler/spiff/handler.go
index 9d229bab9..b32c4d7a9 100644
--- a/pkg/contexts/ocm/transfer/transferhandler/spiff/handler.go
+++ b/pkg/contexts/ocm/transfer/transferhandler/spiff/handler.go
@@ -49,12 +49,12 @@ func (h *Handler) OverwriteVersion(src ocm.ComponentVersionAccess, tgt ocm.Compo
return h.Handler.OverwriteVersion(src, tgt)
}
-func (h *Handler) TransferVersion(repo ocm.Repository, src ocm.ComponentVersionAccess, meta *compdesc.ComponentReference) (ocm.ComponentVersionAccess, transferhandler.TransferHandler, error) {
+func (h *Handler) TransferVersion(repo ocm.Repository, src ocm.ComponentVersionAccess, meta *compdesc.ComponentReference, tgt ocm.Repository) (ocm.ComponentVersionAccess, transferhandler.TransferHandler, error) {
if src == nil || h.opts.IsRecursive() {
if h.opts.GetScript() == nil {
- return h.Handler.TransferVersion(repo, src, meta)
+ return h.Handler.TransferVersion(repo, src, meta, tgt)
}
- binding := h.getBinding(src, nil, &meta.ElementMeta, nil)
+ binding := h.getBinding(src, nil, &meta.ElementMeta, nil, tgt)
result, r, s, err := h.EvalRecursion("componentversion", binding, "process")
if err != nil {
return nil, nil, err
@@ -67,11 +67,11 @@ func (h *Handler) TransferVersion(repo ocm.Repository, src ocm.ComponentVersionA
}
}
if s == nil {
- return h.Handler.TransferVersion(repo, src, meta)
+ return h.Handler.TransferVersion(repo, src, meta, tgt)
}
opts := *h.opts
opts.script = s
- cv, _, err := h.Handler.TransferVersion(repo, src, meta)
+ cv, _, err := h.Handler.TransferVersion(repo, src, meta, tgt)
return cv, &Handler{
Handler: h.Handler,
opts: &opts,
@@ -88,7 +88,7 @@ func (h *Handler) TransferResource(src ocm.ComponentVersionAccess, a ocm.AccessS
if h.opts.GetScript() == nil {
return true, nil
}
- binding := h.getBinding(src, a, &r.Meta().ElementMeta, &r.Meta().Type)
+ binding := h.getBinding(src, a, &r.Meta().ElementMeta, &r.Meta().Type, nil)
return h.EvalBool("resource", binding, "process")
}
@@ -99,11 +99,11 @@ func (h *Handler) TransferSource(src ocm.ComponentVersionAccess, a ocm.AccessSpe
if h.opts.GetScript() == nil {
return true, nil
}
- binding := h.getBinding(src, a, &r.Meta().ElementMeta, &r.Meta().Type)
+ binding := h.getBinding(src, a, &r.Meta().ElementMeta, &r.Meta().Type, nil)
return h.EvalBool("source", binding, "process")
}
-func (h *Handler) getBinding(src ocm.ComponentVersionAccess, a ocm.AccessSpec, m *compdesc.ElementMeta, typ *string) map[string]interface{} {
+func (h *Handler) getBinding(src ocm.ComponentVersionAccess, a ocm.AccessSpec, m *compdesc.ElementMeta, typ *string, tgt ocm.Repository) map[string]interface{} {
binding := map[string]interface{}{}
if src != nil {
binding["component"] = getCVAttrs(src)
@@ -116,6 +116,9 @@ func (h *Handler) getBinding(src ocm.ComponentVersionAccess, a ocm.AccessSpec, m
if typ != nil {
binding["element"].(map[string]interface{})["type"] = *typ
}
+ if tgt != nil {
+ binding["target"] = getData(tgt.GetSpecification())
+ }
return binding
}
diff --git a/pkg/contexts/ocm/transfer/transferhandler/standard/handler.go b/pkg/contexts/ocm/transfer/transferhandler/standard/handler.go
index 1a534154f..215bca67d 100644
--- a/pkg/contexts/ocm/transfer/transferhandler/standard/handler.go
+++ b/pkg/contexts/ocm/transfer/transferhandler/standard/handler.go
@@ -9,6 +9,7 @@ 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/transfer/transferhandler"
+ "github.com/open-component-model/ocm/pkg/errors"
)
type Handler struct {
@@ -35,8 +36,13 @@ func (h *Handler) OverwriteVersion(src ocm.ComponentVersionAccess, tgt ocm.Compo
return h.opts.IsOverwrite(), nil
}
-func (h *Handler) TransferVersion(repo ocm.Repository, src ocm.ComponentVersionAccess, meta *compdesc.ComponentReference) (ocm.ComponentVersionAccess, transferhandler.TransferHandler, error) {
+func (h *Handler) TransferVersion(repo ocm.Repository, src ocm.ComponentVersionAccess, meta *compdesc.ComponentReference, tgt ocm.Repository) (ocm.ComponentVersionAccess, transferhandler.TransferHandler, error) {
if src == nil || h.opts.IsRecursive() {
+ if h.opts.IsStopOnExistingVersion() && tgt != nil {
+ if found, err := tgt.ExistsComponentVersion(meta.ComponentName, meta.Version); found || err != nil {
+ return nil, nil, errors.Wrapf(err, "failed looking up in target")
+ }
+ }
compoundResolver := ocm.NewCompoundResolver(repo, h.opts.GetResolver())
cv, err := compoundResolver.LookupComponentVersion(meta.GetComponentName(), meta.Version)
return cv, h, err
@@ -55,11 +61,19 @@ func (h *Handler) TransferSource(src ocm.ComponentVersionAccess, a ocm.AccessSpe
func (h *Handler) HandleTransferResource(r ocm.ResourceAccess, m ocm.AccessMethod, hint string, t ocm.ComponentVersionAccess) error {
blob := accessio.BlobAccessForDataAccess("", -1, m.MimeType(), m)
defer blob.Close()
- return t.SetResourceBlob(r.Meta(), blob, hint, nil)
+ return t.SetResourceBlob(r.Meta(), blob, hint, h.GlobalAccess(t.GetContext(), m))
}
func (h *Handler) HandleTransferSource(r ocm.SourceAccess, m ocm.AccessMethod, hint string, t ocm.ComponentVersionAccess) error {
blob := accessio.BlobAccessForDataAccess("", -1, m.MimeType(), m)
defer blob.Close()
- return t.SetSourceBlob(r.Meta(), blob, hint, nil)
+
+ return t.SetSourceBlob(r.Meta(), blob, hint, h.GlobalAccess(t.GetContext(), m))
+}
+
+func (h *Handler) GlobalAccess(ctx ocm.Context, m ocm.AccessMethod) ocm.AccessSpec {
+ if h.opts.IsKeepGlobalAccess() {
+ return m.AccessSpec().GlobalAccessSpec(ctx)
+ }
+ return nil
}
diff --git a/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go b/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go
index 28a89c784..fb6855947 100644
--- a/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go
+++ b/pkg/contexts/ocm/transfer/transferhandler/standard/handler_test.go
@@ -28,6 +28,7 @@ import (
"github.com/open-component-model/ocm/pkg/contexts/ocm/resourcetypes"
ocmsign "github.com/open-component-model/ocm/pkg/contexts/ocm/signing"
"github.com/open-component-model/ocm/pkg/contexts/ocm/transfer"
+ "github.com/open-component-model/ocm/pkg/contexts/ocm/transfer/transferhandler"
"github.com/open-component-model/ocm/pkg/contexts/ocm/transfer/transferhandler/standard"
"github.com/open-component-model/ocm/pkg/mime"
"github.com/open-component-model/ocm/pkg/signing"
@@ -91,7 +92,7 @@ var _ = Describe("Transfer handler", func() {
env.Cleanup()
})
- It("it should copy a resource by value to a ctf file", func() {
+ DescribeTable("it should copy a resource by value to a ctf file", func(acc string, topts ...transferhandler.TransferOption) {
src, err := ctf.Open(env.OCMContext(), accessobj.ACC_READONLY, ARCH, 0, env)
Expect(err).To(Succeed())
cv, err := src.LookupComponentVersion(COMPONENT, VERSION)
@@ -101,6 +102,7 @@ var _ = Describe("Transfer handler", func() {
defer tgt.Close()
opts := &standard.Options{}
opts.SetResourcesByValue(true)
+ transferhandler.ApplyOptions(opts, topts...)
handler := standard.NewDefaultHandler(opts)
//handler, err := standard.New(standard.ResourcesByValue())
Expect(err).To(Succeed())
@@ -119,7 +121,7 @@ var _ = Describe("Transfer handler", func() {
fmt.Printf("%s\n", string(data))
hash := HashManifest1(artifactset.DefaultArtifactSetDescriptorFileName)
- Expect(string(data)).To(StringEqualWithContext("{\"localReference\":\"" + hash + "\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"" + OCINAMESPACE + ":" + OCIVERSION + "\",\"type\":\"localBlob\"}"))
+ Expect(string(data)).To(StringEqualWithContext(fmt.Sprintf(acc, hash)))
r, err := comp.GetResourceByIndex(1)
Expect(err).To(Succeed())
@@ -138,7 +140,13 @@ var _ = Describe("Transfer handler", func() {
data, err = blob.Get()
Expect(err).To(Succeed())
Expect(string(data)).To(Equal("manifestlayer"))
- })
+ },
+ Entry("without preserve global",
+ "{\"localReference\":\"%s\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\""+OCINAMESPACE+":"+OCIVERSION+"\",\"type\":\"localBlob\"}"),
+ Entry("with preserve global",
+ "{\"globalAccess\":{\"imageReference\":\"alias.alias/ocm/value:v2.0\",\"type\":\"ociArtifact\"},\"localReference\":\"%s\",\"mediaType\":\"application/vnd.oci.image.manifest.v1+tar+gzip\",\"referenceName\":\"ocm/value:v2.0\",\"type\":\"localBlob\"}",
+ standard.KeepGlobalAccess()),
+ )
It("it should use additional resolver to resolve component ref", func() {
parentSrc, err := ctf.Open(env.OCMContext(), accessobj.ACC_READONLY, ARCH2, 0, env)
diff --git a/pkg/contexts/ocm/transfer/transferhandler/standard/options.go b/pkg/contexts/ocm/transfer/transferhandler/standard/options.go
index aef520626..ac7254f98 100644
--- a/pkg/contexts/ocm/transfer/transferhandler/standard/options.go
+++ b/pkg/contexts/ocm/transfer/transferhandler/standard/options.go
@@ -15,6 +15,8 @@ type Options struct {
recursive bool
resourcesByValue bool
sourcesByValue bool
+ keepGlobalAccess bool
+ stopOnExisting bool
overwrite bool
resolver ocm.ComponentVersionResolver
}
@@ -24,48 +26,65 @@ var (
_ SourcesByValueOption = (*Options)(nil)
_ RecursiveOption = (*Options)(nil)
_ ResolverOption = (*Options)(nil)
+ _ KeepGlobalAccessOption = (*Options)(nil)
)
func (o *Options) SetOverwrite(overwrite bool) {
o.overwrite = overwrite
}
+func (o *Options) IsOverwrite() bool {
+ return o.overwrite
+}
+
func (o *Options) SetRecursive(recursive bool) {
o.recursive = recursive
}
+func (o *Options) IsRecursive() bool {
+ return o.recursive
+}
+
func (o *Options) SetResourcesByValue(resourcesByValue bool) {
o.resourcesByValue = resourcesByValue
}
-func (o *Options) SetSourcesByValue(sourcesByValue bool) {
- o.sourcesByValue = sourcesByValue
+func (o *Options) IsResourcesByValue() bool {
+ return o.resourcesByValue
}
-func (o *Options) SetResolver(resolver ocm.ComponentVersionResolver) {
- o.resolver = resolver
+func (o *Options) SetSourcesByValue(sourcesByValue bool) {
+ o.sourcesByValue = sourcesByValue
}
-func (o *Options) IsOverwrite() bool {
- return o.overwrite
+func (o *Options) IsSourcesByValue() bool {
+ return o.sourcesByValue
}
-func (o *Options) IsRecursive() bool {
- return o.recursive
+func (o *Options) SetKeepGlobalAccess(keepGlobalAccess bool) {
+ o.keepGlobalAccess = keepGlobalAccess
}
-func (o *Options) IsResourcesByValue() bool {
- return o.resourcesByValue
+func (o *Options) IsKeepGlobalAccess() bool {
+ return o.keepGlobalAccess
}
-func (o *Options) IsSourcesByValue() bool {
- return o.sourcesByValue
+func (o *Options) SetResolver(resolver ocm.ComponentVersionResolver) {
+ o.resolver = resolver
}
func (o *Options) GetResolver() ocm.ComponentVersionResolver {
return o.resolver
}
+func (o *Options) SetStopOnExistingVersion(stopOnExistingVersion bool) {
+ o.stopOnExisting = stopOnExistingVersion
+}
+
+func (o *Options) IsStopOnExistingVersion() bool {
+ return o.stopOnExisting
+}
+
///////////////////////////////////////////////////////////////////////////////
type OverwriteOption interface {
@@ -195,3 +214,55 @@ func Resolver(resolver ocm.ComponentVersionResolver) transferhandler.TransferOpt
resolver: resolver,
}
}
+
+///////////////////////////////////////////////////////////////////////////////
+
+type KeepGlobalAccessOption interface {
+ SetKeepGlobalAccess(bool)
+ IsKeepGlobalAccess() bool
+}
+
+type keepGlobalOption struct {
+ flag bool
+}
+
+func (o *keepGlobalOption) ApplyTransferOption(to transferhandler.TransferOptions) error {
+ if eff, ok := to.(KeepGlobalAccessOption); ok {
+ eff.SetKeepGlobalAccess(o.flag)
+ return nil
+ } else {
+ return errors.ErrNotSupported("resolver")
+ }
+}
+
+func KeepGlobalAccess(args ...bool) transferhandler.TransferOption {
+ return &keepGlobalOption{
+ flag: utils.GetOptionFlag(args...),
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+type StopOnExistingVersionOption interface {
+ SetStopOnExistingVersion(bool)
+ IsStopOnExistingVersion() bool
+}
+
+type stopOnExistingVersionOption struct {
+ flag bool
+}
+
+func (o *stopOnExistingVersionOption) ApplyTransferOption(to transferhandler.TransferOptions) error {
+ if eff, ok := to.(StopOnExistingVersionOption); ok {
+ eff.SetStopOnExistingVersion(o.flag)
+ return nil
+ } else {
+ return errors.ErrNotSupported("sources by-value")
+ }
+}
+
+func StopOnExistingVersion(args ...bool) transferhandler.TransferOption {
+ return &stopOnExistingVersionOption{
+ flag: utils.GetOptionFlag(args...),
+ }
+}
diff --git a/pkg/contexts/ocm/transfer/transferhandler/transferhandler.go b/pkg/contexts/ocm/transfer/transferhandler/transferhandler.go
index 61cc53e20..cf2b0b0c9 100644
--- a/pkg/contexts/ocm/transfer/transferhandler/transferhandler.go
+++ b/pkg/contexts/ocm/transfer/transferhandler/transferhandler.go
@@ -19,7 +19,7 @@ type TransferOption interface {
type TransferHandler interface {
OverwriteVersion(src ocm.ComponentVersionAccess, tgt ocm.ComponentVersionAccess) (bool, error)
- TransferVersion(repo ocm.Repository, src ocm.ComponentVersionAccess, meta *compdesc.ComponentReference) (ocm.ComponentVersionAccess, TransferHandler, error)
+ TransferVersion(repo ocm.Repository, src ocm.ComponentVersionAccess, meta *compdesc.ComponentReference, tgt ocm.Repository) (ocm.ComponentVersionAccess, TransferHandler, error)
TransferResource(src ocm.ComponentVersionAccess, a ocm.AccessSpec, r ocm.ResourceAccess) (bool, error)
TransferSource(src ocm.ComponentVersionAccess, a ocm.AccessSpec, r ocm.SourceAccess) (bool, error)
diff --git a/pkg/contexts/ocm/transfer/transferrepo.go b/pkg/contexts/ocm/transfer/transferrepo.go
index a7ace500b..1f6d6bc5a 100644
--- a/pkg/contexts/ocm/transfer/transferrepo.go
+++ b/pkg/contexts/ocm/transfer/transferrepo.go
@@ -50,7 +50,7 @@ func transferVersions(printer common.Printer, closure TransportClosure, list *er
if list.Addf(subp, err, "list versions for %s", c) == nil {
for _, v := range vers {
ref := compdesc.NewComponentReference("", c, v, nil)
- sub, h, err := handler.TransferVersion(repo, nil, ref)
+ sub, h, err := handler.TransferVersion(repo, nil, ref, tgt)
if list.Addf(subp, err, "version %s", v) != nil {
continue
}
diff --git a/pkg/contexts/ocm/utils/resourceref.go b/pkg/contexts/ocm/utils/resourceref.go
index 0ef70cc92..b0c45014c 100644
--- a/pkg/contexts/ocm/utils/resourceref.go
+++ b/pkg/contexts/ocm/utils/resourceref.go
@@ -7,11 +7,12 @@ package utils
import (
"fmt"
+ . "github.com/open-component-model/ocm/pkg/finalizer"
+
"github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/ocm"
metav1 "github.com/open-component-model/ocm/pkg/contexts/ocm/compdesc/meta/v1"
"github.com/open-component-model/ocm/pkg/errors"
- "github.com/open-component-model/ocm/pkg/utils"
)
func ResolveReferencePath(cv ocm.ComponentVersionAccess, path []metav1.Identity, resolver ocm.ComponentVersionResolver) (ocm.ComponentVersionAccess, error) {
@@ -23,7 +24,7 @@ func ResolveReferencePath(cv ocm.ComponentVersionAccess, path []metav1.Identity,
return nil, errors.Wrapf(err, "component version already closed")
}
- var final utils.Finalizer
+ var final Finalizer
defer final.Finalize()
for _, cr := range path {
diff --git a/pkg/errors/error.go b/pkg/errors/error.go
index 29d8da59c..b3cf2d598 100644
--- a/pkg/errors/error.go
+++ b/pkg/errors/error.go
@@ -92,6 +92,20 @@ type wrappedError struct {
msg string
}
+// NewEf provides an arror with an optional cause.
+func NewEf(cause error, msg string, args ...interface{}) error {
+ if cause == nil {
+ return Newf(msg, args...)
+ }
+ if len(args) > 0 {
+ msg = fmt.Sprintf(msg, args...)
+ }
+ return &wrappedError{
+ wrapped: cause,
+ msg: msg,
+ }
+}
+
func Wrapf(err error, msg string, args ...interface{}) error {
if err == nil {
return nil
@@ -105,6 +119,17 @@ func Wrapf(err error, msg string, args ...interface{}) error {
}
}
+func Wrap(err error, args ...interface{}) error {
+ if err == nil || len(args) == 0 {
+ return err
+ }
+ msg := fmt.Sprint(args...)
+ return &wrappedError{
+ wrapped: err,
+ msg: msg,
+ }
+}
+
func (e *wrappedError) Error() string {
return fmt.Sprintf("%s: %s", e.msg, e.wrapped)
}
diff --git a/pkg/exception/exception.go b/pkg/exception/exception.go
new file mode 100644
index 000000000..6509adb77
--- /dev/null
+++ b/pkg/exception/exception.go
@@ -0,0 +1,324 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// Package exception provides a simple exception mechanism
+// to reduce boilerplate for trivial error forwarding in
+// a function.
+// Example:
+//
+// func f0() error {
+// return nil
+// }
+//
+// func f1() (int, error) {
+// return 1, nil
+// }
+//
+// func MyFunc() (err error) {
+// defer PropagateException(&err)
+//
+// Mustf(f0(),"f0 failed")
+// i:=Must1f(R1(f1()), "f1 failed")
+// fmt.Printf("got %d\n", i)
+// return nil
+// }
+package exception
+
+import (
+ "github.com/open-component-model/ocm/pkg/errors"
+)
+
+type Matcher func(err error) bool
+
+// PropagateException catches an exception provided by variations of
+// MustX functions and forwards the error to its argument.
+// It must be called by defer.
+// Cannot reuse PropagateExceptionf, because recover MUST be called
+// at TOP level defer function to recover the panic.
+func PropagateException(errp *error, matchers ...Matcher) {
+ if r := recover(); r != nil {
+ if e, ok := r.(*exception); ok && match(e.err, matchers...) {
+ *errp = e.err
+ } else {
+ panic(r)
+ }
+ }
+}
+
+func match(err error, matchers ...Matcher) bool {
+ if len(matchers) == 0 {
+ return true
+ }
+ for _, m := range matchers {
+ if m(err) {
+ return true
+ }
+ }
+ return false
+}
+
+var All = func(_ error) bool {
+ return true
+}
+
+var None = func(_ error) bool {
+ return false
+}
+
+func ByPrototypes(protos ...error) Matcher {
+ return func(err error) bool {
+ if len(protos) == 0 {
+ return true
+ }
+ for _, p := range protos {
+ if errors.IsA(err, p) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func Or(matchers ...Matcher) Matcher {
+ return func(err error) bool {
+ for _, m := range matchers {
+ if m(err) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func And(matchers ...Matcher) Matcher {
+ return func(err error) bool {
+ for _, m := range matchers {
+ if !m(err) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+// Exception provie the error object from an exception object.
+func Exception(r interface{}) error {
+ if e, ok := r.(*exception); ok {
+ return e.err
+ }
+ return nil
+}
+
+// PropagateExceptionf catches an exception provided by variations of
+// MustX functions and forwards the error to its argument.
+// It must be called by defer.
+// If an error context is given (msg!=""), it is added to the propagated error.
+func PropagateExceptionf(errp *error, msg string, args ...interface{}) {
+ if r := recover(); r != nil {
+ if e, ok := r.(*exception); ok {
+ if msg != "" {
+ *errp = errors.Wrapf(e.err, msg, args...)
+ } else {
+ *errp = e.err
+ }
+ } else {
+ panic(r)
+ }
+ }
+}
+
+func PropagateMatchedExceptionf(errp *error, m Matcher, msg string, args ...interface{}) {
+ if r := recover(); r != nil {
+ if e, ok := r.(*exception); ok && (m == nil || m(e.err)) {
+ if msg != "" {
+ *errp = errors.Wrapf(e.err, msg, args...)
+ } else {
+ *errp = e.err
+ }
+ } else {
+ panic(r)
+ }
+ }
+}
+
+// ForwardExceptionf add an error context to a forwarded exception.
+// Usage: defer ForwardExceptionf("error context").
+func ForwardExceptionf(msg string, args ...interface{}) {
+ if r := recover(); r != nil {
+ if e, ok := r.(*exception); ok {
+ if msg != "" {
+ e.err = errors.Wrapf(e.err, msg, args...)
+ }
+ panic(e)
+ } else {
+ panic(r)
+ }
+ }
+}
+
+type exception struct {
+ err error
+}
+
+func (e *exception) Unwrap() error {
+ return e.err
+}
+
+func (e *exception) Error() string {
+ return e.err.Error()
+}
+
+// Throw throws an exception if err !=nil.
+func Throw(err error) {
+ if err == nil {
+ return
+ }
+ panic(&exception{err})
+}
+
+// Throwf throws an exception if err !=nil.
+// A given error context is given, it wraps the
+// error by the context.
+func Throwf(err error, msg string, args ...interface{}) {
+ if err == nil {
+ return
+ }
+ if msg == "" {
+ panic(&exception{err})
+ }
+ panic(&exception{errors.Wrapf(err, msg, args...)})
+}
+
+// Must converts an error (e.g. provided by a nested function call) into an
+// exception. It provides no regular result.
+func Must(err error) {
+ Throw(err)
+}
+
+// Must converts an error (e.g. provided by a nested function call) into an
+// exception. It provides no regular result.
+// The given error context is used to wrap the error.
+func Mustf(err error, msg string, args ...interface{}) {
+ Throwf(err, msg, args...)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type result1[A any] struct {
+ r1 A
+ err error
+}
+
+// R1 bundles the result of an error function with one additional
+// argument to be passed to Must1f.
+func R1[A any](r1 A, err error) result1[A] { return result1[A]{r1, err} }
+
+// Must1 converts an error into an exception. It provides one regular
+// result.
+//
+// Usage: Must1(ErrorFunctionWithOneRegularResult()).
+func Must1[A any](r1 A, err error) A {
+ Throw(err)
+ return r1
+}
+
+// Must1f converts an error into an exception. It provides one regular
+// result, which has to be provided by method R1.
+// Optionally an error context can be given.
+//
+// Usage: Must1f(R1(ErrorFunctionWithOneRegularResult()), "context").
+//
+// The intermediate function R1 is required , because GO does not
+// allow to compose arguments provided by a function with multiple
+// return values with additional arguments.
+func Must1f[A any](r result1[A], msg string, args ...interface{}) A {
+ Throwf(r.err, msg, args...)
+ return r.r1
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type result2[A, B any] struct {
+ r1 A
+ r2 B
+ err error
+}
+
+// R2 bundles the result of an error function with two additional
+// arguments to be passed to Must1.
+func R2[A, B any](r1 A, r2 B, err error) result2[A, B] { return result2[A, B]{r1, r2, err} }
+
+// Must2 converts an error into an exception. It provides two regular
+// results.
+func Must2[A, B any](r1 A, r2 B, err error) (A, B) {
+ Throw(err)
+ return r1, r2
+}
+
+// Must2f like Must1f, but for two regular
+// results, which has to be provided by method R2.
+// The error is wrapped by the given error context.
+func Must2f[A, B any](r result2[A, B], msg string, args ...interface{}) (A, B) {
+ Throwf(r.err, msg, args...)
+ return r.r1, r.r2
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type result3[A, B, C any] struct {
+ r1 A
+ r2 B
+ r3 C
+ err error
+}
+
+// R3 bundles the result of an error function with three additional
+// arguments to be passed to Must3.
+func R3[A, B, C any](r1 A, r2 B, r3 C, err error) result3[A, B, C] {
+ return result3[A, B, C]{r1, r2, r3, err}
+}
+
+// Must3 converts an error into an exception. It provides three regular
+// results.
+func Must3[A, B, C any](r1 A, r2 B, r3 C, err error) (A, B, C) {
+ Throw(err)
+ return r1, r2, r3
+}
+
+// Must3f like Must1f, but for three regular
+// results, which has to be provided by method R3.
+func Must3f[A, B, C any](r result3[A, B, C], msg string, args ...interface{}) (A, B, C) {
+ Throwf(r.err, msg, args...)
+ return r.r1, r.r2, r.r3
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+type result4[A, B, C, D any] struct {
+ r1 A
+ r2 B
+ r3 C
+ r4 D
+ err error
+}
+
+// R4 bundles the result of an error function with four additional
+// arguments to be passed to Must4.
+func R4[A, B, C, D any](r1 A, r2 B, r3 C, r4 D, err error) result4[A, B, C, D] {
+ return result4[A, B, C, D]{r1, r2, r3, r4, err}
+}
+
+// Must4 converts an error into an exception. It provides four regular
+// results.
+func Must4[A, B, C, D any](r1 A, r2 B, r3 C, r4 D, err error) (A, B, C, D) {
+ Throw(err)
+ return r1, r2, r3, r4
+}
+
+// Must4f like Must1f, but for four regular
+// results, which has to be provided by method R4.
+func Must4f[A, B, C, D any](r result4[A, B, C, D], msg string, args ...interface{}) (A, B, C, D) {
+ Throwf(r.err, msg, args...)
+ return r.r1, r.r2, r.r3, r.r4
+}
diff --git a/pkg/exception/exception_test.go b/pkg/exception/exception_test.go
new file mode 100644
index 000000000..8de551471
--- /dev/null
+++ b/pkg/exception/exception_test.go
@@ -0,0 +1,119 @@
+// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package exception_test
+
+import (
+ "fmt"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/exception"
+)
+
+var dump = fmt.Errorf("dump")
+
+func callee(e error) (int, error) {
+ if e == dump {
+ a := 0
+ _ = 1 / a
+ }
+ return 0, e
+}
+
+func _caller(e error, args ...interface{}) {
+ if len(args) == 0 {
+ exception.Must1(callee(e))
+ } else {
+ exception.Must1f(exception.R1(callee(e)), args[0].(string), args[1:]...)
+ }
+}
+
+func caller(e error, args ...interface{}) (err error) {
+ defer exception.PropagateException(&err)
+ _caller(e, args...)
+ return nil
+}
+
+var _ = Describe("exceptions", func() {
+ It("succeeds", func() {
+ Expect(caller(nil)).To(BeNil())
+ })
+
+ It("propagates", func() {
+ err := fmt.Errorf("test error")
+ Expect(caller(err)).To(Equal(err))
+ })
+
+ It("dumps", func() {
+ defer func() {
+ r := recover()
+ Expect(r).NotTo(BeNil())
+ Expect(fmt.Sprintf("%s", r)).To(Equal("runtime error: integer divide by zero"))
+ }()
+ caller(dump)
+ })
+
+ It("propagates with context", func() {
+ err := fmt.Errorf("test error")
+
+ prop := caller(err, "test")
+ Expect(prop).NotTo(BeNil())
+ Expect(errors.Unwrap(prop)).To(Equal(err))
+ Expect(prop.Error()).To(Equal("test: test error"))
+ })
+
+ It("propagates with outer context", func() {
+ caller := func(e error, args ...interface{}) (err error) {
+ defer exception.PropagateExceptionf(&err, "outer")
+ _caller(e, args...)
+ return nil
+ }
+
+ err := fmt.Errorf("test error")
+
+ prop := caller(err)
+ Expect(prop).NotTo(BeNil())
+ Expect(errors.Unwrap(prop)).To(Equal(err))
+ Expect(prop.Error()).To(Equal("outer: test error"))
+
+ prop = caller(err, "test")
+ Expect(prop).NotTo(BeNil())
+ Expect(errors.Unwrap(errors.Unwrap(prop))).To(Equal(err))
+ Expect(prop.Error()).To(Equal("outer: test: test error"))
+ })
+
+ Context("with matchers", func() {
+
+ caller := func(e error, args ...interface{}) (err error) {
+ defer exception.PropagateException(&err, exception.ByPrototypes(MyException{}))
+ _caller(e, args...)
+ return nil
+ }
+
+ It("catches matched exception", func() {
+ err := caller(MyException{})
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("MyException"))
+ })
+
+ It("passes unmatched exception", func() {
+ defer func() {
+ r := recover()
+ Expect(r).NotTo(BeNil())
+ Expect(fmt.Sprintf("%s", r)).To(Equal("test"))
+ }()
+ err := caller(fmt.Errorf("test"))
+ Expect(err).To(Succeed())
+ })
+ })
+})
+
+type MyException struct{}
+
+func (_ MyException) Error() string {
+ return "MyException"
+}
diff --git a/pkg/exception/suite_test.go b/pkg/exception/suite_test.go
new file mode 100644
index 000000000..18232f1c9
--- /dev/null
+++ b/pkg/exception/suite_test.go
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package exception_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "oci Test Suite")
+}
diff --git a/pkg/utils/finalizer.go b/pkg/finalizer/finalizer.go
similarity index 59%
rename from pkg/utils/finalizer.go
rename to pkg/finalizer/finalizer.go
index 0fa7ddbd6..26ff15fa1 100644
--- a/pkg/utils/finalizer.go
+++ b/pkg/finalizer/finalizer.go
@@ -2,13 +2,14 @@
//
// SPDX-License-Identifier: Apache-2.0
-package utils
+package finalizer
import (
"io"
"sync"
"github.com/open-component-model/ocm/pkg/errors"
+ "github.com/open-component-model/ocm/pkg/exception"
)
// Finalizer gathers finalization functions and calls
@@ -22,10 +23,22 @@ import (
// a loop block.
type Finalizer struct {
lock sync.Mutex
+ catch exception.Matcher
pending []func() error
nested *Finalizer
}
+// CatchException marks the finalizer to catch exceptions.
+// This must be combined with error propagating defers.
+func (f *Finalizer) CatchException(matchers ...exception.Matcher) *Finalizer {
+ if len(matchers) > 0 {
+ f.catch = exception.Or(matchers...)
+ } else {
+ f.catch = exception.All
+ }
+ return f
+}
+
// Lock locks a given Locker and unlocks it again
// during finalization.
func (f *Finalizer) Lock(locker sync.Locker) *Finalizer {
@@ -36,7 +49,7 @@ func (f *Finalizer) Lock(locker sync.Locker) *Finalizer {
// WithVoid registers a simple function to be
// called on finalization.
func (f *Finalizer) WithVoid(fi func()) *Finalizer {
- return f.With(func() error { fi(); return nil })
+ return f.With(CallingV(fi))
}
func (f *Finalizer) With(fi func() error) *Finalizer {
@@ -49,6 +62,56 @@ func (f *Finalizer) With(fi func() error) *Finalizer {
return f
}
+// Calling1 can be used with Finalizer.With, to call an error providing
+// function with one argument.
+func Calling1[T any](f func(arg T) error, arg T) func() error {
+ return func() error {
+ return f(arg)
+ }
+}
+
+func Calling2[T, U any](f func(arg1 T, arg2 U) error, arg1 T, arg2 U) func() error {
+ return func() error {
+ return f(arg1, arg2)
+ }
+}
+
+func Calling3[T, U, V any](f func(arg1 T, arg2 U, arg3 V) error, arg1 T, arg2 U, arg3 V) func() error {
+ return func() error {
+ return f(arg1, arg2, arg3)
+ }
+}
+
+func CallingV(f func()) func() error {
+ return func() error {
+ f()
+ return nil
+ }
+}
+
+// Calling1V can be used with Finalizer.With, to call a void
+// function with one argument.
+func Calling1V[T any](f func(arg T), arg T) func() error {
+ return func() error {
+ f(arg)
+ return nil
+ }
+}
+
+func Calling2V[T, U any](f func(arg1 T, arg2 U), arg1 T, arg2 U) func() error {
+ return func() error {
+ f(arg1, arg2)
+ return nil
+ }
+}
+
+func Calling3V[T, U, V any](f func(arg1 T, arg2 U, arg3 V), arg1 T, arg2 U, arg3 V) func() error {
+ return func() error {
+ f(arg1, arg2, arg3)
+ return nil
+ }
+}
+
// Close will finalize the given object by calling
// its Close function when the finalizer is finalized.
func (f *Finalizer) Close(c io.Closer) *Finalizer {
@@ -103,7 +166,20 @@ func (f *Finalizer) Length() int {
// This is especially intended to be used in a deferred mode to adapt
// the error code of a function to incorporate finalization errors.
func (f *Finalizer) FinalizeWithErrorPropagation(efferr *error) {
- errors.PropagateError(efferr, f.Finalize)
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ errors.PropagateError(efferr, f.finalize)
+ if f.catch == nil {
+ return
+ }
+ if r := recover(); r != nil {
+ if e := exception.Exception(r); e != nil {
+ *efferr = errors.ErrList().Add(e).Add(*efferr).Result()
+ } else {
+ panic(r)
+ }
+ }
}
// FinalizeWithErrorPropagationf calls all finalizations in the reverse order of
@@ -113,15 +189,43 @@ func (f *Finalizer) FinalizeWithErrorPropagation(efferr *error) {
// the error code of a function to incorporate finalization errors.
// The final error will be wrapped by the given common context.
func (f *Finalizer) FinalizeWithErrorPropagationf(efferr *error, msg string, args ...interface{}) {
- errors.PropagateErrorf(efferr, f.Finalize, msg, args...)
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ errors.PropagateErrorf(efferr, f.finalize, msg, args...)
+ if f.catch == nil {
+ return
+ }
+ if r := recover(); r != nil {
+ if e := exception.Exception(r); e != nil {
+ *efferr = errors.ErrList().Add(e).Add(*efferr).Result()
+ } else {
+ panic(r)
+ }
+ }
}
// Finalize calls all finalizations in the reverse order of
-// their registration.
-func (f *Finalizer) Finalize() error {
+// their registration and incorporates catched exceptions.
+func (f *Finalizer) Finalize() (err error) {
f.lock.Lock()
defer f.lock.Unlock()
+ err = f.finalize()
+ if f.catch == nil {
+ return err
+ }
+ if r := recover(); r != nil {
+ if e := exception.Exception(r); e != nil {
+ err = errors.ErrList().Add(e).Add(err).Result()
+ } else {
+ panic(r)
+ }
+ }
+ return err
+}
+
+func (f *Finalizer) finalize() (err error) {
list := errors.ErrList()
l := len(f.pending)
for i := range f.pending {
diff --git a/pkg/utils/finalizer_test.go b/pkg/finalizer/finalizer_test.go
similarity index 76%
rename from pkg/utils/finalizer_test.go
rename to pkg/finalizer/finalizer_test.go
index dc174a2d8..36053f92b 100644
--- a/pkg/utils/finalizer_test.go
+++ b/pkg/finalizer/finalizer_test.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: Apache-2.0
-package utils_test
+package finalizer_test
import (
"fmt"
@@ -10,25 +10,44 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
- "github.com/open-component-model/ocm/pkg/utils"
+ "github.com/open-component-model/ocm/pkg/exception"
+ "github.com/open-component-model/ocm/pkg/finalizer"
)
type Order []string
func F(n string, order *Order) func() error {
return func() error {
- *order = append(*order, n)
- return nil
+ return R(n, order)
}
}
+func R(n string, order *Order) error {
+ *order = append(*order, n)
+ return nil
+}
+
var _ = Describe("finalizer", func() {
- It("finalize in revered order", func() {
- var finalize utils.Finalizer
+ It("finalize with arbitrary method", func() {
+ var finalize finalizer.Finalizer
+ var order Order
+
+ finalize.With(finalizer.Calling2(R, "A", &order))
+ Expect(order).To(BeNil())
+
+ finalize.Finalize()
+
+ Expect(order).To(Equal(Order{"A"}))
+ Expect(finalize.Length()).To(Equal(0))
+ })
+
+ It("finalize in reversed order", func() {
+ var finalize finalizer.Finalizer
var order Order
finalize.With(F("A", &order))
finalize.With(F("B", &order))
+ Expect(order).To(BeNil())
finalize.Finalize()
@@ -37,7 +56,7 @@ var _ = Describe("finalizer", func() {
})
It("is reusable after calling Finalize", func() {
- var finalize utils.Finalizer
+ var finalize finalizer.Finalizer
var order Order
finalize.With(F("A", &order))
@@ -56,7 +75,7 @@ var _ = Describe("finalizer", func() {
})
It("separately finalizes a Nested finalizer", func() {
- var finalize utils.Finalizer
+ var finalize finalizer.Finalizer
var order Order
finalize.With(F("A", &order))
@@ -87,7 +106,7 @@ var _ = Describe("finalizer", func() {
})
It("separately finalizes new finalizers", func() {
- var finalize utils.Finalizer
+ var finalize finalizer.Finalizer
var order Order
finalize.With(F("A", &order))
@@ -164,6 +183,25 @@ var _ = Describe("finalizer", func() {
})
})
})
+
+ Context("with exceptions", func() {
+ callee := func() {
+ exception.Throw(fmt.Errorf("exception"))
+ }
+ caller := func() (err error) {
+ var finalize finalizer.Finalizer
+
+ defer finalize.CatchException().FinalizeWithErrorPropagation(&err)
+ callee()
+ return nil
+ }
+
+ It("catches exception from exception package", func() {
+ err := caller()
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("exception"))
+ })
+ })
})
func errfunc(succeed bool) func() error {
@@ -174,7 +212,7 @@ func errfunc(succeed bool) func() error {
}
func testFunc(msg string, err error, succeed bool) (efferr error) {
- var finalize utils.Finalizer
+ var finalize finalizer.Finalizer
defer finalize.FinalizeWithErrorPropagationf(&efferr, msg)
finalize.With(errfunc(succeed))
diff --git a/pkg/finalizer/suite_test.go b/pkg/finalizer/suite_test.go
new file mode 100644
index 000000000..e721838b2
--- /dev/null
+++ b/pkg/finalizer/suite_test.go
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package finalizer_test
+
+import (
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+func TestConfig(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Utils Test Suite")
+}
diff --git a/pkg/logging/config_test.go b/pkg/logging/config_test.go
index 72f4e29c3..8b62c82d3 100644
--- a/pkg/logging/config_test.go
+++ b/pkg/logging/config_test.go
@@ -6,7 +6,6 @@ package logging_test
import (
"bytes"
- "encoding/json"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -43,11 +42,9 @@ ERROR