Repository navigation
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1977 +/- ##
==========================================
- Coverage 88.97% 88.00% -0.97%
==========================================
Files 111 111
Lines 12457 12602 +145
==========================================
+ Hits 11084 11091 +7
- Misses 1371 1509 +138
Partials 2 2
... and 1 file with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
Content updates bypass existing request ordering and target-instance validation, allowing stale certificate writes and file mutations for nonexistent targets.
2 open issues
3 files not reviewed
api/grpc/mpi/v1/command.pb.go: Generated fileapi/grpc/mpi/v1/command.pb.validate.go: Generated fileinternal/file/filefakes/fake_file_manager_service_interface.go: Generated file
What changed in this PR
Adds a file-only update operation to NGINX Agent’s Management Plane Interface, supporting Gateway Fabric’s TLS certificate rotation without NGINX validation or reload.
Changes:
- Defines
ContentUpdateRequestand regenerates protocol bindings, validation, and documentation. - Routes content updates through command and NGINX plugins, reusing file processing and rollback logic.
- Extends test helpers, service fakes, and subscription assertions.
| File | Description |
|---|---|
| test/protos/management_plane_request.go | Adds a content-update request helper. |
| internal/nginx/nginx_plugin.go | Handles content updates and rollback without reloading. |
| internal/nginx/nginx_plugin_test.go | Checks the new topic subscription. |
| internal/file/filefakes/fake_file_manager_service_interface.go | Adds content-update support to the service fake. |
| internal/file/file_manager_service.go | Shares file processing between apply and content updates. |
| internal/command/command_plugin.go | Dispatches content updates with feature and server checks. |
| internal/bus/topics.go | Defines the content-update topic. |
| docs/proto/protos.md | Documents the new protocol operation. |
| api/grpc/mpi/v1/command.proto | Defines the request and response enum value. |
| api/grpc/mpi/v1/command.pb.validate.go | Adds generated request validation. |
| api/grpc/mpi/v1/command.pb.go | Adds generated protocol bindings. |
Review effort: Balanced
| func (cp *CommandPlugin) handleContentUpdateRequest(newCtx context.Context, message *mpi.ManagementPlaneRequest) { | ||
| cfg := cp.config() | ||
| if cfg.IsFeatureEnabled(pkgConfig.FeatureConfiguration) { | ||
| cp.messagePipe.Process(newCtx, &bus.Message{Topic: bus.ContentUpdateRequestTopic, Data: message}) |
There was a problem hiding this comment.
AI generated response by Claude
The queue exists to serialize config applies per instance (which involve validation + reload). Content updates are designed to be a lighter-weight operation (no reload). Whether they truly need to be serialized with config applies depends on the use case. The Copilot scenario (apply A queued, content update C runs before apply B, then B overwrites C's files) is theoretically possible but only if both operations target the same files — which seems unlikely given that content updates are intended for TLS certs while config applies are for nginx config.
There was a problem hiding this comment.
Open to opinions on this one. I do address with Claude's observation. Files affected by ContentUpdateRequest are logically separated on NGF's side, so they shouldn't go down the same code path.
salonichf5
left a comment
There was a problem hiding this comment.
great work with this just had a question
| func (cp *CommandPlugin) handleContentUpdateRequest(newCtx context.Context, message *mpi.ManagementPlaneRequest) { | ||
| cfg := cp.config() | ||
| if cfg.IsFeatureEnabled(pkgConfig.FeatureConfiguration) { | ||
| cp.messagePipe.Process(newCtx, &bus.Message{Topic: bus.ContentUpdateRequestTopic, Data: message}) | ||
| } else { | ||
| slog.WarnContext( | ||
| newCtx, | ||
| "Configuration feature disabled. Unable to process content update request", | ||
| "request", message, "enabled_features", cfg.Features, | ||
| ) | ||
|
|
||
| err := cp.commandService.SendDataPlaneResponse(newCtx, &mpi.DataPlaneResponse{ | ||
| MessageMeta: message.GetMessageMeta(), | ||
| CommandResponse: &mpi.CommandResponse{ | ||
| Status: mpi.CommandResponse_COMMAND_STATUS_FAILURE, | ||
| Message: "Content update failed", | ||
| Error: "Configuration feature is disabled", | ||
| }, | ||
| InstanceId: message.GetContentUpdateRequest().GetOverview().GetConfigVersion().GetInstanceId(), | ||
| }) | ||
| if err != nil { | ||
| slog.ErrorContext(newCtx, "Unable to send data plane response", "error", err) | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
question: so only check we do is check if that feature is allowed? Like is it a plus change or not?
There was a problem hiding this comment.
Yeah, this is just checking that the configuration feature is enabled. It doesn't check if we're using Plus or OSS though.
| API_ACTION_REQUEST = 5; | ||
| COMMAND_STATUS_REQUEST = 6; | ||
| UPDATE_AGENT_CONFIG_REQUEST = 7; | ||
| CONTENT_UPDATE_REQUEST = 8; |
There was a problem hiding this comment.
I'm assuming this is additive, but I want to make sure this API change is a compatible change. I'm not too familiar with protobuf APIs and what is considered additive vs breaking (this feels like additive).
There was a problem hiding this comment.
It should be additive. The oneof in the ManagementPlaneRequest message adds content_update_request = 10 which wasn't used by any of the other messages (9 was the number before it). This also keeps it backwards compatible since receivers running on older protobufs will just ignore the field, and older senders will never send it too (so no crashes, unhandled messages)

Proposed changes
This PR is one part of issue nginx/nginx-gateway-fabric#5795 to enable NGINX Gateway Fabric to update TLS Certificates without requiring NGINX to reload.
Please see PR nginx/nginx-gateway-fabric#6163 to see how this change integrates.
This change adds a new
CONTENT_UPDATE_REQUESTaction to the Management Plane Interface. This enables the management plane to push file content updates to the data plane without requesting NGINX config validation or a reload cycle.Issue nginx/nginx-gateway-fabric#5795
Checklist
Before creating a PR, run through this checklist and mark each as complete.
CONTRIBUTINGdocumentmake install-toolsand have attached any dependency changes to this pull requestREADME.md)