Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Cai <jcai@gitlab.com>2022-07-26 22:02:59 +0300
committerJohn Cai <jcai@gitlab.com>2022-08-22 15:56:27 +0300
commitcdb5961d14bd60a5d57ba43dedb940b99b51d1d1 (patch)
treed29aaa655c75ec78259eccc717e1c8c06235ed6b
parentb9e607f9bf82defc2b57dacdbc31a1faa2257270 (diff)
config: Generisize functions that create and prune temp directories
Pruning directories is a common operation. Currently it's used for pruning Gitaly runtime directories. However, Cgroup directories are made according to the same convention as Gitaly runtime directories in the form of gitaly-<pid>. Instead of reproducing the logic, simply rename the function so it can be reused. To do this, we also need to encapsulate the logic that constructs the name of a process directory and the logic that parses out the pid from such a directory into functions.
-rw-r--r--cmd/gitaly/main.go2
-rw-r--r--internal/gitaly/config/config.go69
-rw-r--r--internal/gitaly/config/config_test.go19
-rw-r--r--internal/gitaly/config/temp_dir.go88
4 files changed, 101 insertions, 77 deletions
diff --git a/cmd/gitaly/main.go b/cmd/gitaly/main.go
index 765d729ca..636476901 100644
--- a/cmd/gitaly/main.go
+++ b/cmd/gitaly/main.go
@@ -149,7 +149,7 @@ func run(cfg config.Cfg) error {
defer cancel()
if cfg.RuntimeDir != "" {
- if err := config.PruneRuntimeDirectories(log.StandardLogger(), cfg.RuntimeDir); err != nil {
+ if err := config.PruneOldGitalyProcessDirectories(log.StandardLogger(), cfg.RuntimeDir); err != nil {
return fmt.Errorf("prune runtime directories: %w", err)
}
}
diff --git a/internal/gitaly/config/config.go b/internal/gitaly/config/config.go
index 43fae6f40..4b816b752 100644
--- a/internal/gitaly/config/config.go
+++ b/internal/gitaly/config/config.go
@@ -8,7 +8,6 @@ import (
"os"
"path/filepath"
"reflect"
- "strconv"
"strings"
"syscall"
"time"
@@ -543,72 +542,6 @@ func (cfg *Cfg) configurePackObjectsCache() error {
return nil
}
-// PruneRuntimeDirectories removes leftover runtime directories that belonged to processes that
-// no longer exist. The removals are logged prior to being executed. Unexpected directory entries
-// are logged but not removed
-func PruneRuntimeDirectories(log log.FieldLogger, runtimeDir string) error {
- entries, err := os.ReadDir(runtimeDir)
- if err != nil {
- return fmt.Errorf("list runtime directory: %w", err)
- }
-
- for _, entry := range entries {
- if err := func() error {
- log := log.WithField("path", filepath.Join(runtimeDir, entry.Name()))
- if !entry.IsDir() {
- // There should be no files, only the runtime directories.
- log.Error("runtime directory contains an unexpected file")
- return nil
- }
-
- components := strings.Split(entry.Name(), "-")
- if len(components) != 2 || components[0] != "gitaly" {
- // This directory does not match the runtime directory naming format
- // of `gitaly-<process id>.
- log.Error("runtime directory contains an unexpected directory")
- return nil
- }
-
- processID, err := strconv.ParseInt(components[1], 10, 64)
- if err != nil {
- // This is not a runtime directory as the section after the hyphen is not a process id.
- log.Error("runtime directory contains an unexpected directory")
- return nil
- }
-
- process, err := os.FindProcess(int(processID))
- if err != nil {
- return fmt.Errorf("find process: %w", err)
- }
- defer func() {
- if err := process.Release(); err != nil {
- log.WithError(err).Error("failed releasing process")
- }
- }()
-
- if err := process.Signal(syscall.Signal(0)); err != nil {
- // Either the process does not exist, or the pid has been re-used by for a
- // process owned by another user and is not a Gitaly process.
- if !errors.Is(err, os.ErrProcessDone) && !errors.Is(err, syscall.EPERM) {
- return fmt.Errorf("signal: %w", err)
- }
-
- log.Info("removing leftover runtime directory")
-
- if err := os.RemoveAll(filepath.Join(runtimeDir, entry.Name())); err != nil {
- return fmt.Errorf("remove leftover runtime directory: %w", err)
- }
- }
-
- return nil
- }(); err != nil {
- return err
- }
- }
-
- return nil
-}
-
// SetupRuntimeDirectory creates a new runtime directory. Runtime directory contains internal
// runtime data generated by Gitaly such as the internal sockets. If cfg.RuntimeDir is set,
// it's used as the parent directory for the runtime directory. Runtime directory owner process
@@ -638,7 +571,7 @@ func SetupRuntimeDirectory(cfg Cfg, processID int) (string, error) {
// PID exists. Furthermore, it allows easier debugging in case one wants to inspect
// the runtime directory of a running Gitaly node.
- runtimeDir = filepath.Join(cfg.RuntimeDir, fmt.Sprintf("gitaly-%d", processID))
+ runtimeDir = GetGitalyProcessTempDir(cfg.RuntimeDir, processID)
if _, err := os.Stat(runtimeDir); err != nil && !os.IsNotExist(err) {
return "", fmt.Errorf("statting runtime directory: %w", err)
diff --git a/internal/gitaly/config/config_test.go b/internal/gitaly/config/config_test.go
index fb93676f8..cc46193f5 100644
--- a/internal/gitaly/config/config_test.go
+++ b/internal/gitaly/config/config_test.go
@@ -1304,15 +1304,18 @@ func TestSetupRuntimeDirectory(t *testing.T) {
func TestPruneRuntimeDirectories(t *testing.T) {
t.Run("no runtime directories", func(t *testing.T) {
- require.NoError(t, PruneRuntimeDirectories(testhelper.NewDiscardingLogEntry(t), testhelper.TempDir(t)))
+ require.NoError(t, PruneOldGitalyProcessDirectories(testhelper.NewDiscardingLogEntry(t), testhelper.TempDir(t)))
})
t.Run("unset runtime directory", func(t *testing.T) {
- require.EqualError(t, PruneRuntimeDirectories(testhelper.NewDiscardingLogEntry(t), ""), "list runtime directory: open : no such file or directory")
+ require.EqualError(t,
+ PruneOldGitalyProcessDirectories(testhelper.NewDiscardingLogEntry(t), ""), "list gitaly process directory: open : no such file or directory")
})
t.Run("non-existent runtime directory", func(t *testing.T) {
- require.EqualError(t, PruneRuntimeDirectories(testhelper.NewDiscardingLogEntry(t), "/path/does/not/exist"), "list runtime directory: open /path/does/not/exist: no such file or directory")
+ require.EqualError(t,
+ PruneOldGitalyProcessDirectories(testhelper.NewDiscardingLogEntry(t),
+ "/path/does/not/exist"), "list gitaly process directory: open /path/does/not/exist: no such file or directory")
})
t.Run("invalid, stale and active runtime directories", func(t *testing.T) {
@@ -1336,19 +1339,19 @@ func TestPruneRuntimeDirectories(t *testing.T) {
require.NoError(t, err)
prunableDirs = append(prunableDirs, staleRuntimeDir)
- expectedLogs[staleRuntimeDir] = "removing leftover runtime directory"
+ expectedLogs[staleRuntimeDir] = "removing leftover gitaly process directory"
}
// Setup runtime directory with pid of process not owned by git user
rootRuntimeDir, err := SetupRuntimeDirectory(cfg, 1)
require.NoError(t, err)
- expectedLogs[rootRuntimeDir] = "removing leftover runtime directory"
+ expectedLogs[rootRuntimeDir] = "removing leftover gitaly process directory"
prunableDirs = append(prunableDirs, rootRuntimeDir)
// Create an unexpected file in the runtime directory
unexpectedFilePath := filepath.Join(baseDir, "unexpected-file")
require.NoError(t, os.WriteFile(unexpectedFilePath, []byte(""), os.ModePerm))
- expectedLogs[unexpectedFilePath] = "runtime directory contains an unexpected file"
+ expectedLogs[unexpectedFilePath] = "gitaly process directory contains an unexpected file"
nonPrunableDirs := []string{ownRuntimeDir}
@@ -1361,12 +1364,12 @@ func TestPruneRuntimeDirectories(t *testing.T) {
} {
dirPath := filepath.Join(baseDir, dirName)
require.NoError(t, os.Mkdir(dirPath, os.ModePerm))
- expectedLogs[dirPath] = "runtime directory contains an unexpected directory"
+ expectedLogs[dirPath] = "gitaly process directory contains an unexpected directory"
nonPrunableDirs = append(nonPrunableDirs, dirPath)
}
logger, hook := test.NewNullLogger()
- require.NoError(t, PruneRuntimeDirectories(logger, cfg.RuntimeDir))
+ require.NoError(t, PruneOldGitalyProcessDirectories(logger, cfg.RuntimeDir))
actualLogs := map[string]string{}
for _, entry := range hook.Entries {
diff --git a/internal/gitaly/config/temp_dir.go b/internal/gitaly/config/temp_dir.go
new file mode 100644
index 000000000..52ba93dd1
--- /dev/null
+++ b/internal/gitaly/config/temp_dir.go
@@ -0,0 +1,88 @@
+package config
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+
+ log "github.com/sirupsen/logrus"
+)
+
+// PruneOldGitalyProcessDirectories removes leftover temporary directories that belonged to processes that
+// no longer exist. Directories are expected to be in the form gitaly-<pid>.
+// The removals are logged prior to being executed. Unexpected directory entries are logged
+// but not removed.
+func PruneOldGitalyProcessDirectories(log log.FieldLogger, directory string) error {
+ entries, err := os.ReadDir(directory)
+ if err != nil {
+ return fmt.Errorf("list gitaly process directory: %w", err)
+ }
+
+ for _, entry := range entries {
+ if err := func() error {
+ log := log.WithField("path", filepath.Join(directory, entry.Name()))
+ if !entry.IsDir() {
+ // There should be no files, only the gitaly process directories.
+ log.Error("gitaly process directory contains an unexpected file")
+ return nil
+ }
+
+ components := strings.Split(entry.Name(), "-")
+ if len(components) != 2 || components[0] != "gitaly" {
+ // This directory does not match the gitaly process directory naming format
+ // of `gitaly-<process id>.
+ log.Error("gitaly process directory contains an unexpected directory")
+ return nil
+ }
+
+ processID, err := strconv.ParseInt(components[1], 10, 64)
+ if err != nil {
+ // This is not a temporary gitaly process directory as the section
+ // after the hyphen is not a process id.
+ log.Error("gitaly process directory contains an unexpected directory")
+ return nil
+ }
+
+ process, err := os.FindProcess(int(processID))
+ if err != nil {
+ return fmt.Errorf("find process: %w", err)
+ }
+ defer func() {
+ if err := process.Release(); err != nil {
+ log.WithError(err).Error("failed releasing process")
+ }
+ }()
+
+ if err := process.Signal(syscall.Signal(0)); err != nil {
+ // Either the process does not exist, or the pid has been re-used by for a
+ // process owned by another user and is not a Gitaly process.
+ if !errors.Is(err, os.ErrProcessDone) && !errors.Is(err, syscall.EPERM) {
+ return fmt.Errorf("signal: %w", err)
+ }
+
+ log.Info("removing leftover gitaly process directory")
+
+ if err := os.RemoveAll(filepath.Join(directory, entry.Name())); err != nil {
+ return fmt.Errorf("remove leftover gitaly process directory: %w", err)
+ }
+ }
+
+ return nil
+ }(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// GetGitalyProcessTempDir constructs a temporary directory name for the current gitaly
+// process. This way, we can clean up old temporary directories by inspecting the pid attached
+// to the folder.
+func GetGitalyProcessTempDir(parentDir string, processID int) string {
+ return filepath.Join(parentDir, fmt.Sprintf("gitaly-%d", processID))
+}