mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-16 20:32:13 +01:00
fix: LibreOffice newer versions stability (#697)
This commit is contained in:
@@ -158,7 +158,6 @@ func (cmd Cmd) pipeOutput() error {
|
||||
|
||||
for {
|
||||
line, _, err := r.ReadLine()
|
||||
|
||||
if err != nil {
|
||||
if err != io.EOF && !strings.Contains(err.Error(), "file already closed") {
|
||||
logger.Error(fmt.Sprintf("pipe unix process output error: %s", err))
|
||||
|
||||
@@ -47,7 +47,6 @@ func (ctx Context) ParsedFlags() ParsedFlags {
|
||||
// initializes it. Otherwise, returns the already initialized instance.
|
||||
func (ctx *Context) Module(kind interface{}) (interface{}, error) {
|
||||
mods, err := ctx.Modules(kind)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get module: %w", err)
|
||||
}
|
||||
|
||||
@@ -7,24 +7,44 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// TmpPath returns the default directory to use for temporary files and
|
||||
// directories. Most if not all files and directories created by the
|
||||
// application and its dependencies must be based on this default directory.
|
||||
func TmpPath() string {
|
||||
return os.TempDir()
|
||||
// FileSystem provides utilities for managing temporary directories. It creates
|
||||
// unique directory names based on UUIDs to ensure isolation of temporary files
|
||||
// for different modules.
|
||||
type FileSystem struct {
|
||||
workingDir string
|
||||
}
|
||||
|
||||
// NewDirPath returns a random absolute path based on the temporary path.
|
||||
func NewDirPath() string {
|
||||
return fmt.Sprintf("%s/%s", TmpPath(), uuid.New())
|
||||
// NewFileSystem initializes a new FileSystem instance with a unique working
|
||||
// directory.
|
||||
func NewFileSystem() *FileSystem {
|
||||
return &FileSystem{
|
||||
workingDir: uuid.NewString(),
|
||||
}
|
||||
}
|
||||
|
||||
// MkdirAll creates a random directory based on the temporary path and
|
||||
// returns its absolute path.
|
||||
func MkdirAll() (string, error) {
|
||||
path := NewDirPath()
|
||||
// WorkingDir returns the unique name of the working directory.
|
||||
func (fs *FileSystem) WorkingDir() string {
|
||||
return fs.workingDir
|
||||
}
|
||||
|
||||
err := os.MkdirAll(path, 0755)
|
||||
// WorkingDirPath constructs and returns the full path to the working directory
|
||||
// inside the system's temporary directory.
|
||||
func (fs *FileSystem) WorkingDirPath() string {
|
||||
return fmt.Sprintf("%s/%s", os.TempDir(), fs.workingDir)
|
||||
}
|
||||
|
||||
// NewDirPath generates a new unique path for a directory inside the working
|
||||
// directory.
|
||||
func (fs *FileSystem) NewDirPath() string {
|
||||
return fmt.Sprintf("%s/%s", fs.WorkingDirPath(), uuid.NewString())
|
||||
}
|
||||
|
||||
// MkdirAll creates a new unique directory inside the working directory and
|
||||
// returns its path. If the directory creation fails, an error is returned.
|
||||
func (fs *FileSystem) MkdirAll() (string, error) {
|
||||
path := fs.NewDirPath()
|
||||
|
||||
err := os.MkdirAll(path, 0o755)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("create directory %s: %w", path, err)
|
||||
}
|
||||
|
||||
@@ -1,59 +1,55 @@
|
||||
package gotenberg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTmpPath(t *testing.T) {
|
||||
osTempDir := os.TempDir()
|
||||
tmpPath := TmpPath()
|
||||
func TestFileSystem_WorkingDir(t *testing.T) {
|
||||
fs := NewFileSystem()
|
||||
dirName := fs.WorkingDir()
|
||||
|
||||
if tmpPath != osTempDir {
|
||||
t.Errorf("expected path '%s' but got '%s'", osTempDir, tmpPath)
|
||||
if dirName == "" {
|
||||
t.Error("expected directory name but got empty string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDirPath(t *testing.T) {
|
||||
newDirPath := NewDirPath()
|
||||
tmpPath := TmpPath()
|
||||
func TestFileSystem_WorkingDirPath(t *testing.T) {
|
||||
fs := NewFileSystem()
|
||||
expectedPath := fmt.Sprintf("%s/%s", os.TempDir(), fs.WorkingDir())
|
||||
|
||||
if !strings.HasPrefix(newDirPath, tmpPath) {
|
||||
t.Fatalf("expected path '%s' to start with '%s'", newDirPath, tmpPath)
|
||||
}
|
||||
|
||||
newDirPaths := make([]string, 1000)
|
||||
for i := range newDirPaths {
|
||||
newDirPaths[i] = NewDirPath()
|
||||
}
|
||||
|
||||
for i, newDirPath := range newDirPaths {
|
||||
for j, comparison := range newDirPaths {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
|
||||
if newDirPath == comparison {
|
||||
t.Fatalf("expected path '%s' (index %d) to be unique, but found an identical path on index %d", newDirPath, i, j)
|
||||
}
|
||||
}
|
||||
if fs.WorkingDirPath() != expectedPath {
|
||||
t.Errorf("expected path '%s' but got '%s'", expectedPath, fs.WorkingDirPath())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMkdirAll(t *testing.T) {
|
||||
path, err := MkdirAll()
|
||||
func TestFileSystem_NewDirPath(t *testing.T) {
|
||||
fs := NewFileSystem()
|
||||
newDir := fs.NewDirPath()
|
||||
expectedPrefix := fs.WorkingDirPath()
|
||||
|
||||
if !strings.HasPrefix(newDir, expectedPrefix) {
|
||||
t.Errorf("expected new directory to start with '%s' but got '%s'", expectedPrefix, newDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSystem_MkdirAll(t *testing.T) {
|
||||
fs := NewFileSystem()
|
||||
|
||||
newPath, err := fs.MkdirAll()
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error but got: %v", err)
|
||||
}
|
||||
|
||||
tmpPath := TmpPath()
|
||||
if !strings.HasPrefix(path, tmpPath) {
|
||||
t.Fatalf("expected path '%s' to start with '%s'", path, tmpPath)
|
||||
_, err = os.Stat(newPath)
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("expected directory '%s' to exist but it doesn't", newPath)
|
||||
}
|
||||
|
||||
_, err = os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("expected path '%s' to exist but got: %v", path, err)
|
||||
err = os.RemoveAll(fs.WorkingDirPath())
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error while cleaning up but got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
53
pkg/gotenberg/gc.go
Normal file
53
pkg/gotenberg/gc.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package gotenberg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// GarbageCollect scans the root path and deletes files or directories with
|
||||
// names containing specific substrings.
|
||||
func GarbageCollect(logger *zap.Logger, rootPath string, includeSubstr []string) error {
|
||||
logger = logger.Named("gc")
|
||||
|
||||
// To make sure that the next Walk method stays on
|
||||
// the root level of the considered path, we have to
|
||||
// return a filepath.SkipDir error if the current path
|
||||
// is a directory.
|
||||
skipDirOrNil := func(info os.FileInfo) error {
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return filepath.Walk(rootPath, func(path string, info os.FileInfo, pathErr error) error {
|
||||
if pathErr != nil {
|
||||
return pathErr
|
||||
}
|
||||
|
||||
if path == rootPath {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, substr := range includeSubstr {
|
||||
if strings.Contains(info.Name(), substr) || path == substr {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("garbage collect '%s': %w", path, err)
|
||||
}
|
||||
|
||||
logger.Debug(fmt.Sprintf("'%s' removed", path))
|
||||
|
||||
return skipDirOrNil(info)
|
||||
}
|
||||
}
|
||||
|
||||
return skipDirOrNil(info)
|
||||
})
|
||||
}
|
||||
97
pkg/gotenberg/gc_test.go
Normal file
97
pkg/gotenberg/gc_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package gotenberg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestGarbageCollect(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
scenario string
|
||||
rootPath string
|
||||
includeSubstr []string
|
||||
expectErr bool
|
||||
expectNotExists []string
|
||||
expectExists []string
|
||||
}{
|
||||
{
|
||||
scenario: "root path does not exist",
|
||||
rootPath: uuid.NewString(),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
scenario: "remove include substrings",
|
||||
rootPath: func() string {
|
||||
path := fmt.Sprintf("%s/a_directory", os.TempDir())
|
||||
|
||||
err := os.MkdirAll(path, 0o755)
|
||||
if err != nil {
|
||||
t.Fatalf(fmt.Sprintf("expected no error but got: %v", err))
|
||||
}
|
||||
|
||||
err = os.WriteFile(fmt.Sprintf("%s/a_foo_file", path), []byte{1}, 0o755)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error but got: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(fmt.Sprintf("%s/a_bar_file", path), []byte{1}, 0o755)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error but got: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(fmt.Sprintf("%s/a_baz_file", path), []byte{1}, 0o755)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error but got: %v", err)
|
||||
}
|
||||
|
||||
return path
|
||||
}(),
|
||||
includeSubstr: []string{"foo", fmt.Sprintf("%s/a_directory/a_bar_file", os.TempDir())},
|
||||
expectExists: []string{"a_baz_file"},
|
||||
expectNotExists: []string{"a_foo_file", "a_bar_file"},
|
||||
},
|
||||
} {
|
||||
func() {
|
||||
defer func() {
|
||||
err := os.RemoveAll(tc.rootPath)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: expected no error while cleaning up but got: %v", tc.scenario, err)
|
||||
}
|
||||
}()
|
||||
|
||||
err := GarbageCollect(zap.NewNop(), tc.rootPath, tc.includeSubstr)
|
||||
|
||||
if !tc.expectErr && err != nil {
|
||||
t.Fatalf("%s: expected no error but got: %v", tc.scenario, err)
|
||||
}
|
||||
|
||||
if tc.expectErr && err == nil {
|
||||
t.Fatalf("%s: expected error but got: %v", tc.scenario, err)
|
||||
}
|
||||
|
||||
if tc.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, name := range tc.expectNotExists {
|
||||
path := fmt.Sprintf("%s/%s", tc.rootPath, name)
|
||||
_, err = os.Stat(path)
|
||||
if !os.IsNotExist(err) {
|
||||
t.Errorf("%s: expected '%s' not to exist but it does: %v", tc.scenario, path, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range tc.expectExists {
|
||||
path := fmt.Sprintf("%s/%s", tc.rootPath, name)
|
||||
_, err = os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("%s: expected '%s' to exist but it does not: %v", tc.scenario, path, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user