fix: LibreOffice newer versions stability (#697)

This commit is contained in:
Julien Neuhart
2023-10-23 17:05:10 +02:00
committed by GitHub
parent 9cc8e16d64
commit 258876d13f
59 changed files with 870 additions and 1383 deletions

View File

@@ -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))

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
View 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
View 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)
}
}
}()
}
}