remove custom file store in favor of less hacky IoContentWriter extended on top of existing file store

This commit is contained in:
Josh Wolf
2021-12-03 14:01:06 -07:00
parent c7ae551e6f
commit d55e7572e6
18 changed files with 64 additions and 317 deletions

View File

@@ -12,6 +12,7 @@ import (
"github.com/rancherfederal/hauler/internal/layer"
"github.com/rancherfederal/hauler/pkg/artifact"
"github.com/rancherfederal/hauler/pkg/consts"
)
type Client struct {
@@ -60,11 +61,6 @@ func (c *Client) LayerFrom(ctx context.Context, source string) (v1.Layer, error)
opener := func() (io.ReadCloser, error) {
return g.Open(ctx, u)
}
cfg := g.Config(u)
mt, err := cfg.MediaType()
if err != nil {
return nil, err
}
annotations := make(map[string]string)
annotations[ocispec.AnnotationTitle] = g.Name(u)
@@ -75,7 +71,7 @@ func (c *Client) LayerFrom(ctx context.Context, source string) (v1.Layer, error)
}
l, err := layer.FromOpener(opener,
layer.WithMediaType(string(mt)),
layer.WithMediaType(consts.FileLayerMediaType),
layer.WithAnnotations(annotations))
if err != nil {
return nil, err

View File

@@ -2,17 +2,13 @@ package mapper
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
ccontent "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/remotes"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"oras.land/oras-go/pkg/content"
@@ -38,6 +34,7 @@ func (s *store) Pusher(ctx context.Context, ref string) (remotes.Pusher, error)
hash = parts[1]
}
return &pusher{
store: s.File,
tag: tag,
ref: hash,
mapper: s.mapper,
@@ -49,142 +46,40 @@ type store struct {
mapper map[string]Fn
}
type pusher struct {
tag string
ref string
mapper map[string]Fn
}
func (s *pusher) Push(ctx context.Context, desc ocispec.Descriptor) (ccontent.Writer, error) {
now := time.Now()
// TODO: This is suuuuuper ugly... redo this when oras v2 is out
if _, ok := content.ResolveName(desc); ok {
p, err := s.store.Pusher(ctx, s.ref)
if err != nil {
return nil, err
}
return p.Push(ctx, desc)
}
// If no custom mapper found, fall back to content.File mapper
if _, ok := s.mapper[desc.MediaType]; !ok {
return content.NewIoContentWriter(ioutil.Discard, content.WithOutputHash(desc.Digest)), nil
}
f, err := s.mapper[desc.MediaType](desc)
filename, err := s.mapper[desc.MediaType](desc)
if err != nil {
return nil, err
}
return &fileWriter{
file: f,
digester: digest.Canonical.Digester(),
status: ccontent.Status{
Ref: f.Name(),
Total: desc.Size,
StartedAt: now,
UpdatedAt: now,
},
aftercommit: nil,
}, nil
}
type fileWriter struct {
// store *content.File
file *os.File
digester digest.Digester
status ccontent.Status
aftercommit func() error
}
// NewFileWriter will open a new file for writing, existing file will be truncated
func NewFileWriter(path string, perm os.FileMode, size int64) (*fileWriter, error) {
now := time.Now()
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, perm)
fullFileName := filepath.Join(s.store.ResolvePath(""), filename)
// TODO: Don't rewrite everytime, we can check the digest
f, err := os.OpenFile(fullFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "pushing file")
}
return &fileWriter{
file: f,
digester: digest.Canonical.Digester(),
status: ccontent.Status{
Ref: f.Name(),
Total: size,
StartedAt: now,
UpdatedAt: now,
},
}, nil
w := content.NewIoContentWriter(f, content.WithInputHash(desc.Digest), content.WithOutputHash(desc.Digest))
return w, nil
}
func (w *fileWriter) Write(p []byte) (n int, err error) {
n, err = w.file.Write(p)
w.digester.Hash().Write(p[:n])
w.status.Offset += int64(len(p))
w.status.UpdatedAt = time.Now()
return n, err
}
func (w *fileWriter) Close() error {
if w.file == nil {
return nil
}
w.file.Sync()
err := w.file.Close()
w.file = nil
return err
}
func (w *fileWriter) Digest() digest.Digest {
return w.digester.Digest()
}
func (w *fileWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...ccontent.Opt) error {
var base ccontent.Info
for _, opt := range opts {
if err := opt(&base); err != nil {
return err
}
}
if w.file == nil {
return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot commit on closed writer")
}
file := w.file
w.file = nil
if err := file.Sync(); err != nil {
file.Close()
return errors.Wrap(err, "sync failed")
}
fileInfo, err := file.Stat()
if err != nil {
file.Close()
return errors.Wrap(err, "stat failed")
}
if err := file.Close(); err != nil {
return errors.Wrap(err, "failed to close file")
}
if size > 0 && size != fileInfo.Size() {
return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit size %d, expected %d", fileInfo.Size(), size)
}
if dgst := w.digester.Digest(); expected != "" && expected != dgst {
return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit digest %s, expected %s", dgst, expected)
}
// w.store.set(w.desc)
if w.aftercommit != nil {
return w.aftercommit()
}
return nil
}
func (w *fileWriter) Status() (ccontent.Status, error) {
return w.status, nil
}
func (w *fileWriter) Truncate(size int64) error {
if size != 0 {
return content.ErrUnsupportedSize
}
w.status.Offset = 0
w.digester.Hash().Reset()
if _, err := w.file.Seek(0, io.SeekStart); err != nil {
return err
}
return w.file.Truncate(0)
type pusher struct {
store *content.File
tag string
ref string
mapper map[string]Fn
}

View File

@@ -2,38 +2,35 @@ package mapper
import (
"fmt"
"os"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/rancherfederal/hauler/pkg/consts"
)
type Fn func(desc ocispec.Descriptor) (*os.File, error)
type Fn func(desc ocispec.Descriptor) (string, error)
func Images() map[string]Fn {
m := make(map[string]Fn)
manifestMapperFn := Fn(func(desc ocispec.Descriptor) (*os.File, error) {
return os.Create("manifest.json")
manifestMapperFn := Fn(func(desc ocispec.Descriptor) (string, error) {
return "manifest.json", nil
})
for _, l := range []string{consts.DockerManifestSchema2, consts.OCIManifestSchema1} {
m[l] = manifestMapperFn
}
layerMapperFn := Fn(func(desc ocispec.Descriptor) (*os.File, error) {
n := fmt.Sprintf("%s.tar.gz", desc.Digest.String())
return os.Create(n)
layerMapperFn := Fn(func(desc ocispec.Descriptor) (string, error) {
return fmt.Sprintf("%s.tar.gz", desc.Digest.String()), nil
})
for _, l := range []string{consts.OCILayer, consts.DockerLayer} {
m[l] = layerMapperFn
}
configMapperFn := Fn(func(desc ocispec.Descriptor) (*os.File, error) {
return os.Create("config.json")
configMapperFn := Fn(func(desc ocispec.Descriptor) (string, error) {
return "config.json", nil
})
for _, l := range []string{consts.DockerConfigJSON} {
@@ -43,34 +40,19 @@ func Images() map[string]Fn {
return m
}
func Files() map[string]Fn {
m := make(map[string]Fn)
blobMapperFn := Fn(func(desc ocispec.Descriptor) (*os.File, error) {
fmt.Println(desc.Annotations)
if _, ok := desc.Annotations[ocispec.AnnotationTitle]; !ok {
return nil, errors.Errorf("unkown file name")
}
return os.Create(desc.Annotations[ocispec.AnnotationTitle])
})
m[consts.FileLayerMediaType] = blobMapperFn
return m
}
func Chart() map[string]Fn {
m := make(map[string]Fn)
chartMapperFn := Fn(func(desc ocispec.Descriptor) (*os.File, error) {
chartMapperFn := Fn(func(desc ocispec.Descriptor) (string, error) {
f := "chart.tar.gz"
if _, ok := desc.Annotations[ocispec.AnnotationTitle]; ok {
f = desc.Annotations[ocispec.AnnotationTitle]
}
return os.Create(f)
return f, nil
})
provMapperFn := Fn(func(desc ocispec.Descriptor) (*os.File, error) {
return os.Create("prov.json")
provMapperFn := Fn(func(desc ocispec.Descriptor) (string, error) {
return "prov.json", nil
})
m[consts.ChartLayerMediaType] = chartMapperFn