Compare commits

..

2 Commits

Author SHA1 Message Date
d359d8bfab fix the handling of compressed files: ensure the gzip writer is closed before the file
Some checks failed
Lint / golangci-lint (push) Failing after 10m55s
Declarative Tests / test (push) Failing after 33s
2024-10-03 23:34:27 +00:00
a34f83e684 add close injector to allow auto-closing the wrapped writers 2024-10-03 23:31:53 +00:00
6 changed files with 103 additions and 10 deletions

31
internal/ext/addcloser.go Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
package ext
import (
"io"
)
type Closer func() error
func WriteAddCloser(w io.WriteCloser, c Closer) io.WriteCloser {
a := writeAddCloser{ WriteCloser: w, AddClose: func() (err error) {
if err = w.Close(); err != nil {
return
}
if c != nil {
return c()
}
return
} }
return a
}
type writeAddCloser struct {
io.WriteCloser
AddClose Closer
}
func (w writeAddCloser) Close() error {
return w.AddClose()
}

View File

@ -0,0 +1,19 @@
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
package ext
import (
"testing"
"github.com/stretchr/testify/assert"
"strings"
_ "fmt"
_ "log"
)
func TestNewWriteAddCloser(t *testing.T) {
var testWriter strings.Builder
closer := WriteAddCloser(WriteNopCloser(&testWriter), func() error { testWriter.Write([]byte("foo")); return nil })
closer.Close()
assert.Equal(t, "foo", testWriter.String())
}

View File

@ -13,6 +13,7 @@ _ "errors"
"fmt" "fmt"
"compress/gzip" "compress/gzip"
"log/slog" "log/slog"
"decl/internal/ext"
) )
type File struct { type File struct {
@ -98,6 +99,7 @@ func NewFileWriter(u *url.URL) (f *FileWriter, err error) {
} }
f.extension() f.extension()
f.DetectGzip() f.DetectGzip()
slog.Info("transport.NewFileWriter()", "file", f.File)
exists := FileExists(u) exists := FileExists(u)
slog.Info("transport.NewFileWriter()", "file", f, "error", err, "exists", exists) slog.Info("transport.NewFileWriter()", "file", f, "error", err, "exists", exists)
@ -110,7 +112,7 @@ func NewFileWriter(u *url.URL) (f *FileWriter, err error) {
} }
} }
if f.Gzip() { if f.Gzip() {
f.gzipWriter = gzip.NewWriter(f.writeHandle) f.gzipWriter = ext.WriteAddCloser(gzip.NewWriter(f.writeHandle), func() error { return f.writeHandle.Close() })
} }
slog.Info("transport.NewFileWriter()", "file", f, "error", err) slog.Info("transport.NewFileWriter()", "file", f, "error", err)
return return
@ -139,7 +141,7 @@ func NewFile(u *url.URL) (f *File, err error) {
} }
if f.Gzip() { if f.Gzip() {
f.gzipWriter = gzip.NewWriter(f.writeHandle) f.gzipWriter = ext.WriteAddCloser(gzip.NewWriter(f.writeHandle), func() error { return f.writeHandle.Close() })
if exists { if exists {
if f.gzipReader, err = gzip.NewReader(f.readHandle); err != nil { if f.gzipReader, err = gzip.NewReader(f.readHandle); err != nil {
return return
@ -164,7 +166,7 @@ func (f *File) extension() {
} }
func (f *File) DetectGzip() { func (f *File) DetectGzip() {
f.gzip = (f.uri.Query().Get("gzip") == "true" || f.fileext == "gz") f.gzip = (f.uri.Query().Get("gzip") == "true" || f.fileext == "gz" || f.exttype == "tgz" || f.exttype == "gz" || f.fileext == "tgz")
} }
func (f *File) URI() *url.URL { func (f *File) URI() *url.URL {

View File

@ -7,8 +7,11 @@ import (
"testing" "testing"
"fmt" "fmt"
"os" "os"
"io"
"net/url" "net/url"
"path/filepath" "path/filepath"
"compress/gzip"
"strings"
) )
var TransportFileTestFile = fmt.Sprintf("%s/foo", TempDir) var TransportFileTestFile = fmt.Sprintf("%s/foo", TempDir)
@ -37,3 +40,35 @@ func TestNewTransportFileReaderExtension(t *testing.T) {
f.extension() f.extension()
assert.Equal(t, f.exttype, "yaml") assert.Equal(t, f.exttype, "yaml")
} }
// Use transport to write a compressed file then verify it.
func TestTransportFileGzipWriter(t *testing.T) {
var content strings.Builder
var testValue string = "write a compressed plaintext file"
path := TempDir.FilePath("foo.tgz")
u, e := url.Parse(fmt.Sprintf("file://%s", path))
assert.Nil(t, e)
fw, err := NewFileWriter(u)
assert.Nil(t, err)
assert.Equal(t, "tgz", fw.File.exttype)
assert.True(t, fw.Gzip())
writer := fw.Writer()
writer.Write([]byte(testValue))
assert.Nil(t, writer.Close())
assert.True(t, TempDir.FileExists("foo.tgz"))
r, openErr := TempDir.Open("foo.tgz")
assert.Nil(t, openErr)
gzipReader, gzipErr := gzip.NewReader(r)
assert.Nil(t, gzipErr)
_, readErr := io.Copy(&content, gzipReader)
assert.Nil(t, readErr)
assert.Equal(t, content.String(), testValue)
}

View File

@ -19,6 +19,7 @@ type Handler interface {
URI() *url.URL URI() *url.URL
ContentType() string ContentType() string
SetGzip(bool) SetGzip(bool)
DetectGzip()
Gzip() bool Gzip() bool
Signature() string Signature() string
Stat() (fs.FileInfo, error) Stat() (fs.FileInfo, error)
@ -138,6 +139,8 @@ func (r *Reader) SetGzip(value bool) {
r.handle.SetGzip(value) r.handle.SetGzip(value)
} }
func (r *Reader) DetectGzip() { r.handle.DetectGzip() }
func (r *Reader) Gzip() bool { func (r *Reader) Gzip() bool {
return r.handle.Gzip() return r.handle.Gzip()
} }
@ -196,6 +199,8 @@ func (w *Writer) SetGzip(value bool) {
w.handle.SetGzip(value) w.handle.SetGzip(value)
} }
func (w *Writer) DetectGzip() { w.handle.DetectGzip() }
func (w *Writer) Gzip() bool { func (w *Writer) Gzip() bool {
return w.handle.Gzip() return w.handle.Gzip()
} }

View File

@ -9,9 +9,10 @@ import (
"fmt" "fmt"
"os" "os"
"log" "log"
"decl/internal/tempdir"
) )
var TempDir string var TempDir tempdir.Path = "testtransportfile"
var testFileResourceDoc string = ` var testFileResourceDoc string = `
resources: resources:
@ -23,19 +24,19 @@ resources:
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
var err error var err error
TempDir, err = os.MkdirTemp("", "testtransportfile") err = TempDir.Create()
if err != nil || TempDir == "" { if err != nil || TempDir == "" {
log.Fatal(err) log.Fatal(err)
} }
rc := m.Run() rc := m.Run()
os.RemoveAll(TempDir) TempDir.Remove()
os.Exit(rc) os.Exit(rc)
} }
func TestNewTransportReader(t *testing.T) { func TestNewTransportReader(t *testing.T) {
path := fmt.Sprintf("%s/foo", TempDir) path := TempDir.FilePath("foo")
u, e := url.Parse(fmt.Sprintf("file://%s", path)) u, e := url.Parse(fmt.Sprintf("file://%s", path))
assert.Nil(t, e) assert.Nil(t, e)
@ -48,7 +49,7 @@ func TestNewTransportReader(t *testing.T) {
} }
func TestTransportReaderContentType(t *testing.T) { func TestTransportReaderContentType(t *testing.T) {
path := fmt.Sprintf("%s/foo.jx.yaml", TempDir) path := TempDir.FilePath("foo.jx.yaml")
u, e := url.Parse(fmt.Sprintf("file://%s", path)) u, e := url.Parse(fmt.Sprintf("file://%s", path))
assert.Nil(t, e) assert.Nil(t, e)
@ -66,7 +67,7 @@ func TestTransportReaderContentType(t *testing.T) {
} }
func TestTransportReaderDir(t *testing.T) { func TestTransportReaderDir(t *testing.T) {
u, e := url.Parse(fmt.Sprintf("file://%s", TempDir)) u, e := url.Parse(fmt.Sprintf("file://%s", string(TempDir)))
assert.Nil(t, e) assert.Nil(t, e)
reader, err := NewReader(u) reader, err := NewReader(u)
@ -76,7 +77,7 @@ func TestTransportReaderDir(t *testing.T) {
} }
func TestTransportWriter(t *testing.T) { func TestTransportWriter(t *testing.T) {
path := fmt.Sprintf("%s/writefoo", TempDir) path := TempDir.FilePath("writefoo")
u, e := url.Parse(fmt.Sprintf("file://%s", path)) u, e := url.Parse(fmt.Sprintf("file://%s", path))
assert.Nil(t, e) assert.Nil(t, e)