Skip to content

Commit 5e41d61

Browse files
committed
CP/DP Split: Fix empty plus file, blocking calls (#3078)
Problem: The NGINX Plus API conf file was empty when sending using OSS, which caused an error applying config. This also revealed an issue where we received multiple messages from agent, causing some channel blocking. Solution: Don't send the empty NGINX conf file if not running N+. Ignore responses from agent about rollbacks, so we only ever process a single response as expected.
1 parent 4bc7b39 commit 5e41d61

File tree

5 files changed

+35
-27
lines changed

5 files changed

+35
-27
lines changed

internal/mode/static/nginx/agent/agent.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@ func (n *NginxUpdaterImpl) UpdateConfig(
8585
deployment *Deployment,
8686
files []File,
8787
) bool {
88-
n.logger.Info("Sending nginx configuration to agent")
89-
9088
msg := deployment.SetFiles(files)
9189
applied := deployment.GetBroadcaster().Send(msg)
90+
if applied {
91+
n.logger.Info("Sent nginx configuration to agent")
92+
}
9293

9394
deployment.SetLatestConfigError(deployment.GetConfigurationStatus())
9495

internal/mode/static/nginx/agent/command.go

+25-6
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ func (cs *commandService) CreateConnection(
119119
// If any connection or unrecoverable errors occur, return and agent should re-establish a subscription.
120120
// If errors occur with applying the config, log and put those errors into the status queue to be written
121121
// to the Gateway status.
122+
//
123+
//nolint:gocyclo // could be room for improvement here
122124
func (cs *commandService) Subscribe(in pb.CommandService_SubscribeServer) error {
123125
ctx := in.Context()
124126

@@ -179,6 +181,7 @@ func (cs *commandService) Subscribe(in pb.CommandService_SubscribeServer) error
179181
panic(fmt.Sprintf("unknown request type %d", msg.Type))
180182
}
181183

184+
cs.logger.V(1).Info("Sending configuration to agent", "requestType", msg.Type)
182185
if err := msgr.Send(ctx, req); err != nil {
183186
cs.logger.Error(err, "error sending request to agent")
184187
deployment.SetPodErrorStatus(conn.PodName, err)
@@ -189,7 +192,10 @@ func (cs *commandService) Subscribe(in pb.CommandService_SubscribeServer) error
189192
case err = <-msgr.Errors():
190193
cs.logger.Error(err, "connection error", "pod", conn.PodName)
191194
deployment.SetPodErrorStatus(conn.PodName, err)
192-
channels.ResponseCh <- struct{}{}
195+
select {
196+
case channels.ResponseCh <- struct{}{}:
197+
default:
198+
}
193199

194200
if errors.Is(err, io.EOF) {
195201
return grpcStatus.Error(codes.Aborted, err.Error())
@@ -198,7 +204,11 @@ func (cs *commandService) Subscribe(in pb.CommandService_SubscribeServer) error
198204
case msg := <-msgr.Messages():
199205
res := msg.GetCommandResponse()
200206
if res.GetStatus() != pb.CommandResponse_COMMAND_STATUS_OK {
201-
err := fmt.Errorf("bad response from agent: msg: %s; error: %s", res.GetMessage(), res.GetError())
207+
if isRollbackMessage(res.GetMessage()) {
208+
// we don't care about these messages, so ignore them
209+
continue
210+
}
211+
err := fmt.Errorf("msg: %s; error: %s", res.GetMessage(), res.GetError())
202212
deployment.SetPodErrorStatus(conn.PodName, err)
203213
} else {
204214
deployment.SetPodErrorStatus(conn.PodName, nil)
@@ -268,6 +278,8 @@ func (cs *commandService) setInitialConfig(
268278
for _, action := range deployment.GetNGINXPlusActions() {
269279
// retry the API update request because sometimes nginx isn't quite ready after the config apply reload
270280
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
281+
var overallUpstreamApplyErr error
282+
271283
if err := wait.PollUntilContextCancel(
272284
timeoutCtx,
273285
500*time.Millisecond,
@@ -287,13 +299,14 @@ func (cs *commandService) setInitialConfig(
287299
}
288300

289301
if upstreamApplyErr != nil {
290-
return false, nil //nolint:nilerr // this error is collected at the end
302+
overallUpstreamApplyErr = errors.Join(overallUpstreamApplyErr, upstreamApplyErr)
303+
return false, nil
291304
}
292305
return true, nil
293306
},
294307
); err != nil {
295-
if strings.Contains(err.Error(), "bad response from agent") {
296-
errs = append(errs, err)
308+
if overallUpstreamApplyErr != nil {
309+
errs = append(errs, overallUpstreamApplyErr)
297310
} else {
298311
cancel()
299312
return err
@@ -330,7 +343,7 @@ func (cs *commandService) waitForInitialConfigApply(
330343
case msg := <-msgr.Messages():
331344
res := msg.GetCommandResponse()
332345
if res.GetStatus() != pb.CommandResponse_COMMAND_STATUS_OK {
333-
applyErr := fmt.Errorf("bad response from agent: msg: %s; error: %s", res.GetMessage(), res.GetError())
346+
applyErr := fmt.Errorf("msg: %s; error: %s", res.GetMessage(), res.GetError())
334347
return applyErr, nil
335348
}
336349

@@ -379,6 +392,12 @@ func buildRequest(fileOverviews []*pb.File, instanceID, version string) *pb.Mana
379392
}
380393
}
381394

395+
func isRollbackMessage(msg string) bool {
396+
msgToLower := strings.ToLower(msg)
397+
return strings.Contains(msgToLower, "rollback successful") ||
398+
strings.Contains(msgToLower, "rollback failed")
399+
}
400+
382401
func buildPlusAPIRequest(action *pb.NGINXPlusAction, instanceID string) *pb.ManagementPlaneRequest {
383402
return &pb.ManagementPlaneRequest{
384403
MessageMeta: &pb.MessageMeta{

internal/mode/static/nginx/agent/file.go

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ func (fs *fileService) GetFile(
7575
return nil, status.Errorf(codes.NotFound, "file not found")
7676
}
7777

78+
fs.logger.V(1).Info("Getting file for agent", "file", filename)
79+
7880
return &pb.GetFileResponse{
7981
Contents: &pb.FileContents{
8082
Contents: contents,

internal/mode/static/nginx/config/plus_api.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import (
1010
var plusAPITemplate = gotemplate.Must(gotemplate.New("plusAPI").Parse(plusAPITemplateText))
1111

1212
func executePlusAPI(conf dataplane.Configuration) []executeResult {
13-
result := executeResult{
14-
dest: nginxPlusConfigFile,
15-
}
13+
var result executeResult
1614
// if AllowedAddresses is empty, it means that we are not running on nginx plus, and we don't want this generated
1715
if conf.NginxPlus.AllowedAddresses != nil {
1816
result = executeResult{
1917
dest: nginxPlusConfigFile,
2018
data: helpers.MustExecuteTemplate(plusAPITemplate, conf.NginxPlus),
2119
}
20+
} else {
21+
return nil
2222
}
2323

2424
return []executeResult{result}

internal/mode/static/nginx/config/plus_api_test.go

+2-16
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,7 @@ func TestExecutePlusAPI_EmptyNginxPlus(t *testing.T) {
4343
}
4444

4545
g := NewWithT(t)
46-
expSubStrings := map[string]int{
47-
"listen unix:/var/run/nginx/nginx-plus-api.sock;": 0,
48-
"access_log off;": 0,
49-
"api write=on;": 0,
50-
"listen 8765;": 0,
51-
"root /usr/share/nginx/html;": 0,
52-
"allow 127.0.0.1;": 0,
53-
"deny all;": 0,
54-
"location = /dashboard.html {}": 0,
55-
"api write=off;": 0,
56-
}
5746

58-
for expSubStr, expCount := range expSubStrings {
59-
res := executePlusAPI(conf)
60-
g.Expect(res).To(HaveLen(1))
61-
g.Expect(expCount).To(Equal(strings.Count(string(res[0].data), expSubStr)))
62-
}
47+
res := executePlusAPI(conf)
48+
g.Expect(res).To(BeNil())
6349
}

0 commit comments

Comments
 (0)