mirror of
https://github.com/hauler-dev/hauler.git
synced 2026-02-14 09:59:50 +00:00
remove custom file store in favor of less hacky IoContentWriter extended on top of existing file store
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user