diff --git a/Makefile b/Makefile index d436d3582..91d04147b 100644 --- a/Makefile +++ b/Makefile @@ -164,7 +164,7 @@ integration-test: $(SELECTED_PACKAGE) build-mock-management-plane-grpc TEST_ENV="Container" CONTAINER_OS_TYPE=$(CONTAINER_OS_TYPE) BUILD_TARGET="install-agent-local" CONTAINER_NGINX_IMAGE_REGISTRY=${CONTAINER_NGINX_IMAGE_REGISTRY} \ PACKAGES_REPO=$(OSS_PACKAGES_REPO) PACKAGE_NAME=$(PACKAGE_NAME) BASE_IMAGE=$(BASE_IMAGE) DOCKERFILE_PATH=$(DOCKERFILE_PATH) IMAGE_PATH=$(IMAGE_PATH) TAG=${IMAGE_TAG} \ OS_VERSION=$(OS_VERSION) OS_RELEASE=$(OS_RELEASE) \ - go test -v ./test/integration/installuninstall ./test/integration/managementplane ./test/integration/auxiliarycommandserver ./test/integration/nginxless + go test -v ./test/integration/installuninstall ./test/integration/managementplane ./test/integration/auxiliarycommandserver ./test/integration/nginxless official-image-integration-test: $(SELECTED_PACKAGE) build-mock-management-plane-grpc TEST_ENV="Container" CONTAINER_OS_TYPE=$(CONTAINER_OS_TYPE) CONTAINER_NGINX_IMAGE_REGISTRY=${CONTAINER_NGINX_IMAGE_REGISTRY} BUILD_TARGET="install" \ diff --git a/test/helpers/test_containers_utils.go b/test/helpers/test_containers_utils.go index f90ab3182..7eca7eec5 100644 --- a/test/helpers/test_containers_utils.go +++ b/test/helpers/test_containers_utils.go @@ -86,6 +86,11 @@ func StartContainer( ContainerFilePath: "/etc/nginx/nginx.conf", FileMode: configFilePermissions, }, + { + HostFilePath: "../../config/nginx/mime.types", + ContainerFilePath: "/etc/nginx/mime.types", + FileMode: configFilePermissions, + }, }, } diff --git a/test/integration/managementplane/config_apply_test.go b/test/integration/managementplane/config_apply_test.go index 01a9405eb..fba48884f 100644 --- a/test/integration/managementplane/config_apply_test.go +++ b/test/integration/managementplane/config_apply_test.go @@ -13,9 +13,10 @@ import ( "sort" "testing" + mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" + "github.com/nginx/agent/v3/internal/model" "github.com/nginx/agent/v3/test/integration/utils" - mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" "github.com/stretchr/testify/suite" ) @@ -26,9 +27,10 @@ const ( type ConfigApplyTestSuite struct { suite.Suite - ctx context.Context - teardownTest func(testing.TB) - nginxInstanceID string + ctx context.Context + teardownTest func(testing.TB) + nginxInstanceID string + mockManagementConfigDir string } type ConfigApplyChunkingTestSuite struct { @@ -44,6 +46,9 @@ func (s *ConfigApplyTestSuite) SetupSuite() { s.teardownTest = utils.SetupConnectionTest(s.T(), false, false, false, "../../config/agent/nginx-config-with-grpc-client.conf") s.nginxInstanceID = utils.VerifyConnection(s.T(), 2, utils.MockManagementPlaneAPIAddress) + + s.mockManagementConfigDir = "/mock-management-plane-grpc/config/" + s.nginxInstanceID + responses := utils.ManagementPlaneResponses(s.T(), 1, utils.MockManagementPlaneAPIAddress) s.Require().Equal(mpi.CommandResponse_COMMAND_STATUS_OK, responses[0].GetCommandResponse().GetStatus()) s.Require().Equal("Successfully updated all files", responses[0].GetCommandResponse().GetMessage()) @@ -58,12 +63,39 @@ func (s *ConfigApplyTestSuite) TearDownTest() { utils.ClearManagementPlaneResponses(s.T(), utils.MockManagementPlaneAPIAddress) } +// Config Apply with no changes to config func (s *ConfigApplyTestSuite) TestConfigApply_Test1_TestNoConfigChanges() { slog.Info("starting config apply no config changes test") utils.PerformConfigApply(s.T(), s.nginxInstanceID, utils.MockManagementPlaneAPIAddress) responses := utils.ManagementPlaneResponses(s.T(), 2, utils.MockManagementPlaneAPIAddress) s.T().Logf("Config apply responses: %v", responses) + manifestFiles := map[string]*model.ManifestFile{ + "/etc/nginx/mime.types": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/mime.types", + Hash: "b5XR19dePAcpB9hFYipp0jEQ0SZsFv8SKzEJuLIfOuk=", + Size: 5349, + Referenced: true, + }, + }, + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "gJ1slpIAUmHAiSo5ZIalKvE40b1hJCgaXasQOMab6kc=", + Size: 1172, + Referenced: true, + }, + }, + } + + if os.Getenv("IMAGE_PATH") == "/nginx-plus/agent" { + manifestFiles["/etc/nginx/nginx.conf"].ManifestFileMeta.Hash = "/SWXYYenb2EcJNg6fiuzlkdj91nBdsMdF1vLm7Wybvc=" + manifestFiles["/etc/nginx/nginx.conf"].ManifestFileMeta.Size = 1218 + } + + utils.CheckManifestFile(s.T(), utils.Container, manifestFiles) + s.Equal(mpi.CommandResponse_COMMAND_STATUS_OK, responses[0].GetCommandResponse().GetStatus()) s.Equal("Successfully updated all files", responses[0].GetCommandResponse().GetMessage()) s.Equal(mpi.CommandResponse_COMMAND_STATUS_OK, responses[1].GetCommandResponse().GetStatus()) @@ -71,17 +103,27 @@ func (s *ConfigApplyTestSuite) TestConfigApply_Test1_TestNoConfigChanges() { slog.Info("finished config apply no config changes test") } +// Config apply - Add, Update and Delete Referenced file from Management Plane func (s *ConfigApplyTestSuite) TestConfigApply_Test2_TestValidConfig() { slog.Info("starting config apply valid config test") - newConfigFile := "../../config/nginx/nginx-with-test-location.conf" + // Update nginx.conf + utils.WriteConfigFileMock(s.T(), s.nginxInstanceID, "/etc/nginx/test/test.conf", + "/etc/nginx/test/test.conf", "/etc/nginx/test/test.conf") + + // Delete mime.types + code, _, removeErr := utils.MockManagementPlaneGrpcContainer.Exec(context.Background(), []string{ + "rm", + s.mockManagementConfigDir + "/etc/nginx/mime.types", + }) - if os.Getenv("IMAGE_PATH") == "/nginx-plus/agent" { - newConfigFile = "../../config/nginx/nginx-plus-with-test-location.conf" - } + s.Require().NoError(removeErr) + s.Equal(0, code) + + // Add test.conf err := utils.MockManagementPlaneGrpcContainer.CopyFileToContainer( s.ctx, - newConfigFile, - fmt.Sprintf("/mock-management-plane-grpc/config/%s/etc/nginx/nginx.conf", s.nginxInstanceID), + "configs/test.conf", + s.mockManagementConfigDir+"/etc/nginx/test/test.conf", 0o666, ) s.Require().NoError(err) @@ -90,6 +132,27 @@ func (s *ConfigApplyTestSuite) TestConfigApply_Test2_TestValidConfig() { responses := utils.ManagementPlaneResponses(s.T(), 2, utils.MockManagementPlaneAPIAddress) s.T().Logf("Config apply responses: %v", responses) + manifestFiles := map[string]*model.ManifestFile{ + "/etc/nginx/test/test.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/test/test.conf", + Hash: "BF1ztX59kP/N81XcIv3JlPp82j7gzTsVIk2RGxdAta8=", + Size: 175, + Referenced: true, + }, + }, + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "/SsQwpZTdJVRa1+bex7OdZoogvVT0tnTOwwO59vpsoM=", + Size: 1360, + Referenced: true, + }, + }, + } + + utils.CheckManifestFile(s.T(), utils.Container, manifestFiles) + sort.Slice(responses, func(i, j int) bool { return responses[i].GetCommandResponse().GetMessage() < responses[j].GetCommandResponse().GetMessage() }) @@ -101,7 +164,60 @@ func (s *ConfigApplyTestSuite) TestConfigApply_Test2_TestValidConfig() { slog.Info("finished config apply valid config test") } -func (s *ConfigApplyTestSuite) TestConfigApply_Test3_TestInvalidConfig() { +// Add, Update and Delete file on DataPlane - Trigger update file overview +func (s *ConfigApplyTestSuite) TestConfigApply_Test3_DataPlaneUpdate() { + slog.Info("starting config apply data plane update test") + // Add test2.conf to dataplane + err := utils.Container.CopyFileToContainer( + s.ctx, + "configs/test2.conf", + "/etc/nginx/test/test2.conf", + 0o666, + ) + s.Require().NoError(err) + + // Delete test.conf from dataplane + code, _, removeErr := utils.Container.Exec(context.Background(), []string{ + "rm", + "/etc/nginx/test/test.conf", + }) + + s.Require().NoError(removeErr) + s.Equal(0, code) + + // Update nginx.conf to reference new file + utils.WriteConfigFileDataplane(s.T(), "/etc/nginx/test/test2.conf", + "/etc/nginx/test/test2.conf", "/etc/nginx/test/test2.conf") + + manifestFiles := map[string]*model.ManifestFile{ + "/etc/nginx/test/test2.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/test/test2.conf", + Hash: "mV4nVTx8BObqxSwcJprkJesiCJH+oTO89RgZxFuFEJo=", + Size: 136, + Referenced: true, + }, + }, + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "q8Zf3Cv5UOAVyfigx5Mr4mwJpLIxApN1H0UzYKKTAiU=", + Size: 1363, + Referenced: true, + }, + }, + } + + responses := utils.ManagementPlaneResponses(s.T(), 1, utils.MockManagementPlaneAPIAddress) + + utils.CheckManifestFile(s.T(), utils.Container, manifestFiles) + + s.Equal(mpi.CommandResponse_COMMAND_STATUS_OK, responses[0].GetCommandResponse().GetStatus()) + s.Equal("Successfully updated all files", responses[0].GetCommandResponse().GetMessage()) + slog.Info("finished config apply data plane update test") +} + +func (s *ConfigApplyTestSuite) TestConfigApply_Test4_TestInvalidConfig() { slog.Info("starting config apply invalid config test") err := utils.MockManagementPlaneGrpcContainer.CopyFileToContainer( s.ctx, @@ -116,6 +232,27 @@ func (s *ConfigApplyTestSuite) TestConfigApply_Test3_TestInvalidConfig() { responses := utils.ManagementPlaneResponses(s.T(), 2, utils.MockManagementPlaneAPIAddress) s.T().Logf("Config apply responses: %v", responses) + manifestFiles := map[string]*model.ManifestFile{ + "/etc/nginx/test/test2.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/test/test2.conf", + Hash: "mV4nVTx8BObqxSwcJprkJesiCJH+oTO89RgZxFuFEJo=", + Size: 136, + Referenced: true, + }, + }, + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "q8Zf3Cv5UOAVyfigx5Mr4mwJpLIxApN1H0UzYKKTAiU=", + Size: 1363, + Referenced: true, + }, + }, + } + + utils.CheckManifestFile(s.T(), utils.Container, manifestFiles) + s.Equal(mpi.CommandResponse_COMMAND_STATUS_ERROR, responses[0].GetCommandResponse().GetStatus()) s.Equal("Config apply failed, rolling back config", responses[0].GetCommandResponse().GetMessage()) s.Equal(configApplyErrorMessage, responses[0].GetCommandResponse().GetError()) @@ -125,13 +262,33 @@ func (s *ConfigApplyTestSuite) TestConfigApply_Test3_TestInvalidConfig() { slog.Info("finished config apply invalid config test") } -func (s *ConfigApplyTestSuite) TestConfigApply_Test4_TestFileNotInAllowedDirectory() { +func (s *ConfigApplyTestSuite) TestConfigApply_Test5_TestFileNotInAllowedDirectory() { slog.Info("starting config apply file not in allowed directory test") utils.PerformInvalidConfigApply(s.T(), s.nginxInstanceID) responses := utils.ManagementPlaneResponses(s.T(), 1, utils.MockManagementPlaneAPIAddress) s.T().Logf("Config apply responses: %v", responses) + manifestFiles := map[string]*model.ManifestFile{ + "/etc/nginx/test/test2.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/test/test2.conf", + Hash: "mV4nVTx8BObqxSwcJprkJesiCJH+oTO89RgZxFuFEJo=", + Size: 136, + Referenced: true, + }, + }, + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "q8Zf3Cv5UOAVyfigx5Mr4mwJpLIxApN1H0UzYKKTAiU=", + Size: 1363, + Referenced: true, + }, + }, + } + utils.CheckManifestFile(s.T(), utils.Container, manifestFiles) + s.Equal(mpi.CommandResponse_COMMAND_STATUS_FAILURE, responses[0].GetCommandResponse().GetStatus()) s.Equal("Config apply failed", responses[0].GetCommandResponse().GetMessage()) s.Equal( @@ -179,6 +336,27 @@ func (s *ConfigApplyChunkingTestSuite) TestConfigApplyChunking() { return responses[i].GetCommandResponse().GetMessage() < responses[j].GetCommandResponse().GetMessage() }) + manifestFiles := map[string]*model.ManifestFile{ + "/etc/nginx/mime.types": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/mime.types", + Hash: "b5XR19dePAcpB9hFYipp0jEQ0SZsFv8SKzEJuLIfOuk=", + Size: 5349, + Referenced: true, + }, + }, + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "dfDpjGOjOhWWhX43y/d+zBulXCisx+BVYj2eEEud6ac=", + Size: 886910, + Referenced: true, + }, + }, + } + + utils.CheckManifestFile(s.T(), utils.Container, manifestFiles) + s.Equal(mpi.CommandResponse_COMMAND_STATUS_OK, responses[0].GetCommandResponse().GetStatus()) s.Equal("Config apply successful", responses[0].GetCommandResponse().GetMessage()) s.Equal(mpi.CommandResponse_COMMAND_STATUS_OK, responses[1].GetCommandResponse().GetStatus()) diff --git a/test/integration/managementplane/configs/config.go b/test/integration/managementplane/configs/config.go new file mode 100644 index 000000000..496e66497 --- /dev/null +++ b/test/integration/managementplane/configs/config.go @@ -0,0 +1,25 @@ +// Copyright (c) F5, Inc. +// +// This source code is licensed under the Apache License, Version 2.0 license found in the +// LICENSE file in the root directory of this source tree. + +package configs + +import ( + _ "embed" + "fmt" +) + +//go:embed nginx.conf +var embedNginxConfWithMultipleInclude string + +//go:embed nginx.conf +var embedNginxPlusConfWithMultipleInclude string + +func NginxConfigWithMultipleInclude(includeFile1, includeFile2, includeFile3 string) string { + return fmt.Sprintf(embedNginxConfWithMultipleInclude, includeFile1, includeFile2, includeFile3) +} + +func NginxPlusConfigWithMultipleInclude(includeFile1, includeFile2, includeFile3 string) string { + return fmt.Sprintf(embedNginxPlusConfWithMultipleInclude, includeFile1, includeFile2, includeFile3) +} diff --git a/test/integration/managementplane/configs/mime.types b/test/integration/managementplane/configs/mime.types new file mode 100644 index 000000000..1c00d701a --- /dev/null +++ b/test/integration/managementplane/configs/mime.types @@ -0,0 +1,99 @@ + +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/avif avif; + image/png png; + image/svg+xml svg svgz; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/webp webp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + + font/woff woff; + font/woff2 woff2; + + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.oasis.opendocument.graphics odg; + application/vnd.oasis.opendocument.presentation odp; + application/vnd.oasis.opendocument.spreadsheet ods; + application/vnd.oasis.opendocument.text odt; + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx; + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx; + application/vnd.wap.wmlc wmlc; + application/wasm wasm; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/test/integration/managementplane/configs/nginx-plus.conf b/test/integration/managementplane/configs/nginx-plus.conf new file mode 100644 index 000000000..c4764e189 --- /dev/null +++ b/test/integration/managementplane/configs/nginx-plus.conf @@ -0,0 +1,54 @@ +worker_processes 1; +error_log /var/log/nginx/error.log; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for" ' + '"$bytes_sent" "$request_length" "$request_time" ' + '"$gzip_ratio" $server_protocol '; + + access_log /var/log/nginx/access.log main; + + sendfile on; + keepalive_timeout 65; + + server { + listen 8080; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + ## + # Enable Metrics + ## + location /api/ { + api write=on; + allow 127.0.0.1; + deny all; + status_zone my_location_zone1; + } + + + location /test { + return 200 "Test Success"; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/test/integration/managementplane/configs/nginx.conf b/test/integration/managementplane/configs/nginx.conf new file mode 100644 index 000000000..bae76e091 --- /dev/null +++ b/test/integration/managementplane/configs/nginx.conf @@ -0,0 +1,55 @@ +worker_processes 1; +error_log /var/log/nginx/error.log; + +events { + worker_connections 1024; +} + +http { + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for" ' + '"$bytes_sent" "$request_length" "$request_time" ' + '"$gzip_ratio" $server_protocol '; + + access_log /var/log/nginx/access.log main; + + sendfile on; + keepalive_timeout 65; + + include %s; + include %s; + include %s; + + server { + listen 8080; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + ## + # Enable Metrics + ## + location /api { + stub_status; + allow 127.0.0.1; + deny all; + } + + location /test { + return 200 "Test Success"; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/test/integration/managementplane/configs/test.conf b/test/integration/managementplane/configs/test.conf new file mode 100644 index 000000000..495d5146e --- /dev/null +++ b/test/integration/managementplane/configs/test.conf @@ -0,0 +1,10 @@ +# include file for integration tests + +server { + listen 127.0.0.1:8090; + location /stub_status { + stub_status; + allow 127.0.0.1; + deny all; + } +} diff --git a/test/integration/managementplane/configs/test2.conf b/test/integration/managementplane/configs/test2.conf new file mode 100644 index 000000000..f4672124d --- /dev/null +++ b/test/integration/managementplane/configs/test2.conf @@ -0,0 +1,8 @@ +server { + listen 127.0.0.1:8092; + location /stub_status { + stub_status; + allow 127.0.0.1; + deny all; + } +} diff --git a/test/integration/managementplane/configs/unreferenced_file.conf b/test/integration/managementplane/configs/unreferenced_file.conf new file mode 100644 index 000000000..a798e8fa0 --- /dev/null +++ b/test/integration/managementplane/configs/unreferenced_file.conf @@ -0,0 +1,10 @@ +# file to test if un referenced files work + +#server { +# listen 127.0.0.1:8090; +# location /stub_status { +# stub_status; +# allow 127.0.0.1; +# deny all; +# } +#} diff --git a/test/integration/utils/config_apply_utils.go b/test/integration/utils/config_apply_utils.go index 924dfcddd..cc585a2d2 100644 --- a/test/integration/utils/config_apply_utils.go +++ b/test/integration/utils/config_apply_utils.go @@ -6,11 +6,19 @@ package utils import ( + "encoding/json" "fmt" + "io" "net/http" + "os" "testing" "time" + "github.com/nginx/agent/v3/internal/model" + "github.com/nginx/agent/v3/test/helpers" + "github.com/nginx/agent/v3/test/integration/managementplane/configs" + "github.com/testcontainers/testcontainers-go" + mpi "github.com/nginx/agent/v3/api/grpc/mpi/v1" "google.golang.org/protobuf/encoding/protojson" @@ -23,6 +31,7 @@ const ( RetryCount = 10 RetryWaitTime = 5 * time.Second RetryMaxWaitTime = 1 * time.Minute + permissions = 0o666 ) var ( @@ -114,3 +123,73 @@ func PerformInvalidConfigApply(t *testing.T, nginxInstanceID string) { require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode()) } + +func CheckManifestFile(t *testing.T, container testcontainers.Container, + expectedContent map[string]*model.ManifestFile, +) { + t.Helper() + file, err := container.CopyFileFromContainer(t.Context(), "/var/lib/nginx-agent/manifest.json") + require.NoError(t, err) + fileContent, err := io.ReadAll(file) + require.NoError(t, err) + + var manifestFiles map[string]*model.ManifestFile + + err = json.Unmarshal(fileContent, &manifestFiles) + assert.NotEmpty(t, fileContent) + require.NoError(t, err) + + assert.Equal(t, expectedContent, manifestFiles) +} + +func WriteConfigFileMock(t *testing.T, nginxInstanceID, file1, file2, file3 string) { + t.Helper() + tempDir := t.TempDir() + + file := helpers.CreateFileWithErrorCheck(t, tempDir, "nginx.conf") + t.Logf("File: %s", file.Name()) + + if os.Getenv("IMAGE_PATH") == "/nginx-plus/agent" { + writeErr := os.WriteFile(file.Name(), []byte(configs.NginxPlusConfigWithMultipleInclude( + file1, file2, file3)), permissions) + require.NoError(t, writeErr) + } else { + writeErr := os.WriteFile(file.Name(), []byte(configs.NginxConfigWithMultipleInclude( + file1, file2, file3)), permissions) + require.NoError(t, writeErr) + } + + err := MockManagementPlaneGrpcContainer.CopyFileToContainer( + t.Context(), + file.Name(), + fmt.Sprintf("/mock-management-plane-grpc/config/%s/etc/nginx/nginx.conf", nginxInstanceID), + permissions, + ) + require.NoError(t, err) +} + +func WriteConfigFileDataplane(t *testing.T, file1, file2, file3 string) { + t.Helper() + tempDir := t.TempDir() + + file := helpers.CreateFileWithErrorCheck(t, tempDir, "nginx.conf") + t.Logf("File: %s", file.Name()) + + if os.Getenv("IMAGE_PATH") == "/nginx-plus/agent" { + writeErr := os.WriteFile(file.Name(), []byte(configs.NginxPlusConfigWithMultipleInclude( + file1, file2, file3)), permissions) + require.NoError(t, writeErr) + } else { + writeErr := os.WriteFile(file.Name(), []byte(configs.NginxConfigWithMultipleInclude( + file1, file2, file3)), permissions) + require.NoError(t, writeErr) + } + + err := Container.CopyFileToContainer( + t.Context(), + file.Name(), + "/etc/nginx/nginx.conf", + permissions, + ) + require.NoError(t, err) +} diff --git a/test/integration/utils/grpc_management_plane_utils.go b/test/integration/utils/grpc_management_plane_utils.go index de8d20f72..e77c36a83 100644 --- a/test/integration/utils/grpc_management_plane_utils.go +++ b/test/integration/utils/grpc_management_plane_utils.go @@ -43,6 +43,7 @@ const ( statusRetryCount = 3 retryWait = 50 * time.Millisecond retryMaxWait = 200 * time.Millisecond + plusPath = "/nginx-plus/agent" ) type ( @@ -173,7 +174,7 @@ func setupNginxContainer( ) { tb.Helper() nginxConfPath := "../../config/nginx/nginx.conf" - if os.Getenv("IMAGE_PATH") == "/nginx-plus/agent" { + if os.Getenv("IMAGE_PATH") == plusPath { nginxConfPath = "../../config/nginx/nginx-plus.conf" } params.NginxConfigPath = nginxConfPath @@ -501,7 +502,7 @@ func VerifyUpdateDataPlaneStatus(t *testing.T, mockManagementPlaneAPIAddress str assert.NotEmpty(t, instances[1].GetInstanceMeta().GetInstanceId()) - if os.Getenv("IMAGE_PATH") == "/nginx-plus/agent" { + if os.Getenv("IMAGE_PATH") == plusPath { assert.Equal(t, mpi.InstanceMeta_INSTANCE_TYPE_NGINX_PLUS, instances[1].GetInstanceMeta().GetInstanceType()) } else { assert.Equal(t, mpi.InstanceMeta_INSTANCE_TYPE_NGINX, instances[1].GetInstanceMeta().GetInstanceType()) diff --git a/test/mock/grpc/mock_management_file_service.go b/test/mock/grpc/mock_management_file_service.go index 9b916cb76..a865777c7 100644 --- a/test/mock/grpc/mock_management_file_service.go +++ b/test/mock/grpc/mock_management_file_service.go @@ -39,6 +39,7 @@ type FileService struct { agentConfig *config.Config v1.UnimplementedFileServiceServer instanceFiles map[string][]*v1.File // key is instanceID + instanceID string requestChan chan *v1.ManagementPlaneRequest configDirectory string } @@ -49,6 +50,7 @@ func NewFileService(configDirectory string, requestChan chan *v1.ManagementPlane return &FileService{ configDirectory: configDirectory, instanceFiles: make(map[string][]*v1.File), + instanceID: "", requestChan: requestChan, agentConfig: agentConfig, } @@ -62,7 +64,8 @@ func (mgs *FileService) GetOverview( slog.InfoContext(ctx, "Getting overview", "config_version", configVersion) - if _, ok := mgs.instanceFiles[request.GetConfigVersion().GetInstanceId()]; !ok { + mgs.instanceID = request.GetConfigVersion().GetInstanceId() + if _, ok := mgs.instanceFiles[mgs.instanceID]; !ok { slog.ErrorContext(ctx, "Config version not found", "config_version", configVersion) return nil, status.Errorf(codes.NotFound, "Config version not found") } @@ -70,7 +73,7 @@ func (mgs *FileService) GetOverview( return &v1.GetOverviewResponse{ Overview: &v1.FileOverview{ ConfigVersion: configVersion, - Files: mgs.instanceFiles[configVersion.GetInstanceId()], + Files: mgs.instanceFiles[mgs.instanceID], }, }, nil } @@ -81,13 +84,15 @@ func (mgs *FileService) UpdateOverview( ) (*v1.UpdateOverviewResponse, error) { overview := request.GetOverview() + mgs.instanceID = overview.GetConfigVersion().GetInstanceId() + marshaledJSON, errMarshaledJSON := protojson.Marshal(request) if errMarshaledJSON != nil { return nil, fmt.Errorf("failed to marshal struct back to JSON: %w", errMarshaledJSON) } slog.InfoContext(ctx, "Updating overview JSON", "overview", marshaledJSON) - mgs.instanceFiles[overview.GetConfigVersion().GetInstanceId()] = overview.GetFiles() + mgs.instanceFiles[mgs.instanceID] = overview.GetFiles() configUploadRequest := &v1.ManagementPlaneRequest{ MessageMeta: &v1.MessageMeta{ @@ -114,7 +119,6 @@ func (mgs *FileService) GetFile( fileHash := request.GetFileMeta().GetHash() slog.InfoContext(ctx, "Getting file", "name", fileName, "hash", fileHash) - fullFilePath := mgs.findFile(request.GetFileMeta()) if fullFilePath == "" { @@ -419,13 +423,7 @@ func (mgs *FileService) createDirectories(fullFilePath string, filePermissions o } func (mgs *FileService) findFile(fileMeta *v1.FileMeta) (fullFilePath string) { - for instanceID, instanceFiles := range mgs.instanceFiles { - for _, file := range instanceFiles { - if file.GetFileMeta().GetName() == fileMeta.GetName() { - fullFilePath = filepath.Join(mgs.configDirectory, instanceID, fileMeta.GetName()) - } - } - } + fullFilePath = filepath.Join(mgs.configDirectory, mgs.instanceID, fileMeta.GetName()) return fullFilePath }