1
1
package runtime
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"errors"
6
7
"fmt"
@@ -15,58 +16,73 @@ import (
15
16
)
16
17
17
18
const (
18
- pidFile = "/var/run/nginx/nginx.pid"
19
- pidFileTimeout = 10 * time .Second
19
+ pidFile = "/var/run/nginx/nginx.pid"
20
+ pidFileTimeout = 10000 * time .Millisecond
21
+ childProcsTimeout = 1000 * time .Millisecond
22
+ nginxReloadTimeout = 60000 * time .Millisecond
20
23
)
21
24
22
25
type (
23
26
readFileFunc func (string ) ([]byte , error )
24
27
checkFileFunc func (string ) (fs.FileInfo , error )
25
28
)
26
29
30
+ var (
31
+ noNewWorkersErrFmt = "reload unsuccessful: no new NGINX worker processes started for config version %d." +
32
+ " Please check the NGINX container logs for possible configuration issues: %w"
33
+ childProcPathFmt = "/proc/%[1]v/task/%[1]v/children"
34
+ )
35
+
27
36
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Manager
28
37
29
38
// Manager manages the runtime of NGINX.
30
39
type Manager interface {
31
40
// Reload reloads NGINX configuration. It is a blocking operation.
32
- Reload (ctx context.Context ) error
41
+ Reload (ctx context.Context , configVersion int ) error
33
42
}
34
43
35
44
// ManagerImpl implements Manager.
36
- type ManagerImpl struct {}
45
+ type ManagerImpl struct {
46
+ verifyClient * verifyClient
47
+ }
37
48
38
49
// NewManagerImpl creates a new ManagerImpl.
39
50
func NewManagerImpl () * ManagerImpl {
40
- return & ManagerImpl {}
51
+ return & ManagerImpl {
52
+ verifyClient : newVerifyClient (nginxReloadTimeout ),
53
+ }
41
54
}
42
55
43
- func (m * ManagerImpl ) Reload (ctx context.Context ) error {
56
+ func (m * ManagerImpl ) Reload (ctx context.Context , configVersion int ) error {
44
57
// We find the main NGINX PID on every reload because it will change if the NGINX container is restarted.
45
58
pid , err := findMainProcess (ctx , os .Stat , os .ReadFile , pidFileTimeout )
46
59
if err != nil {
47
60
return fmt .Errorf ("failed to find NGINX main process: %w" , err )
48
61
}
49
62
63
+ childProcFile := fmt .Sprintf (childProcPathFmt , pid )
64
+ previousChildProcesses , err := os .ReadFile (childProcFile )
65
+ if err != nil {
66
+ return err
67
+ }
68
+
50
69
// send HUP signal to the NGINX main process reload configuration
51
70
// See https://nginx.org/en/docs/control.html
52
71
if err := syscall .Kill (pid , syscall .SIGHUP ); err != nil {
53
72
return fmt .Errorf ("failed to send the HUP signal to NGINX main: %w" , err )
54
73
}
55
74
56
- // FIXME(pleshakov)
57
- // (1) ensure the reload actually happens.
58
- // https://github.com/nginxinc/nginx-kubernetes-gateway/issues/664
59
-
60
- // for now, to prevent a subsequent reload starting before the in-flight reload finishes, we simply sleep.
61
- // Fixing (1) will make the sleep unnecessary.
62
-
63
- select {
64
- case <- ctx .Done ():
65
- return nil
66
- case <- time .After (1 * time .Second ):
75
+ if err := ensureNewNginxWorkers (
76
+ ctx ,
77
+ childProcFile ,
78
+ previousChildProcesses ,
79
+ os .ReadFile ,
80
+ childProcsTimeout ,
81
+ ); err != nil {
82
+ return fmt .Errorf (noNewWorkersErrFmt , configVersion , err )
67
83
}
68
84
69
- return nil
85
+ return m . verifyClient . waitForCorrectVersion ( ctx , configVersion )
70
86
}
71
87
72
88
// EnsureNginxRunning ensures NGINX is running by locating the main process.
@@ -116,3 +132,30 @@ func findMainProcess(
116
132
117
133
return pid , nil
118
134
}
135
+
136
+ func ensureNewNginxWorkers (
137
+ ctx context.Context ,
138
+ childProcFile string ,
139
+ previousContents []byte ,
140
+ readFile readFileFunc ,
141
+ timeout time.Duration ,
142
+ ) error {
143
+ ctx , cancel := context .WithTimeout (ctx , timeout )
144
+ defer cancel ()
145
+
146
+ return wait .PollUntilContextCancel (
147
+ ctx ,
148
+ 25 * time .Millisecond ,
149
+ true , /* poll immediately */
150
+ func (ctx context.Context ) (bool , error ) {
151
+ content , err := readFile (childProcFile )
152
+ if err != nil {
153
+ return false , err
154
+ }
155
+ if ! bytes .Equal (previousContents , content ) {
156
+ return true , nil
157
+ }
158
+ return false , nil
159
+ },
160
+ )
161
+ }
0 commit comments