Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package genericocireg

import (
"bytes"
"fmt"
"io"
"os"
"strings"
Expand All @@ -13,6 +14,7 @@ import (

"ocm.software/ocm/api/oci"
"ocm.software/ocm/api/oci/artdesc"
"ocm.software/ocm/api/oci/extensions/repositories/artifactset"
"ocm.software/ocm/api/ocm/cpi/accspeccpi"
"ocm.software/ocm/api/ocm/extensions/accessmethods/localblob"
"ocm.software/ocm/api/utils/blobaccess/blobaccess"
Expand Down Expand Up @@ -99,8 +101,15 @@ func (m *localBlobAccessMethod) getBlob() (blobaccess.DataAccess, error) {
err error
)
if len(refs) < 2 {
_, data, err = m.namespace.GetBlobData(digest.Digest(m.spec.LocalReference))
if err != nil {
if m.spec.MediaType == artdesc.MediaTypeImageIndex || m.spec.MediaType == artdesc.MediaTypeImageManifest {
// if we have a nested manifest or index, we can use the blob synthesis utility here to download
// the entire artifact set.
artblob, err := artifactset.SynthesizeArtifactBlob(m.namespace, m.spec.LocalReference)
if err != nil {
return nil, fmt.Errorf("failed to synthesize artifact blob: %w", err)
}
data = artblob
} else if _, data, err = m.namespace.GetBlobData(digest.Digest(m.spec.LocalReference)); err != nil {
return nil, err
}
} else {
Expand All @@ -126,7 +135,7 @@ func (m *localBlobAccessMethod) MimeType() string {
return m.spec.MediaType
}

////////////////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////////////

type composedBlock struct {
m *localBlobAccessMethod
Expand Down
96 changes: 74 additions & 22 deletions api/ocm/extensions/repositories/genericocireg/componentversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,38 +46,81 @@ func newComponentVersionAccess(mode accessobj.AccessMode, comp *componentAccessI
return &repocpi.ComponentVersionAccessInfo{c, true, persistent}, nil
}

////////////////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////////////

type ComponentVersionContainer struct {
bridge repocpi.ComponentVersionAccessBridge

comp *componentAccessImpl
version string
access oci.ArtifactAccess
manifest oci.ManifestAccess
state accessobj.State
comp *componentAccessImpl
version string
indexArtifact oci.ArtifactAccess
manifestArtifact oci.ArtifactAccess
manifest oci.ManifestAccess
index oci.IndexAccess
state accessobj.State
}

var _ repocpi.ComponentVersionAccessImpl = (*ComponentVersionContainer)(nil)

func newComponentVersionContainer(mode accessobj.AccessMode, comp *componentAccessImpl, version string, access oci.ArtifactAccess) (*ComponentVersionContainer, error) {
m := access.ManifestAccess()
if m == nil {
return nil, errors.ErrInvalid("artifact type")
var m oci.ManifestAccess
var i oci.IndexAccess

var manifestArtifact, indexArtifact oci.ArtifactAccess

if access.IsIndex() {
idx, err := access.Index()
if err != nil {
return nil, err
}
if len(idx.Manifests) < 1 {
return nil, fmt.Errorf("index has no manifests")
}
first := idx.Manifests[0]
firstArtifact, err := access.GetArtifact(first.Digest)
if err != nil {
return nil, err
}
if !firstArtifact.IsManifest() {
return nil, fmt.Errorf("first manifest in index is not a manifest")
}

m = firstArtifact.ManifestAccess()
manifestArtifact = firstArtifact
indexArtifact = access
} else {
m = access.ManifestAccess()
if m == nil {
return nil, fmt.Errorf("artifact is neither manifest nor index")
}

manifestArtifact = access
}
state, err := NewState(mode, comp.name, version, m, compatattr.Get(comp.GetContext()))

state, err := NewState(mode, comp.name, version, i, m, compatattr.Get(comp.GetContext()))
if err != nil {
access.Close()
err = errors.Join(err, manifestArtifact.Close())
if indexArtifact != nil {
err = errors.Join(err, indexArtifact.Close())
}
return nil, err
}

return &ComponentVersionContainer{
comp: comp,
version: version,
access: access,
manifest: m,
state: state,
}, nil
container := &ComponentVersionContainer{
comp: comp,
version: version,
manifestArtifact: manifestArtifact,
indexArtifact: indexArtifact,
manifest: m,
state: state,
}

if indexArtifact != nil {
// index based manifests are optional and only read based for next gen support
container.SetReadOnly()
}

return container, nil
}

func (c *ComponentVersionContainer) SetBridge(impl repocpi.ComponentVersionAccessBridge) {
Expand All @@ -89,11 +132,20 @@ func (c *ComponentVersionContainer) GetParentBridge() repocpi.ComponentAccessBri
}

func (c *ComponentVersionContainer) Close() error {
if c.manifest == nil {
if c.manifest == nil && c.manifestArtifact == nil {
return accessio.ErrClosed
}
c.manifest = nil
return c.access.Close()
c.index = nil

var err error
if c.indexArtifact != nil {
err = errors.Join(err, c.indexArtifact.Close())
}
if c.manifestArtifact != nil {
err = errors.Join(err, c.manifestArtifact.Close())
}
return err
}

func (c *ComponentVersionContainer) SetReadOnly() {
Expand Down Expand Up @@ -140,9 +192,9 @@ func (c *ComponentVersionContainer) AccessMethod(a cpi.AccessSpec, cv refmgmt.Ex

switch a.GetKind() {
case localblob.Type:
return newLocalBlobAccessMethod(accessSpec.(*localblob.AccessSpec), c.comp.namespace, c.access, cv)
return newLocalBlobAccessMethod(accessSpec.(*localblob.AccessSpec), c.comp.namespace, c.manifestArtifact, cv)
case localociblob.Type:
return newLocalOCIBlobAccessMethod(accessSpec.(*localblob.AccessSpec), c.comp.namespace, c.access, cv)
return newLocalOCIBlobAccessMethod(accessSpec.(*localblob.AccessSpec), c.comp.namespace, c.manifestArtifact, cv)
case relativeociref.Type:
m, err := ociartifact.NewMethod(c.GetContext(), a, accessSpec.(*relativeociref.AccessSpec).Reference, c.comp.repo.ocirepo)
if err == nil {
Expand Down
4 changes: 2 additions & 2 deletions api/ocm/extensions/repositories/genericocireg/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (h handler) Info(m cpi.ManifestAccess, config []byte) interface{} {
info := &ComponentVersionInfo{
Description: "component version",
}
acc := NewStateAccess(m)
acc := NewStateAccess(nil, m)
data, err := blobaccess.BlobData(acc.Get())
if err != nil {
info.Error = "cannot read component descriptor: " + err.Error()
Expand All @@ -46,7 +46,7 @@ func (h handler) Info(m cpi.ManifestAccess, config []byte) interface{} {

func (h handler) Description(pr common.Printer, m cpi.ManifestAccess, config []byte) {
pr.Printf("component version:\n")
acc := NewStateAccess(m)
acc := NewStateAccess(nil, m)
data, err := blobaccess.BlobData(acc.Get())
if err != nil {
pr.Printf(" cannot read component descriptor: %s\n", err.Error())
Expand Down
10 changes: 6 additions & 4 deletions api/ocm/extensions/repositories/genericocireg/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,25 @@ import (
ocmlog "ocm.software/ocm/api/utils/logging"
)

func NewState(mode accessobj.AccessMode, name, version string, access oci.ManifestAccess, compat ...bool) (accessobj.State, error) {
return accessobj.NewState(mode, NewStateAccess(access, compat...), NewStateHandler(name, version))
func NewState(mode accessobj.AccessMode, name, version string, parentIndex oci.IndexAccess, access oci.ManifestAccess, compat ...bool) (accessobj.State, error) {
return accessobj.NewState(mode, NewStateAccess(parentIndex, access, compat...), NewStateHandler(name, version))
}

// StateAccess handles the component descriptor persistence in an OCI Manifest.
type StateAccess struct {
parent oci.IndexAccess
access oci.ManifestAccess
layerMedia string
compat bool
}

var _ accessobj.StateAccess = (*StateAccess)(nil)

func NewStateAccess(access oci.ManifestAccess, compat ...bool) accessobj.StateAccess {
func NewStateAccess(parentIndex oci.IndexAccess, access oci.ManifestAccess, compat ...bool) accessobj.StateAccess {
return &StateAccess{
compat: utils.Optional(compat...),
access: access,
parent: parentIndex,
}
}

Expand Down Expand Up @@ -226,7 +228,7 @@ type ComponentDescriptorConfig struct {
ComponentDescriptorLayer *ociv1.Descriptor `json:"componentDescriptorLayer,omitempty"`
}

////////////////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////////////

// StateHandler handles the encoding of a component descriptor.
type StateHandler struct {
Expand Down
Loading