Skip to content

Commit

Permalink
Update NGINX plugin to read NGINX config on startup (#489)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhurley authored Oct 4, 2023
1 parent f1a7a51 commit a94d816
Show file tree
Hide file tree
Showing 12 changed files with 58 additions and 60 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,10 @@ The NGINX Agent REST interface can be exposed by validating the following lines

```yaml
api:
port: 8081 # port to expose REST API
# Set API address to allow remote management
host: 127.0.0.1
# Set this value to a secure port number to prevent information leaks
port: 8038
# REST TLS parameters
cert: "<TLS-CERTIFICATE>.crt"
Expand Down
17 changes: 1 addition & 16 deletions examples/grafana-metrics/nginx-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,8 @@

api:
# port to expose http api
host: 0.0.0.0
port: 8081
# tls options
tls:
# enable tls in the nginx-agent setup for grpcs
# default to enable to connect with tls connection but without client cert for mtls
enable: true
# specify the absolute path to the CA certificate file to use for verifying
# the server certificate (also requires 'skip_verify: false' below)
# by default, this will be the trusted root CAs found in the OS CA store
# ca: /etc/nginx-agent/ca.pem
# specify the absolute path to the client cert, when mtls is enabled
# cert: /etc/nginx-agent/client.crt
# specify the absolute path to the client cert key, when mtls is enabled
# key: /etc/nginx-agent/client.key
# controls whether the server certificate chain and host name are verified.
# for production use, see instructions for configuring TLS
skip_verify: true
log:
# set log level (panic, fatal, error, info, debug, trace; default "info")
level: info
Expand Down
6 changes: 4 additions & 2 deletions hugo/content/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ The NGINX Agent REST interface can be exposed by validating the following lines

```yaml
api:
port: 8081 # port to expose REST API
# Set API address to allow remote management
host: 127.0.0.1
# Set this value to a secure port number to prevent information leaks
port: 8038
# REST TLS parameters
cert: "<TLS-CERTIFICATE>.crt"
key: "<PRIVATE-KEY>.key"
Expand Down
3 changes: 1 addition & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ func main() {
corePlugins, extensionPlugins := plugins.LoadPlugins(commander, binary, env, reporter, loadedConfig, eventMeta)

pipe := core.InitializePipe(ctx, corePlugins, extensionPlugins, agent_config.DefaultPluginSize)

defer pipe.Process(core.NewMessage(core.AgentStarted, eventMeta))
pipe.Process(core.NewMessage(core.AgentStarted, eventMeta))
core.HandleSignals(ctx, commander, loadedConfig, env, pipe, cancel, controller)

pipe.Run()
Expand Down
4 changes: 2 additions & 2 deletions src/core/pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,11 @@ func (p *MessagePipe) GetExtensionPlugins() []ExtensionPlugin {

func (p *MessagePipe) initPlugins() {
for _, r := range p.plugins {
go r.Init(p)
r.Init(p)
}

for _, r := range p.extensionPlugins {
go r.Init(p)
r.Init(p)
}
}

Expand Down
21 changes: 14 additions & 7 deletions src/plugins/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ type NginxConfigValidationResponse struct {

func NewNginx(cmdr client.Commander, nginxBinary core.NginxBinary, env core.Environment, loadedConfig *config.Config, processes []*core.Process) *Nginx {
isFeatureNginxConfigEnabled := loadedConfig.IsFeatureEnabled(agent_config.FeatureNginxConfig) || loadedConfig.IsFeatureEnabled(agent_config.FeatureNginxConfigAsync)

isNginxAppProtectEnabled := loadedConfig.IsExtensionEnabled(agent_config.NginxAppProtectExtensionPlugin)

return &Nginx{
Expand All @@ -112,17 +111,25 @@ func (n *Nginx) Init(pipeline core.MessagePipeInterface) {
log.Info("NginxBinary initializing")
n.messagePipeline = pipeline
n.nginxBinary.UpdateNginxDetailsFromProcesses(n.getNginxProccessInfo())
nginxDetails := n.nginxBinary.GetNginxDetailsMapFromProcesses(n.getNginxProccessInfo())

for _, nginxDetail := range nginxDetails {
log.Infof("Reading config in directory %v for nginx instance %v", nginxDetail.GetConfPath(), nginxDetail.GetNginxId())
_, err := n.nginxBinary.ReadConfig(nginxDetail.GetConfPath(), nginxDetail.GetNginxId(), n.env.GetSystemUUID())
if err != nil {
log.Errorf("Unable to read nginx config %s: %v", nginxDetail.GetConfPath(), err)
}
}

n.messagePipeline.Process(
core.NewMessage(core.NginxPluginConfigured, n),
core.NewMessage(core.NginxInstancesFound, nginxDetails),
)
}

// Process processes the messages from the messaging pipe
func (n *Nginx) Process(message *core.Message) {
switch message.Topic() {
case core.AgentStarted:
nginxDetails := n.nginxBinary.GetNginxDetailsMapFromProcesses(n.getNginxProccessInfo())
n.messagePipeline.Process(
core.NewMessage(core.NginxPluginConfigured, n),
core.NewMessage(core.NginxInstancesFound, nginxDetails),
)
case core.CommNginxConfig:
switch cmd := message.Data().(type) {
case *proto.Command:
Expand Down
27 changes: 9 additions & 18 deletions src/plugins/nginx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,6 @@ func TestUploadConfigs(t *testing.T) {
}

msgTopics := []string{
core.AgentStarted,
core.NginxPluginConfigured,
core.NginxInstancesFound,
core.DataplaneChanged,
Expand All @@ -637,14 +636,9 @@ func TestUploadConfigs(t *testing.T) {
messagePipe := core.SetupMockMessagePipe(t, context.TODO(), []core.Plugin{pluginUnderTest}, []core.ExtensionPlugin{})

pluginUnderTest.Init(messagePipe)

// calling Run x 2 means AgentStarted finishes before the DataplaneChanged event gets processed.
// This is the expected order of the real MessagePipe
messagePipe.Process(core.NewMessage(core.AgentStarted, nil))
messagePipe.Run()

messagePipe.RunWithoutInit()
messagePipe.Process(core.NewMessage(core.DataplaneChanged, nil))
messagePipe.Run()
messagePipe.RunWithoutInit()

binary.AssertExpectations(t)
cmdr.AssertExpectations(t)
Expand All @@ -654,7 +648,6 @@ func TestUploadConfigs(t *testing.T) {

func TestDisableUploadConfigs(t *testing.T) {
msgTopics := []string{
core.AgentStarted,
core.NginxPluginConfigured,
core.NginxInstancesFound,
core.DataplaneChanged,
Expand All @@ -665,20 +658,18 @@ func TestDisableUploadConfigs(t *testing.T) {
binary := tutils.NewMockNginxBinary()
binary.On("UpdateNginxDetailsFromProcesses", mock.Anything)
binary.On("GetNginxDetailsMapFromProcesses", mock.Anything).Return(tutils.GetDetailsMap())
binary.On("ReadConfig", mock.Anything, mock.Anything, mock.Anything).Return(&proto.NginxConfig{}, nil)

cmdr := tutils.NewMockCommandClient()

pluginUnderTest := NewNginx(cmdr, binary, env, &loadedConfig.Config{}, tutils.GetProcesses())
messagePipe := core.SetupMockMessagePipe(t, context.TODO(), []core.Plugin{pluginUnderTest}, []core.ExtensionPlugin{})

pluginUnderTest.Init(messagePipe)
// calling Run x 2 means AgentStarted finishes before the DataplaneChanged event gets processed.
// This is the expected order of the real MessagePipe
messagePipe.Process(core.NewMessage(core.AgentStarted, nil))
messagePipe.Run()
messagePipe.RunWithoutInit()

messagePipe.Process(core.NewMessage(core.DataplaneChanged, nil))
messagePipe.Run()
messagePipe.RunWithoutInit()

binary.AssertExpectations(t)

Expand All @@ -692,17 +683,16 @@ func TestNginxDetailProcUpdate(t *testing.T) {
binary := tutils.NewMockNginxBinary()
binary.On("GetNginxDetailsMapFromProcesses", mock.Anything).Return(tutils.GetDetailsMap())
binary.On("UpdateNginxDetailsFromProcesses", tutils.GetProcesses())
binary.On("ReadConfig", mock.Anything, mock.Anything, mock.Anything).Return(&proto.NginxConfig{}, nil)

cmdr := tutils.NewMockCommandClient()

pluginUnderTest := NewNginx(cmdr, binary, env, &loadedConfig.Config{}, tutils.GetProcesses())
messagePipe := core.SetupMockMessagePipe(t, context.TODO(), []core.Plugin{pluginUnderTest}, []core.ExtensionPlugin{})

pluginUnderTest.Init(messagePipe)
messagePipe.Process(core.NewMessage(core.AgentStarted, nil))

messagePipe.Process(core.NewMessage(core.NginxDetailProcUpdate, tutils.GetProcesses()))
messagePipe.Run()
messagePipe.RunWithoutInit()

binary.AssertExpectations(t)
cmdr.AssertExpectations(t)
Expand All @@ -716,7 +706,7 @@ func TestNginxDetailProcUpdate(t *testing.T) {
foundMessage = true
}
}
assert.Len(t, processedMessages, 4)
assert.Len(t, processedMessages, 3)
assert.True(t, foundMessage)
}

Expand Down Expand Up @@ -1048,6 +1038,7 @@ func TestBlock_ConfigApply(t *testing.T) {
binary := tutils.NewMockNginxBinary()
binary.On("UpdateNginxDetailsFromProcesses", env.Processes())
binary.On("GetNginxDetailsMapFromProcesses", env.Processes()).Return(tutils.GetDetailsMap())
binary.On("ReadConfig", mock.Anything, mock.Anything, mock.Anything).Return(&proto.NginxConfig{}, nil)
binary.On("Reload", mock.Anything, mock.Anything).Return(nil)

config := tutils.GetMockAgentConfig()
Expand Down
4 changes: 4 additions & 0 deletions test/integration/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func TestAPI_Metrics(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode())
assert.Contains(t, resp.String(), "system_cpu_system")
assert.NotContains(t, resp.String(), "test_fail_metric")
// Validate that the agent can call the stub status API
assert.Contains(t, resp.String(), "nginx_http_request_count")
// Validate that the agent can read the NGINX access logs
assert.Contains(t, resp.String(), "nginx_http_status_2xx")

metrics := tutils.ProcessResponse(resp)

Expand Down
2 changes: 1 addition & 1 deletion test/integration/api/nginx-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ metrics:
bulk_size: 20
# specify metrics poll interval
report_interval: 1m
collection_interval: 15s
collection_interval: 1s
mode: aggregated

# OSS NGINX default config path
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a94d816

Please sign in to comment.