Skip to content

Commit a864dc1

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

File tree

2 files changed

+47
-14
lines changed

2 files changed

+47
-14
lines changed

internal/nginx/runtime/manager.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package runtime
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io/fs"
78
"os"
89
"strconv"
910
"strings"
1011
"syscall"
1112
"time"
13+
14+
"k8s.io/apimachinery/pkg/util/wait"
1215
)
1316

1417
const (
@@ -39,7 +42,7 @@ func NewManagerImpl() *ManagerImpl {
3942

4043
func (m *ManagerImpl) Reload(ctx context.Context) error {
4144
// We find the main NGINX PID on every reload because it will change if the NGINX container is restarted.
42-
pid, err := findMainProcess(os.Stat, os.ReadFile, pidFileTimeout)
45+
pid, err := findMainProcess(ctx, os.Stat, os.ReadFile, pidFileTimeout)
4346
if err != nil {
4447
return fmt.Errorf("failed to find NGINX main process: %w", err)
4548
}
@@ -67,22 +70,34 @@ func (m *ManagerImpl) Reload(ctx context.Context) error {
6770
return nil
6871
}
6972

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) {
73+
func findMainProcess(
74+
ctx context.Context,
75+
checkFile checkFileFunc,
76+
readFile readFileFunc,
77+
timeout time.Duration,
78+
) (int, error) {
79+
ctx, cancel := context.WithTimeout(ctx, timeout)
80+
defer cancel()
81+
82+
err := wait.PollUntilContextCancel(
83+
ctx,
84+
1*time.Second,
85+
true, /* poll immediately */
86+
func(ctx context.Context) (bool, error) {
7687
_, err := checkFile(pidFile)
7788
if err == nil {
78-
return readFile(pidFile)
89+
return true, nil
90+
}
91+
if !errors.Is(err, fs.ErrNotExist) {
92+
return false, err
7993
}
80-
time.Sleep(1 * time.Second)
81-
}
82-
return nil, fmt.Errorf("timeout waiting for pid file to appear")
94+
return false, nil
95+
})
96+
if err != nil {
97+
return 0, err
8398
}
8499

85-
content, err := fileCheck()
100+
content, err := readFile(pidFile)
86101
if err != nil {
87102
return 0, err
88103
}

internal/nginx/runtime/manager_test.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package runtime
22

33
import (
4+
"context"
45
"errors"
56
"io/fs"
67
"testing"
@@ -32,53 +33,70 @@ func TestFindMainProcess(t *testing.T) {
3233
return nil, errors.New("error")
3334
}
3435
var testFileInfo fs.FileInfo
36+
ctx := context.Background()
37+
cancellingCtx, cancel := context.WithCancel(ctx)
38+
time.AfterFunc(1*time.Millisecond, cancel)
3539

3640
tests := []struct {
41+
ctx context.Context
3742
readFile readFileFunc
3843
checkFile checkFileFunc
3944
msg string
4045
expected int
4146
expectError bool
4247
}{
4348
{
49+
ctx: ctx,
4450
readFile: readFileFuncGen([]byte("1\n")),
4551
checkFile: checkFileFuncGen(testFileInfo),
4652
expected: 1,
4753
expectError: false,
4854
msg: "normal case",
4955
},
5056
{
57+
ctx: ctx,
5158
readFile: readFileFuncGen([]byte("")),
5259
checkFile: checkFileFuncGen(testFileInfo),
5360
expected: 0,
5461
expectError: true,
5562
msg: "empty file content",
5663
},
5764
{
65+
ctx: ctx,
5866
readFile: readFileFuncGen([]byte("not a number")),
5967
checkFile: checkFileFuncGen(testFileInfo),
6068
expected: 0,
6169
expectError: true,
6270
msg: "bad file content",
6371
},
6472
{
73+
ctx: ctx,
6574
readFile: readFileError,
6675
checkFile: checkFileFuncGen(testFileInfo),
6776
expected: 0,
6877
expectError: true,
6978
msg: "cannot read file",
7079
},
7180
{
81+
ctx: ctx,
7282
readFile: readFileFuncGen([]byte("1\n")),
7383
checkFile: checkFileError,
7484
expected: 0,
7585
expectError: true,
76-
msg: "cannot file pid file",
86+
msg: "cannot find pid file",
87+
},
88+
{
89+
ctx: cancellingCtx,
90+
readFile: readFileFuncGen([]byte("1\n")),
91+
checkFile: checkFileError,
92+
expected: 0,
93+
expectError: true,
94+
msg: "context canceled",
7795
},
7896
}
7997

8098
for _, test := range tests {
81-
result, err := findMainProcess(test.checkFile, test.readFile, 1*time.Microsecond)
99+
result, err := findMainProcess(test.ctx, test.checkFile, test.readFile, 2*time.Millisecond)
82100

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

0 commit comments

Comments
 (0)