edge/pkg/bundle/tar_bundle.go

147 lines
2.7 KiB
Go

package bundle
import (
"archive/tar"
"compress/gzip"
"context"
"io"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
type TarBundle struct {
archivePath string
}
func (b *TarBundle) File(filename string) (io.ReadCloser, os.FileInfo, error) {
reader, archive, err := b.openArchive()
if err != nil {
return nil, nil, err
}
ctx := logger.With(
context.Background(),
logger.F("filename", filename),
)
logger.Debug(ctx, "opening file")
for {
header, err := reader.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, nil, errors.Wrap(err, "could not get next tar file")
}
p := strings.TrimPrefix(strings.TrimSuffix(header.Name, "/"), "./")
logger.Debug(ctx, "reading archive file", logger.F("path", p))
if filename != p {
continue
}
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeDir {
continue
}
rc := &archiveFile{reader, archive}
return rc, header.FileInfo(), nil
}
return nil, nil, os.ErrNotExist
}
func (b *TarBundle) Dir(dirname string) ([]os.FileInfo, error) {
reader, archive, err := b.openArchive()
if err != nil {
return nil, err
}
defer archive.Close()
files := make([]os.FileInfo, 0)
ctx := context.Background()
for {
header, err := reader.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, errors.Wrap(err, "could not get next tar file")
}
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeDir {
continue
}
if !strings.HasPrefix(header.Name, dirname) {
continue
}
relPath, err := filepath.Rel(dirname, header.Name)
if err != nil {
return nil, errors.Wrap(err, "could not get relative path")
}
logger.Debug(
ctx, "checking file prefix",
logger.F("dirname", dirname),
logger.F("filename", header.Name),
logger.F("relpath", relPath),
)
if relPath == filepath.Base(header.Name) {
files = append(files, header.FileInfo())
}
}
return files, nil
}
func (b *TarBundle) openArchive() (*tar.Reader, *os.File, error) {
f, err := os.Open(b.archivePath)
if err != nil {
return nil, nil, errors.Wrapf(err, "could not open '%v'", b.archivePath)
}
gzf, err := gzip.NewReader(f)
if err != nil {
return nil, nil, errors.Wrapf(err, "could not decompress '%v'", b.archivePath)
}
tr := tar.NewReader(gzf)
return tr, f, nil
}
func NewTarBundle(archivePath string) *TarBundle {
return &TarBundle{
archivePath: archivePath,
}
}
type archiveFile struct {
reader io.Reader
closer io.Closer
}
func (f *archiveFile) Read(p []byte) (n int, err error) {
return f.reader.Read(p)
}
func (f *archiveFile) Close() error {
return f.closer.Close()
}