From 257329af4470a929994287807fe39ff6b1388b71 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 29 Oct 2024 17:07:32 +0100 Subject: [PATCH] handle implicit version part of an artifact identity unified handling of modification of elements ocm hash tests generate reference doc revert oci related changes --- api/helper/builder/ocm_identity.go | 16 ++ api/helper/builder/ocm_reference.go | 3 +- api/helper/builder/ocm_resource.go | 4 +- api/oci/ociutils/ref.go | 3 +- api/ocm/add_test.go | 14 +- api/ocm/compdesc/componentdescriptor.go | 17 +- api/ocm/compdesc/default.go | 47 +++++- .../versions/ocm.software/v3alpha1/default.go | 26 --- api/ocm/compdesc/versions/v2/default.go | 26 --- api/ocm/cpi/dummy.go | 8 +- api/ocm/cpi/modopts.go | 22 ++- api/ocm/cpi/repocpi/view_cv.go | 46 ++++-- api/ocm/internal/modopts.go | 145 ++++++++++++----- api/ocm/internal/repository.go | 11 +- api/ocm/modopts.go | 16 +- api/ocm/tools/signing/handler_test.go | 14 +- api/ocm/tools/signing/signing_test.go | 153 ++++++++++++++++++ api/ocm/tools/transfer/transfer.go | 2 +- .../commands/ocmcmds/common/addhdlrs/base.go | 8 +- .../ocmcmds/common/addhdlrs/options.go | 41 +++-- .../ocmcmds/common/addhdlrs/refs/elements.go | 3 +- .../ocmcmds/common/addhdlrs/rscs/elements.go | 12 +- .../ocmcmds/common/addhdlrs/srcs/elements.go | 4 +- .../commands/ocmcmds/components/hash/cmd.go | 45 ++++-- .../ocmcmds/components/hash/cmd_test.go | 44 +++++ .../ocmcmds/components/hash/options.go | 2 +- docs/reference/ocm_add_componentversions.md | 1 + docs/reference/ocm_add_references.md | 1 + docs/reference/ocm_add_resources.md | 1 + docs/reference/ocm_add_sources.md | 1 + docs/reference/ocm_hash_componentversions.md | 2 +- 31 files changed, 561 insertions(+), 177 deletions(-) diff --git a/api/helper/builder/ocm_identity.go b/api/helper/builder/ocm_identity.go index f77719a7e3..ec302ee622 100644 --- a/api/helper/builder/ocm_identity.go +++ b/api/helper/builder/ocm_identity.go @@ -1,5 +1,9 @@ package builder +import ( + metav1 "ocm.software/ocm/api/ocm/compdesc/meta/v1" +) + const T_OCMMETA = "element with metadata" //////////////////////////////////////////////////////////////////////////////// @@ -10,6 +14,18 @@ func (b *Builder) ExtraIdentity(name string, value string) { b.ocm_meta.ExtraIdentity.Set(name, value) } +func (b *Builder) ExtraIdentities(extras ...string) { + b.expect(b.ocm_meta, T_OCMMETA) + + id := metav1.NewExtraIdentity(extras...) + if b.ocm_meta.ExtraIdentity == nil { + b.ocm_meta.ExtraIdentity = metav1.Identity{} + } + for k, v := range id { + b.ocm_meta.ExtraIdentity.Set(k, v) + } +} + //////////////////////////////////////////////////////////////////////////////// func (b *Builder) RemoveExtraIdentity(name string) { diff --git a/api/helper/builder/ocm_reference.go b/api/helper/builder/ocm_reference.go index 93fc02f932..8f3d2b92f7 100644 --- a/api/helper/builder/ocm_reference.go +++ b/api/helper/builder/ocm_reference.go @@ -1,6 +1,7 @@ package builder import ( + "ocm.software/ocm/api/ocm" "ocm.software/ocm/api/ocm/compdesc" ) @@ -22,7 +23,7 @@ func (r *ocmReference) Set() { } func (r *ocmReference) Close() error { - return r.ocm_vers.SetReference(&r.meta) + return r.ocm_vers.SetReference(&r.meta, ocm.ModifyElement()) } //////////////////////////////////////////////////////////////////////////////// diff --git a/api/helper/builder/ocm_resource.go b/api/helper/builder/ocm_resource.go index 9b3ffe30dc..fb29c85b65 100644 --- a/api/helper/builder/ocm_resource.go +++ b/api/helper/builder/ocm_resource.go @@ -42,9 +42,9 @@ func (r *ocmResource) Close() error { } switch { case r.access != nil: - return r.Builder.ocm_vers.SetResource(&r.meta, r.access, r.opts.ApplyModificationOptions((ocm.ModifyResource()))) + return r.Builder.ocm_vers.SetResource(&r.meta, r.access, r.opts.ApplyModificationOptions((ocm.ModifyElement()))) case r.blob != nil: - return r.Builder.ocm_vers.SetResourceBlob(&r.meta, r.blob, r.hint, nil, r.opts.ApplyModificationOptions((ocm.ModifyResource()))) + return r.Builder.ocm_vers.SetResourceBlob(&r.meta, r.blob, r.hint, nil, r.opts.ApplyModificationOptions((ocm.ModifyElement()))) } return errors.New("access or blob required") } diff --git a/api/oci/ociutils/ref.go b/api/oci/ociutils/ref.go index 50f7675264..5d05921777 100644 --- a/api/oci/ociutils/ref.go +++ b/api/oci/ociutils/ref.go @@ -89,8 +89,7 @@ func (v *ArtVersion) IsDigested() bool { } func (v *ArtVersion) GetTag() string { - if v != nil && - v.Tag != nil { + if v != nil && v.Tag != nil { return *v.Tag } return "" diff --git a/api/ocm/add_test.go b/api/ocm/add_test.go index 9d27a6c0de..1c33309f7d 100644 --- a/api/ocm/add_test.go +++ b/api/ocm/add_test.go @@ -179,13 +179,13 @@ var _ = Describe("add resources", func() { Context("references", func() { It("adds reference", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) Expect(len(cv.GetDescriptor().References)).To(Equal(1)) }) It("replaces reference", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) MustBeSuccessful(cv.SetReference(ref.WithVersion("v1"))) Expect(len(Must(cv.SelectReferences(selectors.Name("test"))))).To(Equal(1)) @@ -193,7 +193,7 @@ var _ = Describe("add resources", func() { It("replaces source (enforced)", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) MustBeSuccessful(cv.SetReference(ref.WithVersion("v2"))) Expect(len(Must(cv.SelectReferences(selectors.Name("test"))))).To(Equal(1)) @@ -201,7 +201,7 @@ var _ = Describe("add resources", func() { It("fails replace non-existent source)", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) Expect(cv.SetReference(ref.WithExtraIdentity("attr", "value"), ocm.UpdateElement)).To( MatchError("element \"attr\"=\"value\",\"name\"=\"test\" not found")) @@ -209,21 +209,21 @@ var _ = Describe("add resources", func() { It("adds duplicate reference with different version", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) MustBeSuccessful(cv.SetReference(ref.WithVersion("v2"), ocm.AppendElement)) Expect(len(Must(cv.SelectReferences(selectors.Name("test"))))).To(Equal(2)) }) It("rejects duplicate reference with same version", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) Expect(cv.SetReference(ref.WithVersion("v1"), ocm.AppendElement)). To(MatchError("adding a new reference with same base identity requires different version")) }) It("rejects duplicate reference with extra identity", func() { ref := ocm.NewComponentReference("test", COMPONENT+"/sub", "v1").WithExtraIdentity("attr", "value") - MustBeSuccessful(cv.SetReference(ref)) + MustBeSuccessful(cv.SetReference(ref, ocm.ModifyElement())) Expect(cv.SetReference(ref, ocm.AppendElement)). To(MatchError("adding a new reference with same base identity requires different version")) }) diff --git a/api/ocm/compdesc/componentdescriptor.go b/api/ocm/compdesc/componentdescriptor.go index 02feb8d26c..8638dfdb7c 100644 --- a/api/ocm/compdesc/componentdescriptor.go +++ b/api/ocm/compdesc/componentdescriptor.go @@ -238,18 +238,21 @@ func (o *ElementMeta) GetIdentity(accessor ElementListAccessor) metav1.Identity identity = metav1.Identity{} } identity[SystemIdentityName] = o.Name - if accessor != nil { + if identity.Get(SystemIdentityVersion) == "" && accessor != nil { found := false l := accessor.Len() for i := 0; i < l; i++ { m := accessor.Get(i).GetMeta() - if m.GetName() == o.Name && m.GetExtraIdentity().Equals(o.ExtraIdentity) { - if found { - identity[SystemIdentityVersion] = o.Version - - break + if m.GetName() == o.Name { + mid := m.GetExtraIdentity() + mid.Remove(SystemIdentityVersion) + if mid.Equals(o.ExtraIdentity) { + if found { + identity[SystemIdentityVersion] = o.Version + break + } + found = true } - found = true } } } diff --git a/api/ocm/compdesc/default.go b/api/ocm/compdesc/default.go index af504034f2..22c3359152 100644 --- a/api/ocm/compdesc/default.go +++ b/api/ocm/compdesc/default.go @@ -27,9 +27,16 @@ func DefaultComponent(component *ComponentDescriptor) *ComponentDescriptor { return component } +func DefaultElements(component *ComponentDescriptor) { + DefaultResources(component) + DefaultSources(component) + DefaultReferences(component) +} + // DefaultResources defaults a list of resources. // The version of the component is defaulted for local resources that do not contain a version. // adds the version as identity if the resource identity would clash otherwise. +// The version is added to an extraIdentity, if it is not unique without it. func DefaultResources(component *ComponentDescriptor) { for i, res := range component.Resources { if res.Relation == v1.LocalRelation && len(res.Version) == 0 { @@ -39,7 +46,45 @@ func DefaultResources(component *ComponentDescriptor) { id := res.GetIdentity(component.Resources) if v, ok := id[SystemIdentityVersion]; ok { if res.ExtraIdentity == nil { - res.ExtraIdentity = v1.Identity{ + component.Resources[i].ExtraIdentity = v1.Identity{ + SystemIdentityVersion: v, + } + } else { + if _, ok := res.ExtraIdentity[SystemIdentityVersion]; !ok { + res.ExtraIdentity[SystemIdentityVersion] = v + } + } + } + } +} + +// DefaultSources defaults a list of sources. +// The version is added to an extraIdentity, if it is not unique without it. +func DefaultSources(component *ComponentDescriptor) { + for i, res := range component.Sources { + id := res.GetIdentity(component.Resources) + if v, ok := id[SystemIdentityVersion]; ok { + if res.ExtraIdentity == nil { + component.Sources[i].ExtraIdentity = v1.Identity{ + SystemIdentityVersion: v, + } + } else { + if _, ok := res.ExtraIdentity[SystemIdentityVersion]; !ok { + res.ExtraIdentity[SystemIdentityVersion] = v + } + } + } + } +} + +// DefaultReferences defaults a list of references. +// The version is added to an extraIdentity, if it is not unique without it. +func DefaultReferences(component *ComponentDescriptor) { + for i, res := range component.References { + id := res.GetIdentity(component.Resources) + if v, ok := id[SystemIdentityVersion]; ok { + if res.ExtraIdentity == nil { + component.References[i].ExtraIdentity = v1.Identity{ SystemIdentityVersion: v, } } else { diff --git a/api/ocm/compdesc/versions/ocm.software/v3alpha1/default.go b/api/ocm/compdesc/versions/ocm.software/v3alpha1/default.go index 0a03ce49ab..979c2b09ea 100644 --- a/api/ocm/compdesc/versions/ocm.software/v3alpha1/default.go +++ b/api/ocm/compdesc/versions/ocm.software/v3alpha1/default.go @@ -1,7 +1,6 @@ package v3alpha1 import ( - v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1" "ocm.software/ocm/api/utils/runtime" ) @@ -20,30 +19,5 @@ func (cd *ComponentDescriptor) Default() error { cd.Spec.Resources = make([]Resource, 0) } - DefaultResources(cd) return nil } - -// DefaultResources defaults a list of resources. -// The version of the component is defaulted for local resources that do not contain a version. -// adds the version as identity if the resource identity would clash otherwise. -func DefaultResources(component *ComponentDescriptor) { - for i, res := range component.Spec.Resources { - if res.Relation == v1.LocalRelation && len(res.Version) == 0 { - component.Spec.Resources[i].Version = component.GetVersion() - } - - id := res.GetIdentity(component.Spec.Resources) - if v, ok := id[SystemIdentityVersion]; ok { - if res.ExtraIdentity == nil { - res.ExtraIdentity = v1.Identity{ - SystemIdentityVersion: v, - } - } else { - if _, ok := res.ExtraIdentity[SystemIdentityVersion]; !ok { - res.ExtraIdentity[SystemIdentityVersion] = v - } - } - } - } -} diff --git a/api/ocm/compdesc/versions/v2/default.go b/api/ocm/compdesc/versions/v2/default.go index ce4aec2201..08ea24a1a3 100644 --- a/api/ocm/compdesc/versions/v2/default.go +++ b/api/ocm/compdesc/versions/v2/default.go @@ -1,7 +1,6 @@ package v2 import ( - v1 "ocm.software/ocm/api/ocm/compdesc/meta/v1" "ocm.software/ocm/api/utils/runtime" ) @@ -20,30 +19,5 @@ func (cd *ComponentDescriptor) Default() error { cd.Resources = make([]Resource, 0) } - DefaultResources(cd) return nil } - -// DefaultResources defaults a list of resources. -// The version of the component is defaulted for local resources that do not contain a version. -// adds the version as identity if the resource identity would clash otherwise. -func DefaultResources(component *ComponentDescriptor) { - for i, res := range component.Resources { - if res.Relation == v1.LocalRelation && len(res.Version) == 0 { - component.Resources[i].Version = component.GetVersion() - } - - id := res.GetIdentity(component.Resources) - if v, ok := id[SystemIdentityVersion]; ok { - if res.ExtraIdentity == nil { - res.ExtraIdentity = v1.Identity{ - SystemIdentityVersion: v, - } - } else { - if _, ok := res.ExtraIdentity[SystemIdentityVersion]; !ok { - res.ExtraIdentity[SystemIdentityVersion] = v - } - } - } - } -} diff --git a/api/ocm/cpi/dummy.go b/api/ocm/cpi/dummy.go index 7181a35c05..fa45b54aa4 100644 --- a/api/ocm/cpi/dummy.go +++ b/api/ocm/cpi/dummy.go @@ -164,19 +164,19 @@ func (d *DummyComponentVersionAccess) SetResourceByAccess(art ResourceAccess, mo return errors.ErrNotSupported("resource modification") } -func (d *DummyComponentVersionAccess) SetSourceBlob(meta *SourceMeta, blob BlobAccess, refname string, global AccessSpec, opts ...TargetOption) error { +func (d *DummyComponentVersionAccess) SetSourceBlob(meta *SourceMeta, blob BlobAccess, refname string, global AccessSpec, opts ...TargetElementOption) error { return errors.ErrNotSupported("source modification") } -func (d *DummyComponentVersionAccess) SetSource(meta *SourceMeta, spec compdesc.AccessSpec, opts ...TargetOption) error { +func (d *DummyComponentVersionAccess) SetSource(meta *SourceMeta, spec compdesc.AccessSpec, opts ...TargetElementOption) error { return errors.ErrNotSupported("source modification") } -func (d *DummyComponentVersionAccess) SetSourceByAccess(art SourceAccess, opts ...TargetOption) error { +func (d *DummyComponentVersionAccess) SetSourceByAccess(art SourceAccess, opts ...TargetElementOption) error { return errors.ErrNotSupported() } -func (d *DummyComponentVersionAccess) SetReference(ref *ComponentReference, opts ...TargetOption) error { +func (d *DummyComponentVersionAccess) SetReference(ref *ComponentReference, opts ...ElementModificationOption) error { return errors.ErrNotSupported() } diff --git a/api/ocm/cpi/modopts.go b/api/ocm/cpi/modopts.go index 1666c0355b..ebdfeab00d 100644 --- a/api/ocm/cpi/modopts.go +++ b/api/ocm/cpi/modopts.go @@ -9,9 +9,12 @@ import ( ) type ( - TargetElement = internal.TargetElement - TargetOption = internal.TargetOption - TargetOptions = internal.TargetOptions + TargetElement = internal.TargetElement + TargetElementOption = internal.TargetElementOption + TargetElementOptions = internal.TargetElementOptions + + ElementModificationOption = internal.ElementModificationOption + ElementModificationOptions = internal.ElementModificationOptions ModificationOption = internal.ModificationOption ModificationOptions = internal.ModificationOptions @@ -26,8 +29,8 @@ type ( AddVersionOptions = internal.AddVersionOptions ) -func NewTargetOptions(list ...TargetOption) *TargetOptions { - var m TargetOptions +func NewTargetElementOptions(list ...TargetElementOption) *TargetElementOptions { + var m TargetElementOptions m.ApplyTargetOptions(list...) return &m } @@ -65,6 +68,10 @@ func NewModificationOptions(list ...ModificationOption) *ModificationOptions { return internal.NewModificationOptions(list...) } +func NewElementModificationOptions(list ...ElementModificationOption) *ElementModificationOptions { + return internal.NewElementModificationOptions(list...) +} + func TargetIndex(idx int) internal.TargetIndex { return internal.TargetIndex(-1) } @@ -77,10 +84,15 @@ func TargetIdentity(id v1.Identity) internal.TargetIdentity { return internal.TargetIdentity(id) } +// Deprecated: use ModifyElement. func ModifyResource(flag ...bool) internal.ModOptionImpl { return internal.ModifyResource(flag...) } +func ModifyElement(flag ...bool) internal.ElemModOptionImpl { + return internal.ModifyElement(flag...) +} + func AcceptExistentDigests(flag ...bool) internal.ModOptionImpl { return internal.AcceptExistentDigests(flag...) } diff --git a/api/ocm/cpi/repocpi/view_cv.go b/api/ocm/cpi/repocpi/view_cv.go index 98e43e2aa6..2c36d65db8 100644 --- a/api/ocm/cpi/repocpi/view_cv.go +++ b/api/ocm/cpi/repocpi/view_cv.go @@ -249,7 +249,7 @@ func (c *componentVersionAccessView) SetResourceBlob(meta *cpi.ResourceMeta, blo return fmt.Errorf("unable to add blob (component %s:%s resource %s): %w", c.GetName(), c.GetVersion(), meta.GetName(), err) } - if err := c.SetResource(meta, acc, eff, cpi.ModifyResource()); err != nil { + if err := c.SetResource(meta, acc, eff, cpi.ModifyElement()); err != nil { return fmt.Errorf("unable to set resource: %w", err) } @@ -264,7 +264,7 @@ func (c *componentVersionAccessView) AdjustSourceAccess(meta *cpi.SourceMeta, ac return errors.ErrUnknown(cpi.KIND_RESOURCE, meta.GetIdentity(cd.Resources).String()) } -func (c *componentVersionAccessView) SetSourceBlob(meta *cpi.SourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec, modopts ...cpi.TargetOption) error { +func (c *componentVersionAccessView) SetSourceBlob(meta *cpi.SourceMeta, blob cpi.BlobAccess, refName string, global cpi.AccessSpec, modopts ...cpi.TargetElementOption) error { cpi.Logger(c).Debug("adding source blob", "source", meta.Name) if err := utils.ValidateObject(blob); err != nil { return err @@ -384,7 +384,7 @@ func (c *componentVersionAccessView) SetResource(meta *cpi.ResourceMeta, acc com cd := c.bridge.GetDescriptor() - idx, err := c.getElementIndex("resource", cd.Resources, res, &opts.TargetOptions) + idx, err := c.getElementIndex("resource", cd.Resources, res, &opts.TargetElementOptions) if err != nil { return err } @@ -393,7 +393,7 @@ func (c *componentVersionAccessView) SetResource(meta *cpi.ResourceMeta, acc com } if old == nil { - if !opts.IsModifyResource() && c.bridge.IsPersistent() { + if !opts.IsModifyElement() && c.bridge.IsPersistent() { return fmt.Errorf("new resource would invalidate signature") } } @@ -456,7 +456,7 @@ func (c *componentVersionAccessView) SetResource(meta *cpi.ResourceMeta, acc com if old != nil { eq := res.Equivalent(old) if !eq.IsLocalHashEqual() && c.bridge.IsPersistent() { - if !opts.IsModifyResource() { + if !opts.IsModifyElement() { return fmt.Errorf("resource would invalidate signature") } cd.Signatures = nil @@ -468,6 +468,10 @@ func (c *componentVersionAccessView) SetResource(meta *cpi.ResourceMeta, acc com } else { cd.Resources[idx] = *res } + if opts.IsModifyElement() { + // default handling for completing an extra identity for modifications, only. + compdesc.DefaultResources(cd) + } return c.bridge.Update(false) }) } @@ -499,7 +503,7 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R if !old.Digest.IsNone() { digester.HashAlgorithm = old.Digest.HashAlgorithm digester.NormalizationAlgorithm = old.Digest.NormalisationAlgorithm - if opts.IsAcceptExistentDigests() && !opts.IsModifyResource() && c.bridge.IsPersistent() { + if opts.IsAcceptExistentDigests() && !opts.IsModifyElement() && c.bridge.IsPersistent() { res.Digest = old.Digest value = old.Digest.Value } @@ -508,7 +512,7 @@ func (c *componentVersionAccessView) evaluateResourceDigest(res, old *compdesc.R return hashAlgo, digester, value } -func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess, optslist ...cpi.TargetOption) error { +func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess, optslist ...cpi.TargetElementOption) error { return setAccess(c, "source", art, func(meta *cpi.SourceMeta, acc compdesc.AccessSpec) error { return c.SetSource(meta, acc, optslist...) @@ -518,7 +522,7 @@ func (c *componentVersionAccessView) SetSourceByAccess(art cpi.SourceAccess, opt }) } -func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdesc.AccessSpec, optlist ...cpi.TargetOption) error { +func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdesc.AccessSpec, optlist ...cpi.TargetElementOption) error { if c.bridge.IsReadOnly() { return accessio.ErrReadOnly } @@ -544,33 +548,51 @@ func (c *componentVersionAccessView) SetSource(meta *cpi.SourceMeta, acc compdes } else { cd.Sources[idx] = *res } + compdesc.DefaultSources(cd) return c.bridge.Update(false) }) } -func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference, optlist ...cpi.TargetOption) error { +func (c *componentVersionAccessView) SetReference(ref *cpi.ComponentReference, optlist ...cpi.ElementModificationOption) error { + opts := cpi.NewElementModificationOptions(optlist...) + moddef := false + return c.Execute(func() error { cd := c.bridge.GetDescriptor() if ref.Version == "" { return fmt.Errorf("version required for component version reference") } - idx, err := c.getElementIndex("reference", cd.References, ref, optlist...) + idx, err := c.getElementIndex("reference", cd.References, ref, &opts.TargetElementOptions) if err != nil { return err } if idx < 0 { + if !opts.IsModifyElement(moddef) { + return fmt.Errorf("adding reference would invalidate signature") + } cd.References = append(cd.References, *ref) } else { + eq := ref.Equivalent(&cd.References[idx]) + if !eq.IsEquivalent() && c.bridge.IsPersistent() { + if !opts.IsModifyElement(moddef) { + return fmt.Errorf("reference would invalidate signature") + } + cd.Signatures = nil + } + cd.References[idx].Equivalent(ref) cd.References[idx] = *ref } + if opts.IsModifyElement(moddef) { + compdesc.DefaultReferences(cd) + } return c.bridge.Update(false) }) } -func (c *componentVersionAccessView) getElementIndex(kind string, acc compdesc.ElementListAccessor, prov compdesc.ElementMetaProvider, optlist ...cpi.TargetOption) (int, error) { - opts := internal.NewTargetOptions(optlist...) +func (c *componentVersionAccessView) getElementIndex(kind string, acc compdesc.ElementListAccessor, prov compdesc.ElementMetaProvider, optlist ...cpi.TargetElementOption) (int, error) { + opts := internal.NewTargetElementOptions(optlist...) curidx := compdesc.ElementIndex(acc, prov) meta := prov.GetMeta() var idx int diff --git a/api/ocm/internal/modopts.go b/api/ocm/internal/modopts.go index 41826374bc..3732c59434 100644 --- a/api/ocm/internal/modopts.go +++ b/api/ocm/internal/modopts.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/mandelsoft/goutils/general" + "github.com/mandelsoft/goutils/generics" "github.com/mandelsoft/goutils/optionutils" "ocm.software/ocm/api/ocm/compdesc" @@ -100,32 +101,36 @@ type TargetElement interface { } type TargetOptionImpl interface { - TargetOption + TargetElementOption ModificationOption BlobModificationOption } -type TargetOptions struct { +type TargetElementOptions struct { TargetElement TargetElement } -type TargetOption interface { - ApplyTargetOption(options *TargetOptions) +type TargetElementOption interface { + ApplyTargetOption(options *TargetElementOptions) } -func (m *TargetOptions) ApplyBlobModificationOption(opts *BlobModificationOptions) { - m.ApplyTargetOption(&opts.TargetOptions) +func (m *TargetElementOptions) ApplyBlobModificationOption(opts *BlobModificationOptions) { + m.ApplyTargetOption(&opts.TargetElementOptions) } -func (m *TargetOptions) ApplyModificationOption(opts *ModificationOptions) { - m.ApplyTargetOption(&opts.TargetOptions) +func (m *TargetElementOptions) ApplyModificationOption(opts *ModificationOptions) { + m.ApplyTargetOption(&opts.TargetElementOptions) } -func (m *TargetOptions) ApplyTargetOption(opts *TargetOptions) { +func (m *TargetElementOptions) ApplyElementModificationOption(opts *ElementModificationOptions) { + m.ApplyTargetOption(&opts.TargetElementOptions) +} + +func (m *TargetElementOptions) ApplyTargetOption(opts *TargetElementOptions) { optionutils.Transfer(&opts.TargetElement, m.TargetElement) } -func (m *TargetOptions) ApplyTargetOptions(list ...TargetOption) *TargetOptions { +func (m *TargetElementOptions) ApplyTargetOptions(list ...TargetElementOption) *TargetElementOptions { for _, o := range list { if o != nil { o.ApplyTargetOption(m) @@ -134,12 +139,55 @@ func (m *TargetOptions) ApplyTargetOptions(list ...TargetOption) *TargetOptions return m } -func NewTargetOptions(list ...TargetOption) *TargetOptions { - var m TargetOptions +func NewTargetElementOptions(list ...TargetElementOption) *TargetElementOptions { + var m TargetElementOptions m.ApplyTargetOptions(list...) return &m } +type ElementModificationOption interface { + ApplyElementModificationOption(opts *ElementModificationOptions) +} + +type ElementModificationOptions struct { + TargetElementOptions + + // ModifyElement disables the modification of signature relevant + // resource parts. + ModifyElement *bool +} + +func (m *ElementModificationOptions) ApplyBlobModificationOption(opts *BlobModificationOptions) { + m.ApplyElementModificationOption(&opts.ElementModificationOptions) +} + +func (m *ElementModificationOptions) ApplyModificationOption(opts *ModificationOptions) { + m.ApplyElementModificationOption(&opts.ElementModificationOptions) +} + +func (m *ElementModificationOptions) ApplyElementModificationOption(opts *ElementModificationOptions) { + optionutils.Transfer(&opts.ModifyElement, m.ModifyElement) +} + +func (m *ElementModificationOptions) ApplyElementModificationOptions(list ...ElementModificationOption) *ElementModificationOptions { + for _, o := range list { + if o != nil { + o.ApplyElementModificationOption(m) + } + } + return m +} + +func (m *ElementModificationOptions) IsModifyElement(def ...bool) bool { + return utils.AsBool(m.ModifyElement, def...) +} + +func NewElementModificationOptions(list ...ElementModificationOption) *ElementModificationOptions { + var m ElementModificationOptions + m.ApplyElementModificationOptions(list...) + return &m +} + type ModificationOption interface { ApplyModificationOption(opts *ModificationOptions) } @@ -149,12 +197,14 @@ type ModOptionImpl interface { BlobModificationOption } -type ModificationOptions struct { - TargetOptions +type ElemModOptionImpl interface { + ElementModificationOption + ModificationOption + BlobModificationOption +} - // ModifyResource disables the modification of signature releveant - // resource parts. - ModifyResource *bool +type ModificationOptions struct { + ElementModificationOptions // AcceptExistentDigests don't validate/recalculate the content digest // of resources. @@ -173,10 +223,6 @@ type ModificationOptions struct { SkipDigest *bool } -func (m *ModificationOptions) IsModifyResource() bool { - return utils.AsBool(m.ModifyResource) -} - func (m *ModificationOptions) IsAcceptExistentDigests() bool { return utils.AsBool(m.AcceptExistentDigests) } @@ -203,8 +249,8 @@ func (m *ModificationOptions) ApplyBlobModificationOption(opts *BlobModification } func (m *ModificationOptions) ApplyModificationOption(opts *ModificationOptions) { - m.TargetOptions.ApplyTargetOption(&opts.TargetOptions) - optionutils.Transfer(&opts.ModifyResource, m.ModifyResource) + m.TargetElementOptions.ApplyTargetOption(&opts.TargetElementOptions) + optionutils.Transfer(&opts.ModifyElement, m.ModifyElement) optionutils.Transfer(&opts.AcceptExistentDigests, m.AcceptExistentDigests) optionutils.Transfer(&opts.SkipDigest, m.SkipDigest) optionutils.Transfer(&opts.SkipVerify, m.SkipVerify) @@ -238,10 +284,17 @@ func (m TargetIndex) ApplyBlobModificationOption(opts *BlobModificationOptions) } func (m TargetIndex) ApplyModificationOption(opts *ModificationOptions) { - m.ApplyTargetOption(&opts.TargetOptions) + m.ApplyTargetOption(&opts.TargetElementOptions) } -func (m TargetIndex) ApplyTargetOption(opts *TargetOptions) { +func (m TargetIndex) ApplyElementModificationOption(opts *ElementModificationOptions) { + if m < 0 { + opts.ModifyElement = generics.Pointer(true) + } + m.ApplyTargetOption(&opts.TargetElementOptions) +} + +func (m TargetIndex) ApplyTargetOption(opts *TargetElementOptions) { opts.TargetElement = m } @@ -259,10 +312,14 @@ func (m TargetIdentityOrAppend) ApplyBlobModificationOption(opts *BlobModificati } func (m TargetIdentityOrAppend) ApplyModificationOption(opts *ModificationOptions) { - m.ApplyTargetOption(&opts.TargetOptions) + m.ApplyTargetOption(&opts.TargetElementOptions) +} + +func (m TargetIdentityOrAppend) ApplyElementModificationOption(opts *ElementModificationOptions) { + m.ApplyTargetOption(&opts.TargetElementOptions) } -func (m TargetIdentityOrAppend) ApplyTargetOption(opts *TargetOptions) { +func (m TargetIdentityOrAppend) ApplyTargetOption(opts *TargetElementOptions) { opts.TargetElement = m } @@ -285,10 +342,14 @@ func (m TargetIdentity) ApplyBlobModificationOption(opts *BlobModificationOption } func (m TargetIdentity) ApplyModificationOption(opts *ModificationOptions) { - m.ApplyTargetOption(&opts.TargetOptions) + m.ApplyTargetOption(&opts.TargetElementOptions) } -func (m TargetIdentity) ApplyTargetOption(opts *TargetOptions) { +func (m TargetIdentity) ApplyElementModificationOption(opts *ElementModificationOptions) { + m.ApplyTargetOption(&opts.TargetElementOptions) +} + +func (m TargetIdentity) ApplyTargetOption(opts *TargetElementOptions) { opts.TargetElement = m } @@ -313,27 +374,39 @@ func (m replaceElement) ApplyBlobModificationOption(opts *BlobModificationOption } func (m replaceElement) ApplyModificationOption(opts *ModificationOptions) { - m.ApplyTargetOption(&opts.TargetOptions) + m.ApplyTargetOption(&opts.TargetElementOptions) } -func (m replaceElement) ApplyTargetOption(opts *TargetOptions) { +func (m replaceElement) ApplyElementModificationOption(opts *ElementModificationOptions) { + m.ApplyTargetOption(&opts.TargetElementOptions) +} + +func (m replaceElement) ApplyTargetOption(opts *TargetElementOptions) { opts.TargetElement = m } //////////////////////////////////////////////////////////////////////////////// -type modifyresource bool +type modifyelement bool -func (m modifyresource) ApplyBlobModificationOption(opts *BlobModificationOptions) { +func (m modifyelement) ApplyBlobModificationOption(opts *BlobModificationOptions) { m.ApplyModificationOption(&opts.ModificationOptions) } -func (m modifyresource) ApplyModificationOption(opts *ModificationOptions) { - opts.ModifyResource = utils.BoolP(m) +func (m modifyelement) ApplyModificationOption(opts *ModificationOptions) { + opts.ModifyElement = utils.BoolP(m) +} + +func (m modifyelement) ApplyElementModificationOption(opts *ElementModificationOptions) { + opts.ModifyElement = utils.BoolP(m) } func ModifyResource(flag ...bool) ModOptionImpl { - return modifyresource(utils.OptionalDefaultedBool(true, flag...)) + return modifyelement(utils.OptionalDefaultedBool(true, flag...)) +} + +func ModifyElement(flag ...bool) ElemModOptionImpl { + return modifyelement(utils.OptionalDefaultedBool(true, flag...)) } //////////////////////////////////////////////////////////////////////////////// diff --git a/api/ocm/internal/repository.go b/api/ocm/internal/repository.go index 4aa4008afc..7ed53168f7 100644 --- a/api/ocm/internal/repository.go +++ b/api/ocm/internal/repository.go @@ -144,10 +144,10 @@ type ComponentVersionAccess interface { // SetSource updates or sets anew source. The options only use the // target options. All other options are ignored. - SetSource(*SourceMeta, compdesc.AccessSpec, ...TargetOption) error + SetSource(*SourceMeta, compdesc.AccessSpec, ...TargetElementOption) error // SetSourceByAccess updates or sets anew source. The options only use the // target options. All other options are ignored. - SetSourceByAccess(art SourceAccess, opts ...TargetOption) error + SetSourceByAccess(art SourceAccess, opts ...TargetElementOption) error GetReference(meta metav1.Identity) (ComponentReference, error) GetReferenceIndex(meta metav1.Identity) int @@ -155,7 +155,10 @@ type ComponentVersionAccess interface { GetReferences() []ComponentReference SelectReferences(sel ...refsel.Selector) ([]ComponentReference, error) - SetReference(ref *ComponentReference, opts ...TargetOption) error + // SetReference adds or updates a reference. By default, it does not allow for + // signature relevant changes. If such operations should be possible + // the option ModifyElement() has to be passed as option. + SetReference(ref *ComponentReference, opts ...ElementModificationOption) error // AddBlob adds a local blob and returns an appropriate local access spec. AddBlob(blob BlobAccess, artType, refName string, global AccessSpec, opts ...BlobUploadOption) (AccessSpec, error) @@ -166,7 +169,7 @@ type ComponentVersionAccess interface { AdjustSourceAccess(meta *SourceMeta, acc compdesc.AccessSpec) error // SetSourceBlob updates or sets anew source. The options only use the // target options. All other options are ignored. - SetSourceBlob(meta *SourceMeta, blob BlobAccess, refname string, global AccessSpec, opts ...TargetOption) error + SetSourceBlob(meta *SourceMeta, blob BlobAccess, refname string, global AccessSpec, opts ...TargetElementOption) error // AccessMethod provides an access method implementation for // an access spec. This might be a repository local implementation diff --git a/api/ocm/modopts.go b/api/ocm/modopts.go index 5c271f0e6b..a980ae0485 100644 --- a/api/ocm/modopts.go +++ b/api/ocm/modopts.go @@ -9,9 +9,12 @@ import ( ) type ( - TargetElement = internal.TargetElement - TargetOption = internal.TargetOption - TargetOptions = internal.TargetOptions + TargetElement = internal.TargetElement + TargetElementOption = internal.TargetElementOption + TargetElementOptions = internal.TargetElementOptions + + ElementModificationOption = internal.ElementModificationOption + ElementModificationOptions = internal.ElementModificationOptions ModificationOption = internal.ModificationOption ModificationOptions = internal.ModificationOptions @@ -60,7 +63,7 @@ func NewModificationOptions(list ...ModificationOption) *ModificationOptions { } func TargetIndex(idx int) internal.TargetOptionImpl { - return internal.TargetIndex(-1) + return internal.TargetIndex(idx) } const AppendElement = internal.TargetIndex(-1) @@ -75,10 +78,15 @@ func TargetIdentityOrCreate(id v1.Identity) internal.TargetOptionImpl { return internal.TargetIdentityOrAppend(id) } +// Deprecated: use ModifyElement. func ModifyResource(flag ...bool) internal.ModOptionImpl { return internal.ModifyResource(flag...) } +func ModifyElement(flag ...bool) internal.ElemModOptionImpl { + return internal.ModifyElement(flag...) +} + func AcceptExistentDigests(flag ...bool) internal.ModOptionImpl { return internal.AcceptExistentDigests(flag...) } diff --git a/api/ocm/tools/signing/handler_test.go b/api/ocm/tools/signing/handler_test.go index 6707f84a8f..4b7984543c 100644 --- a/api/ocm/tools/signing/handler_test.go +++ b/api/ocm/tools/signing/handler_test.go @@ -58,18 +58,28 @@ var _ = Describe("Simple signing handlers", func() { meta := ocm.NewResourceMeta("blob", resourcetypes.PLAIN_TEXT, v1.LocalRelation) meta.Version = "v1" - meta.ExtraIdentity = map[string]string{} MustBeSuccessful(cv.SetResourceBlob(meta, blobaccess.ForString(mime.MIME_TEXT, "test data"), "", nil)) + meta.ExtraIdentity = map[string]string{} meta.Version = "v2" MustBeSuccessful(cv.SetResourceBlob(meta, blobaccess.ForString(mime.MIME_TEXT, "other test data"), "", nil, ocm.TargetIndex(-1))) }) - It("signs without modification", func() { + It("signs without modification (compatibility)", func() { Must(signing.SignComponentVersion(cv, "signature", signing.PrivateKey("signature", priv))) cd := cv.GetDescriptor() + cd.Resources[0].ExtraIdentity = v1.Identity{} + cd.Resources[1].ExtraIdentity = v1.Identity{} Expect(len(cd.Resources)).To(Equal(2)) Expect(len(cd.Resources[0].ExtraIdentity)).To(Equal(0)) Expect(len(cd.Resources[1].ExtraIdentity)).To(Equal(0)) }) + + It("signs defaulted", func() { + Must(signing.SignComponentVersion(cv, "signature", signing.PrivateKey("signature", priv))) + cd := cv.GetDescriptor() + Expect(len(cd.Resources)).To(Equal(2)) + Expect(len(cd.Resources[0].ExtraIdentity)).To(Equal(1)) + Expect(len(cd.Resources[1].ExtraIdentity)).To(Equal(1)) + }) }) }) diff --git a/api/ocm/tools/signing/signing_test.go b/api/ocm/tools/signing/signing_test.go index f374ec0528..a54e91c9bb 100644 --- a/api/ocm/tools/signing/signing_test.go +++ b/api/ocm/tools/signing/signing_test.go @@ -1286,6 +1286,159 @@ applying to version "github.com/mandelsoft/ref2:v1"[github.com/mandelsoft/ref2:v CheckStore(store, common.NewNameVersion(COMPONENTA, VERSION)) }) }) + + Context("handle extra identity", func() { + BeforeEach(func() { + env.OCMCommonTransport(ARCH, accessio.FormatDirectory, func() { + env.ComponentVersion(COMPONENTA, VERSION, func() { + env.Provider(PROVIDER) + env.Resource("test", "v1", resourcetypes.PLAIN_TEXT, metav1.ExternalRelation, func() { + env.BlobStringData(mime.MIME_TEXT, "test data") + }) + env.Resource("test", "v2", resourcetypes.PLAIN_TEXT, metav1.ExternalRelation, func() { + env.BlobStringData(mime.MIME_TEXT, "extended test data") + env.ModificationOptions(ocm.AppendElement) + env.ExtraIdentities() + }) + }) + }) + }) + + It("signs version with non-unique resource names", func() { + session := datacontext.NewSession() + defer session.Close() + + src := Must(ctf.Open(env.OCMContext(), accessobj.ACC_WRITABLE, ARCH, 0, env)) + archcloser := session.AddCloser(src) + + cv := Must(src.LookupComponentVersion(COMPONENTA, VERSION)) + closer := session.AddCloser(cv) + + cd := cv.GetDescriptor() + + Expect(cd.Resources[0].GetIdentity(cv.GetDescriptor().Resources)).To(YAMLEqual(` +name: test +version: v1 +`)) + Expect(cd.Resources[0].ExtraIdentity).To(YAMLEqual(` +version: v1 +`)) + Expect(cd.Resources[1].GetIdentity(cv.GetDescriptor().Resources)).To(YAMLEqual(` +name: test +version: v2 +`)) + Expect(cd.Resources[1].ExtraIdentity).To(YAMLEqual(` +version: v2 +`)) + data := Must(compdesc.Encode(cd, compdesc.DefaultYAMLCodec)) + Expect(string(data)).To(YAMLEqual(` + component: + componentReferences: [] + name: github.com/mandelsoft/test + provider: mandelsoft + repositoryContexts: [] + resources: + - access: + localReference: sha256:916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9 + mediaType: text/plain + type: localBlob + digest: + hashAlgorithm: SHA-256 + normalisationAlgorithm: genericBlobDigest/v1 + value: 916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9 + name: test + relation: external + type: plainText + version: v1 + extraIdentity: + version: v1 + - access: + localReference: sha256:920ce99fb13b43ca0408caee6e61f6335ea5156d79aa98e733e1ed2393e0f649 + mediaType: text/plain + type: localBlob + digest: + hashAlgorithm: SHA-256 + normalisationAlgorithm: genericBlobDigest/v1 + value: 920ce99fb13b43ca0408caee6e61f6335ea5156d79aa98e733e1ed2393e0f649 + name: test + relation: external + type: plainText + version: v2 + extraIdentity: + version: v2 + sources: [] + version: v1 + meta: + schemaVersion: v2 +`)) + + digest := "70c1b7f5e2260a283e24788c81ea7f8f6e9a70a8544dbf62d6f3a27285f6b633" + + pr, buf := common.NewBufferedPrinter() + // key taken from signing attr + dig := Must(SignComponentVersion(cv, SIGNATURE, SignerByAlgo(SIGN_ALGO), Printer(pr))) + Expect(closer.Close()).To(Succeed()) + Expect(archcloser.Close()).To(Succeed()) + Expect(dig.Value).To(StringEqualWithContext(digest)) + + Expect(buf.String()).To(StringEqualTrimmedWithContext(` +applying to version "github.com/mandelsoft/test:v1"[github.com/mandelsoft/test:v1]... + resource 0: "name"="test","version"="v1": digest SHA-256:916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9[genericBlobDigest/v1] + resource 1: "name"="test","version"="v2": digest SHA-256:920ce99fb13b43ca0408caee6e61f6335ea5156d79aa98e733e1ed2393e0f649[genericBlobDigest/v1] +`)) + + src = Must(ctf.Open(env.OCMContext(), accessobj.ACC_READONLY, ARCH, 0, env)) + session.AddCloser(src) + cv = Must(src.LookupComponentVersion(COMPONENTA, VERSION)) + session.AddCloser(cv) + + cd = cv.GetDescriptor().Copy() + Expect(len(cd.Signatures)).To(Equal(1)) + cd.Signatures = nil // for comparison + data = Must(compdesc.Encode(cd, compdesc.DefaultYAMLCodec)) + + Expect(string(data)).To(YAMLEqual(` + component: + componentReferences: [] + name: github.com/mandelsoft/test + provider: mandelsoft + repositoryContexts: [] + resources: + - access: + localReference: sha256:916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9 + mediaType: text/plain + type: localBlob + digest: + hashAlgorithm: SHA-256 + normalisationAlgorithm: genericBlobDigest/v1 + value: 916f0027a575074ce72a331777c3478d6513f786a591bd892da1a577bf2335f9 + name: test + relation: external + type: plainText + version: v1 + extraIdentity: + version: v1 + - access: + localReference: sha256:920ce99fb13b43ca0408caee6e61f6335ea5156d79aa98e733e1ed2393e0f649 + mediaType: text/plain + type: localBlob + digest: + hashAlgorithm: SHA-256 + normalisationAlgorithm: genericBlobDigest/v1 + value: 920ce99fb13b43ca0408caee6e61f6335ea5156d79aa98e733e1ed2393e0f649 + name: test + relation: external + type: plainText + version: v2 + extraIdentity: + version: v2 + sources: [] + version: v1 + meta: + schemaVersion: v2 +`)) + }) + }) }) func CheckStore(store VerifiedStore, ve common.VersionedElement) { diff --git a/api/ocm/tools/transfer/transfer.go b/api/ocm/tools/transfer/transfer.go index 8490138868..ad6121728e 100644 --- a/api/ocm/tools/transfer/transfer.go +++ b/api/ocm/tools/transfer/transfer.go @@ -273,7 +273,7 @@ func copyVersion(printer common.Printer, log logging.Logger, hist common.History err = handler.HandleTransferResource(r, m, hint, t) } else { if err == nil { // old resource found -> keep current access method - t.SetResource(r.Meta(), old.Access, ocm.ModifyResource(), ocm.SkipVerify()) + t.SetResource(r.Meta(), old.Access, ocm.ModifyElement(), ocm.SkipVerify()) } notifyArtifactInfo(printer, log, "resource", i, r.Meta(), hint, "already present") } diff --git a/cmds/ocm/commands/ocmcmds/common/addhdlrs/base.go b/cmds/ocm/commands/ocmcmds/common/addhdlrs/base.go index f772472acc..5856d57a23 100644 --- a/cmds/ocm/commands/ocmcmds/common/addhdlrs/base.go +++ b/cmds/ocm/commands/ocmcmds/common/addhdlrs/base.go @@ -34,6 +34,10 @@ func (h *ResourceSpecHandlerBase) AddFlags(opts *pflag.FlagSet) { h.options.AddFlags(opts) } -func (h *ResourceSpecHandlerBase) GetTargetOpts() []ocm.TargetOption { - return options.FindOptions[ocm.TargetOption](h.options) +func (h *ResourceSpecHandlerBase) GetTargetOpts() []ocm.TargetElementOption { + return options.FindOptions[ocm.TargetElementOption](h.options) +} + +func (h *ResourceSpecHandlerBase) GetElementModificationOpts() []ocm.ElementModificationOption { + return options.FindOptions[ocm.ElementModificationOption](h.options) } diff --git a/cmds/ocm/commands/ocmcmds/common/addhdlrs/options.go b/cmds/ocm/commands/ocmcmds/common/addhdlrs/options.go index e9614db216..19d370a178 100644 --- a/cmds/ocm/commands/ocmcmds/common/addhdlrs/options.go +++ b/cmds/ocm/commands/ocmcmds/common/addhdlrs/options.go @@ -8,30 +8,51 @@ import ( ) type Options struct { - Replace bool + Replace bool + PreserveSignature bool } -var _ ocm.ModificationOption = (*Options)(nil) +var ( + _ ocm.ModificationOption = (*Options)(nil) + _ ocm.ElementModificationOption = (*Options)(nil) + _ ocm.BlobModificationOption = (*Options)(nil) + _ ocm.TargetElementOption = (*Options)(nil) +) func (o *Options) AddFlags(fs *pflag.FlagSet) { f := fs.Lookup("replace") - if f != nil { - if bp := generics.Cast[*bool](f.Value); bp != nil { - return - } + if f == nil { + fs.BoolVarP(&o.Replace, "replace", "R", false, "replace existing elements") + } + + f = fs.Lookup("preserve-signature") + if f == nil { + fs.BoolVarP(&o.PreserveSignature, "preserve-signature", "P", false, "preserve existing signatures") + } +} + +func (o *Options) applyPreserve(opts *ocm.ElementModificationOptions) { + if !o.PreserveSignature { + opts.ModifyElement = generics.Pointer(true) } - fs.BoolVarP(&o.Replace, "replace", "R", false, "replace existing elements") } func (o *Options) ApplyBlobModificationOption(opts *ocm.BlobModificationOptions) { - o.ApplyTargetOption(&opts.TargetOptions) + o.applyPreserve(&opts.ElementModificationOptions) + o.ApplyTargetOption(&opts.TargetElementOptions) } func (o *Options) ApplyModificationOption(opts *ocm.ModificationOptions) { - o.ApplyTargetOption(&opts.TargetOptions) + o.applyPreserve(&opts.ElementModificationOptions) + o.ApplyTargetOption(&opts.TargetElementOptions) +} + +func (o *Options) ApplyElementModificationOption(opts *ocm.ElementModificationOptions) { + o.applyPreserve(opts) + o.ApplyTargetOption(&opts.TargetElementOptions) } -func (o *Options) ApplyTargetOption(opts *ocm.TargetOptions) { +func (o *Options) ApplyTargetOption(opts *ocm.TargetElementOptions) { if !o.Replace { opts.TargetElement = ocm.AppendElement } diff --git a/cmds/ocm/commands/ocmcmds/common/addhdlrs/refs/elements.go b/cmds/ocm/commands/ocmcmds/common/addhdlrs/refs/elements.go index 3c529accd7..ddab09974b 100644 --- a/cmds/ocm/commands/ocmcmds/common/addhdlrs/refs/elements.go +++ b/cmds/ocm/commands/ocmcmds/common/addhdlrs/refs/elements.go @@ -66,7 +66,8 @@ func (h *ResourceSpecHandler) Set(v ocm.ComponentVersionAccess, r addhdlrs.Eleme }, ComponentName: spec.ComponentName, } - return v.SetReference(meta, h.GetTargetOpts()...) + + return v.SetReference(meta, h.GetElementModificationOpts()...) } //////////////////////////////////////////////////////////////////////////////// diff --git a/cmds/ocm/commands/ocmcmds/common/addhdlrs/rscs/elements.go b/cmds/ocm/commands/ocmcmds/common/addhdlrs/rscs/elements.go index 1cd98d2392..0066db6b9d 100644 --- a/cmds/ocm/commands/ocmcmds/common/addhdlrs/rscs/elements.go +++ b/cmds/ocm/commands/ocmcmds/common/addhdlrs/rscs/elements.go @@ -63,7 +63,7 @@ func (*ResourceSpecHandler) RequireInputs() bool { return true } -func (ResourceSpecHandler) Decode(data []byte) (addhdlrs.ElementSpec, error) { +func (*ResourceSpecHandler) Decode(data []byte) (addhdlrs.ElementSpec, error) { var desc ResourceSpec err := runtime.DefaultYAMLEncoding.Unmarshal(data, &desc) if err != nil { @@ -72,7 +72,7 @@ func (ResourceSpecHandler) Decode(data []byte) (addhdlrs.ElementSpec, error) { return &desc, nil } -func (h ResourceSpecHandler) Set(v ocm.ComponentVersionAccess, r addhdlrs.Element, acc compdesc.AccessSpec) error { +func (h *ResourceSpecHandler) Set(v ocm.ComponentVersionAccess, r addhdlrs.Element, acc compdesc.AccessSpec) error { spec, ok := r.Spec().(*ResourceSpec) if !ok { return fmt.Errorf("element spec is not a valid resource spec, failed to assert type %T to ResourceSpec", r.Spec()) @@ -102,9 +102,11 @@ func (h ResourceSpecHandler) Set(v ocm.ComponentVersionAccess, r addhdlrs.Elemen SourceRefs: compdescv2.ConvertSourcerefsTo(spec.SourceRefs), } opts := h.getModOpts() - if ocm.IsIntermediate(v.Repository().GetSpecification()) { - opts = append(opts, ocm.ModifyResource()) - } + /* + if ocm.IsIntermediate(v.Repository().GetSpecification()) { + opts = append(opts, ocm.ModifyResource()) + } + */ return v.SetResource(meta, acc, opts...) } diff --git a/cmds/ocm/commands/ocmcmds/common/addhdlrs/srcs/elements.go b/cmds/ocm/commands/ocmcmds/common/addhdlrs/srcs/elements.go index 6ab83fca1d..9d8dced1dc 100644 --- a/cmds/ocm/commands/ocmcmds/common/addhdlrs/srcs/elements.go +++ b/cmds/ocm/commands/ocmcmds/common/addhdlrs/srcs/elements.go @@ -29,11 +29,11 @@ func New(opts ...options.Options) *ResourceSpecHandler { return &ResourceSpecHandler{addhdlrs.NewBase(opts...)} } -func (ResourceSpecHandler) Key() string { +func (*ResourceSpecHandler) Key() string { return "source" } -func (ResourceSpecHandler) RequireInputs() bool { +func (*ResourceSpecHandler) RequireInputs() bool { return true } diff --git a/cmds/ocm/commands/ocmcmds/components/hash/cmd.go b/cmds/ocm/commands/ocmcmds/components/hash/cmd.go index 43922b6ff0..b2ef1f9932 100644 --- a/cmds/ocm/commands/ocmcmds/components/hash/cmd.go +++ b/cmds/ocm/commands/ocmcmds/components/hash/cmd.go @@ -13,6 +13,7 @@ import ( "ocm.software/ocm/api/ocm" "ocm.software/ocm/api/ocm/compdesc" common "ocm.software/ocm/api/utils/misc" + "ocm.software/ocm/api/utils/out" "ocm.software/ocm/cmds/ocm/commands/common/options/closureoption" ocmcommon "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common" "ocm.software/ocm/cmds/ocm/commands/ocmcmds/common/handlers/comphdlr" @@ -180,17 +181,26 @@ func (h *action) Close() error { func (h *action) Out() error { if len(h.norms) > 1 { - dir := h.mode.outfile - dir = strings.TrimSuffix(dir, ".ncd") - err := h.ctx.FileSystem().Mkdir(dir, 0o755) - if err != nil { - return fmt.Errorf("cannot create output dir %s", dir) - } - for k, n := range h.norms { - p := filepath.Join(dir, k.String()) - err := h.write(p+".ncd", n) + if h.mode.outfile == "" || h.mode.outfile == "-" { + for _, n := range h.norms { + err := h.write(h.mode.outfile, n) + if err != nil { + return err + } + } + } else { + dir := h.mode.outfile + dir = strings.TrimSuffix(dir, ".ncd") + err := h.ctx.FileSystem().Mkdir(dir, 0o755) if err != nil { - return err + return fmt.Errorf("cannot create output dir %s", dir) + } + for k, n := range h.norms { + p := filepath.Join(dir, k.String()) + err := h.write(p+".ncd", n) + if err != nil { + return err + } } } } else { @@ -202,12 +212,17 @@ func (h *action) Out() error { } func (h *action) write(p, n string) error { - dir := filepath.Dir(p) - err := h.ctx.FileSystem().MkdirAll(dir, 0o755) - if err != nil { - return fmt.Errorf("cannot create dir %s", dir) + if p == "" || p == "-" { + out.Outln(h.ctx, n) + return nil + } else { + dir := filepath.Dir(p) + err := h.ctx.FileSystem().MkdirAll(dir, 0o755) + if err != nil { + return fmt.Errorf("cannot create dir %s", dir) + } + return vfs.WriteFile(h.ctx.FileSystem(), p, []byte(n), 0o644) } - return vfs.WriteFile(h.ctx.FileSystem(), p, []byte(n), 0o644) } ///////// diff --git a/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go b/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go index 7783820f74..f79e2c1c05 100644 --- a/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go +++ b/cmds/ocm/commands/ocmcmds/components/hash/cmd_test.go @@ -2,6 +2,8 @@ package hash_test import ( "bytes" + "crypto/sha256" + "encoding/hex" "fmt" . "github.com/mandelsoft/goutils/testutils" @@ -50,6 +52,48 @@ test.de/x v1 37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b `)) }) + It("normalize component archive v1", func() { + env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { + env.Provider(PROVIDER) + }) + + buf := bytes.NewBuffer(nil) + Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-O", "-", "-o", "norm")).To(Succeed()) + Expect(buf.String()).To(Equal(`[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}] +`)) + }) + + It("normalize component archive v2", func() { + env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { + env.Provider(PROVIDER) + }) + + buf := bytes.NewBuffer(nil) + Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-N", "jsonNormalisation/v2", "-o", "norm")).To(Succeed()) + Expect(buf.String()).To(StringEqualTrimmedWithContext(`{"component":{"componentReferences":[],"name":"test.de/x","provider":{"name":"mandelsoft"},"resources":[],"sources":[],"version":"v1"}} +`)) + }) + + It("check hash", func() { + env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { + env.Provider(PROVIDER) + }) + + buf := bytes.NewBuffer(nil) + Expect(env.CatchOutput(buf).Execute("hash", "components", ARCH, "-o", "yaml")).To(Succeed()) + Expect(buf.String()).To(StringEqualTrimmedWithContext(` +--- +component: test.de/x +context: [] +hash: 37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b2b +normalized: '[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}]' +version: v1 +`)) + + h := sha256.Sum256([]byte(`[{"component":[{"componentReferences":[]},{"name":"test.de/x"},{"provider":"mandelsoft"},{"resources":[]},{"version":"v1"}]},{"meta":[{"schemaVersion":"v2"}]}]`)) + Expect(hex.EncodeToString(h[:])).To(Equal("37f7f500d87f4b0a8765649f7c047db382e272b73e042805131df57279991b2b")) + }) + It("hash component archive with resources", func() { env.ComponentArchive(ARCH, accessio.FormatDirectory, COMP, VERSION, func() { env.Provider(PROVIDER) diff --git a/cmds/ocm/commands/ocmcmds/components/hash/options.go b/cmds/ocm/commands/ocmcmds/components/hash/options.go index d3165c732b..99346e88a2 100644 --- a/cmds/ocm/commands/ocmcmds/components/hash/options.go +++ b/cmds/ocm/commands/ocmcmds/components/hash/options.go @@ -33,7 +33,7 @@ func (o *Option) AddFlags(fs *pflag.FlagSet) { fs.BoolVarP(&o.Actual, "actual", "", false, "use actual component descriptor") fs.BoolVarP(&o.Update, "update", "U", false, "update digests in component version") fs.BoolVarP(&o.Verify, "verify", "V", false, "verify digests found in component version") - fs.StringVarP(&o.outfile, "outfile", "O", "norm.ncd", "Output file for normalized component descriptor") + fs.StringVarP(&o.outfile, "outfile", "O", "-", "Output file for normalized component descriptor") } func (o *Option) Complete(cmd *Command) error { diff --git a/docs/reference/ocm_add_componentversions.md b/docs/reference/ocm_add_componentversions.md index b95988d4ac..13ee9e84d0 100644 --- a/docs/reference/ocm_add_componentversions.md +++ b/docs/reference/ocm_add_componentversions.md @@ -26,6 +26,7 @@ componentversions, componentversion, cv, components, component, comps, comp, c -h, --help help for componentversions --lookup stringArray repository name or spec for closure lookup fallback -O, --output string output file for dry-run + -P, --preserve-signature preserve existing signatures -R, --replace replace existing elements -S, --scheme string schema version (default "v2") -s, --settings stringArray settings file with variable settings (yaml) diff --git a/docs/reference/ocm_add_references.md b/docs/reference/ocm_add_references.md index 6af5dfca9c..84e77acff8 100644 --- a/docs/reference/ocm_add_references.md +++ b/docs/reference/ocm_add_references.md @@ -20,6 +20,7 @@ references, reference, refs -F, --file string target file/directory (default "component-archive") -h, --help help for references -O, --output string output file for dry-run + -P, --preserve-signature preserve existing signatures -R, --replace replace existing elements -s, --settings stringArray settings file with variable settings (yaml) --templater string templater to use (go, none, spiff, subst) (default "subst") diff --git a/docs/reference/ocm_add_resources.md b/docs/reference/ocm_add_resources.md index 88985eff7b..253bf67cd7 100644 --- a/docs/reference/ocm_add_resources.md +++ b/docs/reference/ocm_add_resources.md @@ -20,6 +20,7 @@ resources, resource, res, r -F, --file string target file/directory (default "component-archive") -h, --help help for resources -O, --output string output file for dry-run + -P, --preserve-signature preserve existing signatures -R, --replace replace existing elements -s, --settings stringArray settings file with variable settings (yaml) --skip-digest-generation skip digest creation diff --git a/docs/reference/ocm_add_sources.md b/docs/reference/ocm_add_sources.md index 8426c5e600..26d54c0631 100644 --- a/docs/reference/ocm_add_sources.md +++ b/docs/reference/ocm_add_sources.md @@ -20,6 +20,7 @@ sources, source, src, s -F, --file string target file/directory (default "component-archive") -h, --help help for sources -O, --output string output file for dry-run + -P, --preserve-signature preserve existing signatures -R, --replace replace existing elements -s, --settings stringArray settings file with variable settings (yaml) --templater string templater to use (go, none, spiff, subst) (default "subst") diff --git a/docs/reference/ocm_hash_componentversions.md b/docs/reference/ocm_hash_componentversions.md index a00153f15a..561a9c16c9 100644 --- a/docs/reference/ocm_hash_componentversions.md +++ b/docs/reference/ocm_hash_componentversions.md @@ -22,7 +22,7 @@ componentversions, componentversion, cv, components, component, comps, comp, c --latest restrict component versions to latest --lookup stringArray repository name or spec for closure lookup fallback -N, --normalization string normalization algorithm (default "jsonNormalisation/v1") - -O, --outfile string Output file for normalized component descriptor (default "norm.ncd") + -O, --outfile string Output file for normalized component descriptor (default "-") -o, --output string output mode (JSON, json, norm, wide, yaml) -r, --recursive follow component reference nesting --repo string repository name or spec