@@ -34,9 +34,9 @@ const (
3434 configApplyErrorMessage = "failed to parse config invalid " +
3535 "number of arguments in \" worker_processes\" directive in /etc/nginx/nginx.conf:1"
3636
37- retryCount = 5
38- retryWaitTime = 4 * time .Second
39- retryMaxWaitTime = 5 * time .Second
37+ retryCount = 8
38+ retryWaitTime = 5 * time .Second
39+ retryMaxWaitTime = 6 * time .Second
4040)
4141
4242var (
@@ -189,6 +189,88 @@ func setupLocalEnvironment(tb testing.TB) {
189189 }(tb )
190190}
191191
192+ func TestGrpc_Reconnection (t * testing.T ) {
193+ ctx := context .Background ()
194+ teardownTest := setupConnectionTest (t , false , false )
195+ defer teardownTest (t )
196+
197+ timeout := 15 * time .Second
198+
199+ originalID := verifyConnection (t , 2 )
200+
201+ stopErr := mockManagementPlaneGrpcContainer .Stop (ctx , & timeout )
202+
203+ require .NoError (t , stopErr )
204+
205+ startErr := mockManagementPlaneGrpcContainer .Start (ctx )
206+ require .NoError (t , startErr )
207+
208+ ipAddress , err := mockManagementPlaneGrpcContainer .Host (ctx )
209+ require .NoError (t , err )
210+ ports , err := mockManagementPlaneGrpcContainer .Ports (ctx )
211+ require .NoError (t , err )
212+ mockManagementPlaneAPIAddress = net .JoinHostPort (ipAddress , ports ["9093/tcp" ][0 ].HostPort )
213+
214+ currentID := verifyConnection (t , 2 )
215+ assert .Equal (t , originalID , currentID )
216+ }
217+
218+ // Verify that the agent sends a connection request and an update data plane status request
219+ func TestGrpc_StartUp (t * testing.T ) {
220+ teardownTest := setupConnectionTest (t , true , false )
221+ defer teardownTest (t )
222+
223+ verifyConnection (t , 2 )
224+ assert .False (t , t .Failed ())
225+ verifyUpdateDataPlaneHealth (t )
226+ }
227+
228+ func TestGrpc_ConfigUpload (t * testing.T ) {
229+ teardownTest := setupConnectionTest (t , true , false )
230+ defer teardownTest (t )
231+
232+ nginxInstanceID := verifyConnection (t , 2 )
233+ assert .False (t , t .Failed ())
234+
235+ responses := getManagementPlaneResponses (t , 1 )
236+
237+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [0 ].GetCommandResponse ().GetStatus ())
238+ assert .Equal (t , "Successfully updated all files" , responses [0 ].GetCommandResponse ().GetMessage ())
239+
240+ request := fmt .Sprintf (`{
241+ "message_meta": {
242+ "message_id": "5d0fa83e-351c-4009-90cd-1f2acce2d184",
243+ "correlation_id": "79794c1c-8e91-47c1-a92c-b9a0c3f1a263",
244+ "timestamp": "2023-01-15T01:30:15.01Z"
245+ },
246+ "config_upload_request": {
247+ "overview" : {
248+ "config_version": {
249+ "instance_id": "%s"
250+ }
251+ }
252+ }
253+ }` , nginxInstanceID )
254+
255+ t .Logf ("Sending config upload request: %s" , request )
256+
257+ client := resty .New ()
258+ client .SetRetryCount (retryCount ).SetRetryWaitTime (retryWaitTime ).SetRetryMaxWaitTime (retryMaxWaitTime )
259+
260+ url := fmt .Sprintf ("http://%s/api/v1/requests" , mockManagementPlaneAPIAddress )
261+ resp , err := client .R ().EnableTrace ().SetBody (request ).Post (url )
262+
263+ require .NoError (t , err )
264+ assert .Equal (t , http .StatusOK , resp .StatusCode ())
265+
266+ responses = getManagementPlaneResponses (t , 2 )
267+
268+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [0 ].GetCommandResponse ().GetStatus ())
269+ assert .Equal (t , "Successfully updated all files" , responses [0 ].GetCommandResponse ().GetMessage ())
270+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [1 ].GetCommandResponse ().GetStatus ())
271+ assert .Equal (t , "Successfully updated all files" , responses [1 ].GetCommandResponse ().GetMessage ())
272+ }
273+
192274func TestGrpc_ConfigApply (t * testing.T ) {
193275 ctx := context .Background ()
194276 teardownTest := setupConnectionTest (t , false , false )
@@ -232,15 +314,15 @@ func TestGrpc_ConfigApply(t *testing.T) {
232314 responses = getManagementPlaneResponses (t , 2 )
233315 t .Logf ("Config apply responses: %v" , responses )
234316
235- if len (responses ) > 1 {
236- t .Logf ("Config apply responses more than 1" )
237- sort .Slice (responses , func (i , j int ) bool {
238- return responses [i ].GetCommandResponse ().GetMessage () < responses [j ].GetCommandResponse ().GetMessage ()
239- })
240- }
317+ t .Logf ("Config apply responses more than 1" )
318+ sort .Slice (responses , func (i , j int ) bool {
319+ return responses [i ].GetCommandResponse ().GetMessage () < responses [j ].GetCommandResponse ().GetMessage ()
320+ })
241321
242322 assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [0 ].GetCommandResponse ().GetStatus ())
243323 assert .Equal (t , "Config apply successful" , responses [0 ].GetCommandResponse ().GetMessage ())
324+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [1 ].GetCommandResponse ().GetStatus ())
325+ assert .Equal (t , "Successfully updated all files" , responses [1 ].GetCommandResponse ().GetMessage ())
244326 })
245327
246328 t .Run ("Test 3: Invalid config" , func (t * testing.T ) {
@@ -283,6 +365,67 @@ func TestGrpc_ConfigApply(t *testing.T) {
283365 })
284366}
285367
368+ func TestGrpc_FileWatcher (t * testing.T ) {
369+ ctx := context .Background ()
370+ teardownTest := setupConnectionTest (t , true , false )
371+ defer teardownTest (t )
372+
373+ verifyConnection (t , 2 )
374+ assert .False (t , t .Failed ())
375+
376+ err := container .CopyFileToContainer (
377+ ctx ,
378+ "../config/nginx/nginx-with-server-block-access-log.conf" ,
379+ "/etc/nginx/nginx.conf" ,
380+ 0o666 ,
381+ )
382+ require .NoError (t , err )
383+
384+ responses := getManagementPlaneResponses (t , 2 )
385+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [0 ].GetCommandResponse ().GetStatus ())
386+ assert .Equal (t , "Successfully updated all files" , responses [0 ].GetCommandResponse ().GetMessage ())
387+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [1 ].GetCommandResponse ().GetStatus ())
388+ assert .Equal (t , "Successfully updated all files" , responses [1 ].GetCommandResponse ().GetMessage ())
389+
390+ verifyUpdateDataPlaneStatus (t )
391+ }
392+
393+ func TestGrpc_DataplaneHealthRequest (t * testing.T ) {
394+ teardownTest := setupConnectionTest (t , true , false )
395+ defer teardownTest (t )
396+
397+ verifyConnection (t , 2 )
398+
399+ responses := getManagementPlaneResponses (t , 1 )
400+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [0 ].GetCommandResponse ().GetStatus ())
401+ assert .Equal (t , "Successfully updated all files" , responses [0 ].GetCommandResponse ().GetMessage ())
402+
403+ assert .False (t , t .Failed ())
404+
405+ request := `{
406+ "message_meta": {
407+ "message_id": "5d0fa83e-351c-4009-90cd-1f2acce2d184",
408+ "correlation_id": "79794c1c-8e91-47c1-a92c-b9a0c3f1a263",
409+ "timestamp": "2023-01-15T01:30:15.01Z"
410+ },
411+ "health_request": {}
412+ }`
413+
414+ client := resty .New ()
415+ client .SetRetryCount (retryCount ).SetRetryWaitTime (retryWaitTime ).SetRetryMaxWaitTime (retryMaxWaitTime )
416+
417+ url := fmt .Sprintf ("http://%s/api/v1/requests" , mockManagementPlaneAPIAddress )
418+ resp , err := client .R ().EnableTrace ().SetBody (request ).Post (url )
419+
420+ require .NoError (t , err )
421+ assert .Equal (t , http .StatusOK , resp .StatusCode ())
422+
423+ responses = getManagementPlaneResponses (t , 2 )
424+
425+ assert .Equal (t , mpi .CommandResponse_COMMAND_STATUS_OK , responses [1 ].GetCommandResponse ().GetStatus ())
426+ assert .Equal (t , "Successfully sent the health status update" , responses [1 ].GetCommandResponse ().GetMessage ())
427+ }
428+
286429func performConfigApply (t * testing.T , nginxInstanceID string ) {
287430 t .Helper ()
288431
@@ -488,3 +631,156 @@ func verifyConnection(t *testing.T, instancesLength int) string {
488631
489632 return nginxInstanceID
490633}
634+
635+ func verifyUpdateDataPlaneHealth (t * testing.T ) {
636+ t .Helper ()
637+
638+ client := resty .New ()
639+
640+ client .SetRetryCount (retryCount ).SetRetryWaitTime (retryWaitTime ).SetRetryMaxWaitTime (retryMaxWaitTime )
641+
642+ client .AddRetryCondition (
643+
644+ func (r * resty.Response , err error ) bool {
645+ return r .StatusCode () == http .StatusNotFound
646+ },
647+ )
648+
649+ url := fmt .Sprintf ("http://%s/api/v1/health" , mockManagementPlaneAPIAddress )
650+
651+ resp , err := client .R ().EnableTrace ().Get (url )
652+
653+ require .NoError (t , err )
654+
655+ assert .Equal (t , http .StatusOK , resp .StatusCode ())
656+
657+ responseData := resp .Body ()
658+
659+ t .Logf ("Response: %s" , string (responseData ))
660+
661+ assert .True (t , json .Valid (responseData ))
662+
663+ pb := protojson.UnmarshalOptions {DiscardUnknown : true }
664+
665+ updateDataPlaneHealthRequest := mpi.UpdateDataPlaneHealthRequest {}
666+
667+ unmarshalErr := pb .Unmarshal (responseData , & updateDataPlaneHealthRequest )
668+
669+ require .NoError (t , unmarshalErr )
670+
671+ t .Logf ("UpdateDataPlaneHealthRequest: %v" , & updateDataPlaneHealthRequest )
672+
673+ assert .NotNil (t , & updateDataPlaneHealthRequest )
674+
675+ // Verify message metadata
676+
677+ messageMeta := updateDataPlaneHealthRequest .GetMessageMeta ()
678+
679+ assert .NotEmpty (t , messageMeta .GetCorrelationId ())
680+
681+ assert .NotEmpty (t , messageMeta .GetMessageId ())
682+
683+ assert .NotEmpty (t , messageMeta .GetTimestamp ())
684+
685+ healths := updateDataPlaneHealthRequest .GetInstanceHealths ()
686+
687+ assert .Len (t , healths , 1 )
688+
689+ // Verify health metadata
690+
691+ assert .NotEmpty (t , healths [0 ].GetInstanceId ())
692+
693+ assert .Equal (t , mpi .InstanceHealth_INSTANCE_HEALTH_STATUS_HEALTHY , healths [0 ].GetInstanceHealthStatus ())
694+ }
695+
696+ func verifyUpdateDataPlaneStatus (t * testing.T ) {
697+ t .Helper ()
698+
699+ client := resty .New ()
700+
701+ client .SetRetryCount (3 ).SetRetryWaitTime (50 * time .Millisecond ).SetRetryMaxWaitTime (200 * time .Millisecond )
702+
703+ url := fmt .Sprintf ("http://%s/api/v1/status" , mockManagementPlaneAPIAddress )
704+
705+ resp , err := client .R ().EnableTrace ().Get (url )
706+
707+ require .NoError (t , err )
708+
709+ assert .Equal (t , http .StatusOK , resp .StatusCode ())
710+
711+ updateDataPlaneStatusRequest := mpi.UpdateDataPlaneStatusRequest {}
712+
713+ responseData := resp .Body ()
714+
715+ t .Logf ("Response: %s" , string (responseData ))
716+
717+ assert .True (t , json .Valid (responseData ))
718+
719+ pb := protojson.UnmarshalOptions {DiscardUnknown : true }
720+
721+ unmarshalErr := pb .Unmarshal (responseData , & updateDataPlaneStatusRequest )
722+
723+ require .NoError (t , unmarshalErr )
724+
725+ t .Logf ("UpdateDataPlaneStatusRequest: %v" , & updateDataPlaneStatusRequest )
726+
727+ assert .NotNil (t , & updateDataPlaneStatusRequest )
728+
729+ // Verify message metadata
730+
731+ messageMeta := updateDataPlaneStatusRequest .GetMessageMeta ()
732+
733+ assert .NotEmpty (t , messageMeta .GetCorrelationId ())
734+
735+ assert .NotEmpty (t , messageMeta .GetMessageId ())
736+
737+ assert .NotEmpty (t , messageMeta .GetTimestamp ())
738+
739+ instances := updateDataPlaneStatusRequest .GetResource ().GetInstances ()
740+
741+ sort .Slice (instances , func (i , j int ) bool {
742+ return instances [i ].GetInstanceMeta ().GetInstanceType () < instances [j ].GetInstanceMeta ().GetInstanceType ()
743+ })
744+
745+ assert .Len (t , instances , 2 )
746+
747+ // Verify agent instance metadata
748+
749+ assert .NotEmpty (t , instances [0 ].GetInstanceMeta ().GetInstanceId ())
750+
751+ assert .Equal (t , mpi .InstanceMeta_INSTANCE_TYPE_AGENT , instances [0 ].GetInstanceMeta ().GetInstanceType ())
752+
753+ assert .NotEmpty (t , instances [0 ].GetInstanceMeta ().GetVersion ())
754+
755+ // Verify agent instance configuration
756+
757+ assert .Empty (t , instances [0 ].GetInstanceConfig ().GetActions ())
758+
759+ assert .NotEmpty (t , instances [0 ].GetInstanceRuntime ().GetProcessId ())
760+
761+ assert .Equal (t , "/usr/bin/nginx-agent" , instances [0 ].GetInstanceRuntime ().GetBinaryPath ())
762+
763+ assert .Equal (t , "/etc/nginx-agent/nginx-agent.conf" , instances [0 ].GetInstanceRuntime ().GetConfigPath ())
764+
765+ // Verify NGINX instance metadata
766+
767+ assert .NotEmpty (t , instances [1 ].GetInstanceMeta ().GetInstanceId ())
768+
769+ if os .Getenv ("IMAGE_PATH" ) == "/nginx-plus/agent" {
770+ assert .Equal (t , mpi .InstanceMeta_INSTANCE_TYPE_NGINX_PLUS , instances [1 ].GetInstanceMeta ().GetInstanceType ())
771+ } else {
772+ assert .Equal (t , mpi .InstanceMeta_INSTANCE_TYPE_NGINX , instances [1 ].GetInstanceMeta ().GetInstanceType ())
773+ }
774+
775+ assert .NotEmpty (t , instances [1 ].GetInstanceMeta ().GetVersion ())
776+
777+ // Verify NGINX instance configuration
778+
779+ assert .Empty (t , instances [1 ].GetInstanceConfig ().GetActions ())
780+
781+ assert .NotEmpty (t , instances [1 ].GetInstanceRuntime ().GetProcessId ())
782+
783+ assert .Equal (t , "/usr/sbin/nginx" , instances [1 ].GetInstanceRuntime ().GetBinaryPath ())
784+
785+ assert .Equal (t , "/etc/nginx/nginx.conf" , instances [1 ].GetInstanceRuntime ().GetConfigPath ())
786+ }
0 commit comments