Skip to content

Commit ec53c9d

Browse files
committed
Add timeout and retry logic for the NGINX pid file
1 parent 609beef commit ec53c9d

File tree

2 files changed

+54
-11
lines changed

2 files changed

+54
-11
lines changed

internal/nginx/runtime/manager.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@ package runtime
33
import (
44
"context"
55
"fmt"
6+
"io/fs"
67
"os"
78
"strconv"
89
"strings"
910
"syscall"
1011
"time"
1112
)
1213

13-
const pidFile = "/etc/nginx/nginx.pid"
14+
const (
15+
pidFile = "/etc/nginx/nginx.pid"
16+
pidFileTimeout = 5 * time.Second
17+
)
1418

15-
type readFileFunc func(string) ([]byte, error)
19+
type (
20+
readFileFunc func(string) ([]byte, error)
21+
checkFileFunc func(string) (fs.FileInfo, error)
22+
)
1623

1724
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Manager
1825

@@ -31,13 +38,8 @@ func NewManagerImpl() *ManagerImpl {
3138
}
3239

3340
func (m *ManagerImpl) Reload(ctx context.Context) error {
34-
// FIXME(pleshakov): Before reload attempt, make sure NGINX is running.
35-
// If the gateway container starts before NGINX container (which is possible),
36-
// then it is possible that a reload can be attempted when NGINX is not running yet.
37-
// Make sure to prevent this case, so we don't get an error.
38-
3941
// We find the main NGINX PID on every reload because it will change if the NGINX container is restarted.
40-
pid, err := findMainProcess(os.ReadFile)
42+
pid, err := findMainProcess(os.Stat, os.ReadFile, pidFileTimeout)
4143
if err != nil {
4244
return fmt.Errorf("failed to find NGINX main process: %w", err)
4345
}
@@ -65,8 +67,22 @@ func (m *ManagerImpl) Reload(ctx context.Context) error {
6567
return nil
6668
}
6769

68-
func findMainProcess(readFile readFileFunc) (int, error) {
69-
content, err := readFile(pidFile)
70+
func findMainProcess(checkFile checkFileFunc, readFile readFileFunc, timeout time.Duration) (int, error) {
71+
startTime := time.Now()
72+
deadline := startTime.Add(timeout)
73+
74+
fileCheck := func() (content []byte, err error) {
75+
for time.Now().Before(deadline) {
76+
_, err := checkFile(pidFile)
77+
if err == nil {
78+
return readFile(pidFile)
79+
}
80+
time.Sleep(1 * time.Second)
81+
}
82+
return nil, fmt.Errorf("timeout waiting for pid file to appear")
83+
}
84+
85+
content, err := fileCheck()
7086
if err != nil {
7187
return 0, err
7288
}

internal/nginx/runtime/manager_test.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package runtime
22

33
import (
44
"errors"
5+
"io/fs"
56
"testing"
7+
"time"
68
)
79

810
func TestFindMainProcess(t *testing.T) {
@@ -18,40 +20,65 @@ func TestFindMainProcess(t *testing.T) {
1820
return nil, errors.New("error")
1921
}
2022

23+
checkFileFuncGen := func(content fs.FileInfo) checkFileFunc {
24+
return func(name string) (fs.FileInfo, error) {
25+
if name != pidFile {
26+
return nil, errors.New("error")
27+
}
28+
return content, nil
29+
}
30+
}
31+
checkFileError := func(string) (fs.FileInfo, error) {
32+
return nil, errors.New("error")
33+
}
34+
var testFileInfo fs.FileInfo
35+
2136
tests := []struct {
2237
readFile readFileFunc
38+
checkFile checkFileFunc
2339
msg string
2440
expected int
2541
expectError bool
2642
}{
2743
{
2844
readFile: readFileFuncGen([]byte("1\n")),
45+
checkFile: checkFileFuncGen(testFileInfo),
2946
expected: 1,
3047
expectError: false,
3148
msg: "normal case",
3249
},
3350
{
3451
readFile: readFileFuncGen([]byte("")),
52+
checkFile: checkFileFuncGen(testFileInfo),
3553
expected: 0,
3654
expectError: true,
3755
msg: "empty file content",
3856
},
3957
{
4058
readFile: readFileFuncGen([]byte("not a number")),
59+
checkFile: checkFileFuncGen(testFileInfo),
4160
expected: 0,
4261
expectError: true,
4362
msg: "bad file content",
4463
},
4564
{
4665
readFile: readFileError,
66+
checkFile: checkFileFuncGen(testFileInfo),
4767
expected: 0,
4868
expectError: true,
4969
msg: "cannot read file",
5070
},
71+
{
72+
readFile: readFileFuncGen([]byte("1\n")),
73+
checkFile: checkFileError,
74+
expected: 0,
75+
expectError: true,
76+
msg: "cannot file pid file",
77+
},
5178
}
5279

5380
for _, test := range tests {
54-
result, err := findMainProcess(test.readFile)
81+
result, err := findMainProcess(test.checkFile, test.readFile, 1*time.Microsecond)
5582

5683
if result != test.expected {
5784
t.Errorf("findMainProcess() returned %d but expected %d for case %q", result, test.expected, test.msg)

0 commit comments

Comments
 (0)