Skip to content

Commit 33304b6

Browse files
miledxzkate-osbornsjberman
authored
Test/add runtime manager tests (#2175)
--------- Co-authored-by: Mile Druzijanic <153705375+zedGGs@users.noreply.github.com> Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> Co-authored-by: Saylor Berman <s.berman@f5.com>
1 parent 767294e commit 33304b6

11 files changed

+1481
-58
lines changed

internal/mode/static/manager.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"os"
78
"time"
89

910
"github.com/go-logr/logr"
10-
ngxclient "github.com/nginxinc/nginx-plus-go-client/client"
1111
tel "github.com/nginxinc/telemetry-exporter/pkg/telemetry"
1212
"github.com/prometheus/client_golang/prometheus"
1313
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
@@ -146,8 +146,10 @@ func StartManager(cfg config.Config) error {
146146
return fmt.Errorf("cannot clear NGINX configuration folders: %w", err)
147147
}
148148

149+
processHandler := ngxruntime.NewProcessHandlerImpl(os.ReadFile, os.Stat)
150+
149151
// Ensure NGINX is running before registering metrics & starting the manager.
150-
if err := ngxruntime.EnsureNginxRunning(ctx); err != nil {
152+
if _, err := processHandler.FindMainProcess(ctx, ngxruntime.PidFileTimeout); err != nil {
151153
return fmt.Errorf("NGINX is not running: %w", err)
152154
}
153155

@@ -156,8 +158,9 @@ func StartManager(cfg config.Config) error {
156158
handlerCollector handlerMetricsCollector = collectors.NewControllerNoopCollector()
157159
)
158160

159-
var ngxPlusClient *ngxclient.NginxClient
161+
var ngxPlusClient ngxruntime.NginxPlusClient
160162
var usageSecret *usage.Secret
163+
161164
if cfg.Plus {
162165
ngxPlusClient, err = ngxruntime.CreatePlusClient()
163166
if err != nil {
@@ -223,6 +226,8 @@ func StartManager(cfg config.Config) error {
223226
ngxPlusClient,
224227
ngxruntimeCollector,
225228
cfg.Logger.WithName("nginxRuntimeManager"),
229+
processHandler,
230+
ngxruntime.NewVerifyClient(ngxruntime.NginxReloadTimeout),
226231
),
227232
statusUpdater: groupStatusUpdater,
228233
eventRecorder: recorder,

internal/mode/static/metrics/collectors/nginx.go

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

33
import (
4+
"fmt"
5+
46
"github.com/go-kit/log"
57
"github.com/nginxinc/nginx-plus-go-client/client"
68
prometheusClient "github.com/nginxinc/nginx-prometheus-exporter/client"
@@ -26,12 +28,16 @@ func NewNginxMetricsCollector(constLabels map[string]string, logger log.Logger)
2628

2729
// NewNginxPlusMetricsCollector creates an NginxCollector which fetches stats from NGINX Plus API over a unix socket.
2830
func NewNginxPlusMetricsCollector(
29-
plusClient *client.NginxClient,
31+
plusClient runtime.NginxPlusClient,
3032
constLabels map[string]string,
3133
logger log.Logger,
3234
) (prometheus.Collector, error) {
35+
nc, ok := plusClient.(*client.NginxClient)
36+
if !ok {
37+
panic(fmt.Sprintf("expected *client.NginxClient, got %T", plusClient))
38+
}
3339
collector := nginxCollector.NewNginxPlusCollector(
34-
plusClient,
40+
nc,
3541
metrics.Namespace,
3642
nginxCollector.VariableLabelNames{},
3743
constLabels,

internal/mode/static/nginx/runtime/manager.go

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,36 @@ import (
1919
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
2020

2121
const (
22-
pidFile = "/var/run/nginx/nginx.pid"
23-
pidFileTimeout = 10000 * time.Millisecond
24-
nginxReloadTimeout = 60000 * time.Millisecond
22+
// PidFile specifies the location of the PID file for the Nginx process.
23+
PidFile = "/var/run/nginx/nginx.pid"
24+
// PidFileTimeout defines the timeout duration for accessing the PID file.
25+
PidFileTimeout = 10000 * time.Millisecond
26+
// NginxReloadTimeout sets the timeout duration for reloading the Nginx configuration.
27+
NginxReloadTimeout = 60000 * time.Millisecond
2528
)
2629

2730
type (
28-
readFileFunc func(string) ([]byte, error)
29-
checkFileFunc func(string) (fs.FileInfo, error)
31+
ReadFileFunc func(string) ([]byte, error)
32+
CheckFileFunc func(string) (fs.FileInfo, error)
3033
)
3134

3235
var childProcPathFmt = "/proc/%[1]v/task/%[1]v/children"
3336

37+
//counterfeiter:generate . NginxPlusClient
38+
39+
type NginxPlusClient interface {
40+
UpdateHTTPServers(
41+
upstream string,
42+
servers []ngxclient.UpstreamServer,
43+
) (
44+
added []ngxclient.UpstreamServer,
45+
deleted []ngxclient.UpstreamServer,
46+
updated []ngxclient.UpstreamServer,
47+
err error,
48+
)
49+
GetUpstreams() (*ngxclient.Upstreams, error)
50+
}
51+
3452
//counterfeiter:generate . Manager
3553

3654
// Manager manages the runtime of NGINX.
@@ -48,6 +66,8 @@ type Manager interface {
4866
}
4967

5068
// MetricsCollector is an interface for the metrics of the NGINX runtime manager.
69+
//
70+
//counterfeiter:generate . MetricsCollector
5171
type MetricsCollector interface {
5272
IncReloadCount()
5373
IncReloadErrors()
@@ -56,21 +76,25 @@ type MetricsCollector interface {
5676

5777
// ManagerImpl implements Manager.
5878
type ManagerImpl struct {
59-
verifyClient *verifyClient
79+
processHandler ProcessHandler
6080
metricsCollector MetricsCollector
61-
ngxPlusClient *ngxclient.NginxClient
81+
verifyClient nginxConfigVerifier
82+
ngxPlusClient NginxPlusClient
6283
logger logr.Logger
6384
}
6485

6586
// NewManagerImpl creates a new ManagerImpl.
6687
func NewManagerImpl(
67-
ngxPlusClient *ngxclient.NginxClient,
88+
ngxPlusClient NginxPlusClient,
6889
collector MetricsCollector,
6990
logger logr.Logger,
91+
processHandler ProcessHandler,
92+
verifyClient nginxConfigVerifier,
7093
) *ManagerImpl {
7194
return &ManagerImpl{
72-
verifyClient: newVerifyClient(nginxReloadTimeout),
95+
processHandler: processHandler,
7396
metricsCollector: collector,
97+
verifyClient: verifyClient,
7498
ngxPlusClient: ngxPlusClient,
7599
logger: logger,
76100
}
@@ -84,25 +108,25 @@ func (m *ManagerImpl) IsPlus() bool {
84108
func (m *ManagerImpl) Reload(ctx context.Context, configVersion int) error {
85109
start := time.Now()
86110
// We find the main NGINX PID on every reload because it will change if the NGINX container is restarted.
87-
pid, err := findMainProcess(ctx, os.Stat, os.ReadFile, pidFileTimeout)
111+
pid, err := m.processHandler.FindMainProcess(ctx, PidFileTimeout)
88112
if err != nil {
89113
return fmt.Errorf("failed to find NGINX main process: %w", err)
90114
}
91115

92116
childProcFile := fmt.Sprintf(childProcPathFmt, pid)
93-
previousChildProcesses, err := os.ReadFile(childProcFile)
117+
previousChildProcesses, err := m.processHandler.ReadFile(childProcFile)
94118
if err != nil {
95119
return err
96120
}
97121

98122
// send HUP signal to the NGINX main process reload configuration
99123
// See https://nginx.org/en/docs/control.html
100-
if err := syscall.Kill(pid, syscall.SIGHUP); err != nil {
124+
if err := m.processHandler.Kill(pid); err != nil {
101125
m.metricsCollector.IncReloadErrors()
102126
return fmt.Errorf("failed to send the HUP signal to NGINX main: %w", err)
103127
}
104128

105-
if err = m.verifyClient.waitForCorrectVersion(
129+
if err = m.verifyClient.WaitForCorrectVersion(
106130
ctx,
107131
configVersion,
108132
childProcFile,
@@ -153,18 +177,31 @@ func (m *ManagerImpl) GetUpstreams() (ngxclient.Upstreams, error) {
153177
return *upstreams, nil
154178
}
155179

156-
// EnsureNginxRunning ensures NGINX is running by locating the main process.
157-
func EnsureNginxRunning(ctx context.Context) error {
158-
if _, err := findMainProcess(ctx, os.Stat, os.ReadFile, pidFileTimeout); err != nil {
159-
return fmt.Errorf("failed to find NGINX main process: %w", err)
180+
//counterfeiter:generate . ProcessHandler
181+
182+
type ProcessHandler interface {
183+
FindMainProcess(
184+
ctx context.Context,
185+
timeout time.Duration,
186+
) (int, error)
187+
ReadFile(file string) ([]byte, error)
188+
Kill(pid int) error
189+
}
190+
191+
type ProcessHandlerImpl struct {
192+
readFile ReadFileFunc
193+
checkFile CheckFileFunc
194+
}
195+
196+
func NewProcessHandlerImpl(readFile ReadFileFunc, checkFile CheckFileFunc) *ProcessHandlerImpl {
197+
return &ProcessHandlerImpl{
198+
readFile: readFile,
199+
checkFile: checkFile,
160200
}
161-
return nil
162201
}
163202

164-
func findMainProcess(
203+
func (p *ProcessHandlerImpl) FindMainProcess(
165204
ctx context.Context,
166-
checkFile checkFileFunc,
167-
readFile readFileFunc,
168205
timeout time.Duration,
169206
) (int, error) {
170207
ctx, cancel := context.WithTimeout(ctx, timeout)
@@ -175,7 +212,7 @@ func findMainProcess(
175212
500*time.Millisecond,
176213
true, /* poll immediately */
177214
func(_ context.Context) (bool, error) {
178-
_, err := checkFile(pidFile)
215+
_, err := p.checkFile(PidFile)
179216
if err == nil {
180217
return true, nil
181218
}
@@ -188,7 +225,7 @@ func findMainProcess(
188225
return 0, err
189226
}
190227

191-
content, err := readFile(pidFile)
228+
content, err := p.readFile(PidFile)
192229
if err != nil {
193230
return 0, err
194231
}
@@ -200,3 +237,11 @@ func findMainProcess(
200237

201238
return pid, nil
202239
}
240+
241+
func (p *ProcessHandlerImpl) ReadFile(file string) ([]byte, error) {
242+
return p.readFile(file)
243+
}
244+
245+
func (p *ProcessHandlerImpl) Kill(pid int) error {
246+
return syscall.Kill(pid, syscall.SIGHUP)
247+
}

0 commit comments

Comments
 (0)