Skip to content

Commit

Permalink
Merge pull request #3284 from dtrudg/pick-artifact-fixes
Browse files Browse the repository at this point in the history
Pick artifact fixes to release-4.2
  • Loading branch information
dtrudg authored Sep 4, 2024
2 parents 7563ce1 + 2feee0c commit 9c1fb3f
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 15 deletions.
21 changes: 11 additions & 10 deletions internal/pkg/ocisif/datacontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@ import (

// ConfigMediaType custom media type.
const (
DataContainerConfigMediaType types.MediaType = "application/vnd.sylabs.data-container.config.v1+json"
DataContainerArtifactType string = "application/vnd.sylabs.data-container.v1"
EmptyConfigMediaType types.MediaType = "application/vnd.oci.empty.v1+json"
)

// Placeholder config - will become empty JSON in written image
type DataContainerConfig struct{}

// WriteDataContainerFromPath takes a path to a directory or regular file, and writes
// a data container image populated with the directory/file to dest, as an OCI-SIF.
func WriteDataContainerFromPath(path string, dst string, workDir string) error {
img, err := newDataContainerFromFSPath(os.DirFS(filepath.Dir(path)), filepath.Base(path), DataContainerConfig{})
img, err := newDataContainerFromFSPath(os.DirFS(filepath.Dir(path)), filepath.Base(path))
if err != nil {
return err
}
w, err := NewImageWriter(img, dst, workDir, WithSquashFSLayers(true))
w, err := NewImageWriter(img, dst, workDir,
WithSquashFSLayers(true),
WithArtifactType(DataContainerArtifactType),
)
if err != nil {
return err
}
Expand All @@ -47,7 +48,7 @@ func WriteDataContainerFromPath(path string, dst string, workDir string) error {

// newDataContainerFromFSPath takes a path to a directory or regular file within fsys, and returns
// a data container image populated with the directory/file.
func newDataContainerFromFSPath(fsys fs.FS, path string, cfg DataContainerConfig) (ggcrv1.Image, error) {
func newDataContainerFromFSPath(fsys fs.FS, path string) (ggcrv1.Image, error) {
fi, err := fs.Stat(fsys, path)
if err != nil {
return nil, err
Expand Down Expand Up @@ -75,7 +76,7 @@ func newDataContainerFromFSPath(fsys fs.FS, path string, cfg DataContainerConfig
return nil, err
}

return createDataContainerFromLayer(l, cfg)
return createDataContainerFromLayer(l)
}

// tarOpener adapts a tarWriter to a tarball.Opener, in a way that is safe for concurrent use, as
Expand All @@ -96,7 +97,7 @@ func tarOpener(fn tarWriterFunc) tarball.Opener {
}

// createDataContainerFromLayer create OCI datacontainer from the supplied v1.Layer.
func createDataContainerFromLayer(layer ggcrv1.Layer, cfg DataContainerConfig) (ggcrv1.Image, error) {
func createDataContainerFromLayer(layer ggcrv1.Layer) (ggcrv1.Image, error) {
img := ocimutate.MediaType(empty.Image, types.OCIManifestSchema1)

img, err := ocimutate.AppendLayers(img, layer)
Expand All @@ -105,7 +106,7 @@ func createDataContainerFromLayer(layer ggcrv1.Layer, cfg DataContainerConfig) (
}

return mutate.Apply(img,
mutate.SetConfig(cfg, DataContainerConfigMediaType),
mutate.SetConfig(struct{}{}, types.MediaType(EmptyConfigMediaType)),
)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/ocisif/datacontainer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func Test_newDataContainerFromFSPath(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
img, err := newDataContainerFromFSPath(tt.fsys, tt.path, DataContainerConfig{})
img, err := newDataContainerFromFSPath(tt.fsys, tt.path)
if err != nil {
t.Fatal(err)
}
Expand Down
87 changes: 87 additions & 0 deletions internal/pkg/ocisif/imagewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
package ocisif

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"path/filepath"
"time"

ggcrv1 "github.com/google/go-containerregistry/pkg/v1"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
ggcrmutate "github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/tarball"
Expand Down Expand Up @@ -43,6 +46,7 @@ type ImageWriter struct {
srcDigest ggcrv1.Hash
squashLayers bool
squashFSLayers bool
artifactType string
workDir string
}

Expand All @@ -64,6 +68,15 @@ func WithSquashFSLayers(v bool) ImageWriterOpt {
}
}

// WithArtifactType says the image should be written with the artifactType field defined in the OCI
// v1.1.0 specification to v.
func WithArtifactType(v string) ImageWriterOpt {
return func(w *ImageWriter) error {
w.artifactType = v
return nil
}
}

var (
errNoDestProvided = errors.New("no destination file provided")
errNoWorkDirProvided = errors.New("no workDir for intermediate files provided")
Expand Down Expand Up @@ -142,6 +155,15 @@ func (w *ImageWriter) Write() error {
}
}

if w.artifactType != "" {
// GGCR does not yet support OCI v1.1 artifacts, so wrap our image to handle that in the
// meantime.
img = &oci11Artifact{
Image: img,
artifactType: w.artifactType,
}
}

ii := ggcrmutate.AppendManifests(empty.Index, ggcrmutate.IndexAddendum{
Add: img,
})
Expand Down Expand Up @@ -248,3 +270,68 @@ func imgLayersToSquashfs(img ggcrv1.Image, digest ggcrv1.Hash, workDir string) (

return sqfsImage, nil
}

// oci11Artifact adapts the base image to comply with the OCI v1.1 artifact specification.
type oci11Artifact struct {
v1.Image
artifactType string
}

// Size returns the size of the manifest.
func (w *oci11Artifact) Size() (int64, error) {
mf, err := w.RawManifest()
if err != nil {
return 0, err
}

return int64(len(mf)), nil
}

// Digest returns the sha256 of this image's manifest.
func (w *oci11Artifact) Digest() (v1.Hash, error) {
mf, err := w.RawManifest()
if err != nil {
return v1.Hash{}, err
}

h, _, err := v1.SHA256(bytes.NewReader(mf))
if err != nil {
return v1.Hash{}, err
}
return h, nil
}

// RawManifest returns the serialized bytes of Manifest().
func (w *oci11Artifact) RawManifest() ([]byte, error) {
mf, err := w.Image.RawManifest()
if err != nil {
return nil, err
}

var manifest struct {
SchemaVersion int64 `json:"schemaVersion"`
MediaType types.MediaType `json:"mediaType,omitempty"`
ArtifactType string `json:"artifactType,omitempty"`
Config v1.Descriptor `json:"config"`
Layers []v1.Descriptor `json:"layers"`
Annotations map[string]string `json:"annotations,omitempty"`
Subject *v1.Descriptor `json:"subject,omitempty"`
}
if err := json.Unmarshal(mf, &manifest); err != nil {
return nil, fmt.Errorf("unmarshal OCI v1.1 manifest: %w", err)
}

// Otherwise, set artifactType based on the config mediaType.
manifest.ArtifactType = w.artifactType

mf, err = json.Marshal(manifest)
if err != nil {
return nil, fmt.Errorf("marshal OCI v1.1 manifest: %w", err)
}
return mf, nil
}

// ArtifactType returns the artifact type.
func (w *oci11Artifact) ArtifactType() (string, error) {
return w.artifactType, nil
}
7 changes: 7 additions & 0 deletions internal/pkg/ocisif/imagewriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ func TestWrite(t *testing.T) {
workDir: tmpDir,
opts: []ImageWriterOpt{WithSquash(true), WithSquashFSLayers(true)},
},
{
name: "ArtifactType",
srcImg: tImg,
dest: filepath.Join(tmpDir, "default.oci.sif"),
workDir: tmpDir,
opts: []ImageWriterOpt{WithArtifactType(DataContainerArtifactType)},
},
}

for _, tt := range tests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.sylabs.data-container.config.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":118,"digest":"sha256:8519aae926249001411c6ed55b789090fb0011642cbacdd2ef09393473c763e4"}]}
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":118,"digest":"sha256:8519aae926249001411c6ed55b789090fb0011642cbacdd2ef09393473c763e4"}]}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.sylabs.data-container.config.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":160,"digest":"sha256:3d4af439a0003c0190f0cac9868e78561fef1adedd2eb155252579ea734b0217"}]}
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":160,"digest":"sha256:3d4af439a0003c0190f0cac9868e78561fef1adedd2eb155252579ea734b0217"}]}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.sylabs.data-container.config.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":192,"digest":"sha256:d8c17ac3eb003ba5991246d5e5b11c25de17ede79fff279ed8a56e2856202a13"}]}
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":192,"digest":"sha256:d8c17ac3eb003ba5991246d5e5b11c25de17ede79fff279ed8a56e2856202a13"}]}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.sylabs.data-container.config.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":120,"digest":"sha256:2b4dbdae6bcf92979a3913c564f4249bb0df7d61b8da1700c5da23990625e2d7"}]}
{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.empty.v1+json","size":2,"digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","size":120,"digest":"sha256:2b4dbdae6bcf92979a3913c564f4249bb0df7d61b8da1700c5da23990625e2d7"}]}

0 comments on commit 9c1fb3f

Please sign in to comment.