From 6920c50f16a988a907acacc322012719cce724d9 Mon Sep 17 00:00:00 2001 From: Nutsa Bidzishvili Date: Mon, 25 Aug 2025 10:12:33 +0100 Subject: [PATCH 01/12] add config files --- .../configs/nginx-agent-v3-valid-config.conf | 24 +++++++++ .../upgrade/configs/nginx-agent.conf | 24 +++++++++ .../upgrade/configs/nginx-oss.conf | 47 ++++++++++++++++++ .../upgrade/configs/nginx-plus.conf | 49 +++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 test/integration/upgrade/configs/nginx-agent-v3-valid-config.conf create mode 100644 test/integration/upgrade/configs/nginx-agent.conf create mode 100644 test/integration/upgrade/configs/nginx-oss.conf create mode 100644 test/integration/upgrade/configs/nginx-plus.conf diff --git a/test/integration/upgrade/configs/nginx-agent-v3-valid-config.conf b/test/integration/upgrade/configs/nginx-agent-v3-valid-config.conf new file mode 100644 index 000000000..215ed3890 --- /dev/null +++ b/test/integration/upgrade/configs/nginx-agent-v3-valid-config.conf @@ -0,0 +1,24 @@ +# +# /etc/nginx-agent/nginx-agent.conf +# +# Configuration file for NGINX Agent. +# + +log: + # set log level (error, warn, info, debug; default "info") + level: debug + # set log path. if empty, don't log to file. + path: /var/log/nginx-agent/ + +allowed_directories: + - /etc/nginx + - /usr/local/etc/nginx + - /usr/share/nginx/modules + - /var/run/nginx + - /var/log/nginx + +command: + server: + host: managementPlane + port: 9092 + type: grpc diff --git a/test/integration/upgrade/configs/nginx-agent.conf b/test/integration/upgrade/configs/nginx-agent.conf new file mode 100644 index 000000000..215ed3890 --- /dev/null +++ b/test/integration/upgrade/configs/nginx-agent.conf @@ -0,0 +1,24 @@ +# +# /etc/nginx-agent/nginx-agent.conf +# +# Configuration file for NGINX Agent. +# + +log: + # set log level (error, warn, info, debug; default "info") + level: debug + # set log path. if empty, don't log to file. + path: /var/log/nginx-agent/ + +allowed_directories: + - /etc/nginx + - /usr/local/etc/nginx + - /usr/share/nginx/modules + - /var/run/nginx + - /var/log/nginx + +command: + server: + host: managementPlane + port: 9092 + type: grpc diff --git a/test/integration/upgrade/configs/nginx-oss.conf b/test/integration/upgrade/configs/nginx-oss.conf new file mode 100644 index 000000000..c1e43d4b3 --- /dev/null +++ b/test/integration/upgrade/configs/nginx-oss.conf @@ -0,0 +1,47 @@ +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 { + stub_status; + allow 127.0.0.1; + deny all; + } + + # 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/upgrade/configs/nginx-plus.conf b/test/integration/upgrade/configs/nginx-plus.conf new file mode 100644 index 000000000..e4fbb16a3 --- /dev/null +++ b/test/integration/upgrade/configs/nginx-plus.conf @@ -0,0 +1,49 @@ +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; + } + + # 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; + } + } +} From baabe21fc55629d745ba880397e57b33e1cc59ec Mon Sep 17 00:00:00 2001 From: Nutsa Bidzishvili Date: Thu, 28 Aug 2025 09:09:12 +0100 Subject: [PATCH 02/12] Add upgrade tests script draft --- Makefile | 10 +- test/integration/upgrade/upgrade_test.go | 194 +++++++++++++++++++++++ 2 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 test/integration/upgrade/upgrade_test.go diff --git a/Makefile b/Makefile index d436d3582..9cae95965 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,10 @@ VERSION ?= $(shell git describe --match "v[0-9]*" --abbrev=0 --tags) ifeq ($(strip $(VERSION)),) VERSION := $(shell curl https://api.github.com/repos/nginx/agent/releases/latest -s | jq .name -r) endif + +OLD_VERSION := 3.1.0 +OLD_PACKAGE_NAME := $(PACKAGE_PREFIX)-$(shell echo $(OLD_VERSION) | tr -d 'v')-SNAPSHOT-$(COMMIT) + COMMIT = $(shell git rev-parse --short HEAD) DATE = $(shell date +%F_%H-%M-%S) LDFLAGS = "-s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)" @@ -162,9 +166,9 @@ build-mock-management-otel-collector: 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 + PACKAGES_REPO=$(OSS_PACKAGES_REPO) PACKAGE_NAME=$(PACKAGE_NAME) OLD_PACKAGE_NAME=$(OLD_PACKAGE_NAME) OLD_VERSION=$(OLD_VERSION) 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 ./test/integration/upgrade 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/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go new file mode 100644 index 000000000..79d207bdf --- /dev/null +++ b/test/integration/upgrade/upgrade_test.go @@ -0,0 +1,194 @@ +// 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 upgrade + +import ( + "bytes" + "context" + "github.com/nginx/agent/v3/test/helpers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" + "io" + "log/slog" + "os" + "path" + "path/filepath" + "strings" + "testing" + "time" +) + +const ( + maxFileSize = 70000000 + maxUpgradeTime = 30 * time.Second + agentBuildDir = "../agent/build" + agentPackageName = "nginx-agent" +) + +var ( + osRelease = os.Getenv("OS_RELEASE") + oldPackageName = os.Getenv("OLD_PACKAGE_NAME") + packageName = os.Getenv("PACKAGE_NAME") +) + +func TestV3toV3Upgrade(t *testing.T) { + ctx := context.Background() + testContainer, teardownTest := upgradeSetup(t, true) + defer teardownTest(t) + + slog.Info("starting upgrade to latest agent v3 tests") + + // Verify Agent Package Path & Install Agent + agentPackagePath := verifyAgentPackageSize(t, testContainer) + + // verify agent upgrade + upgradeAgent(ctx, t, testContainer) + + // verify version of agent + verifyAgentVersion(ctx, t, testContainer, agentPackageName) + + // verify agent v3 config has not changed + + // Validate expected logs + + // validate agent manifest file + + // verify agent package size and get its path + packagePath := verifyAgentPackage(t, testContainer) + + // verify size of agent package and get path to agent package + containerAgentPackagePath := verifyAgentPackage(t, testContainer) +} + +func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool) (testcontainers.Container, func(tb testing.TB)) { + tb.Helper() + ctx := context.Background() + + params := &helpers.Parameters{ + NginxConfigPath: "./config/nginx/nginx.conf", + LogMessage: "nginx_pid", + } + + testContainer := helpers.StartAgentlessContainer( + ctx, + tb, + params, + ) + + return testContainer, func(tb testing.TB) { + tb.Helper() + helpers.LogAndTerminateContainers( + ctx, + tb, + nil, + testContainer, + expectNoErrorsInLogs, + nil, + ) + } +} + +func upgradeAgent(tb testing.TB, testContainer testcontainers.Container) (string, time.Duration) { + tb.Helper() + + var updateCmd, upgradeCmd []string + + if strings.Contains(osRelease, "Ubuntu") || strings.Contains(osRelease, "Debian") { + updateCmd = []string{"apt-get", "update"} + upgradeCmd = []string{"apt-get", "install", "-y", "--only-upgrade", "nginx-agent", "-o", "Dpkg::Options::=--force-confold"} + } else { + updateCmd = []string{"yum", "-y", "makecache"} + upgradeCmd = []string{"yum", "update", "-y", "nginx-agent"} + } + + start := time.Now() + + var output []byte + + exitCode, cmdOut, err := testContainer.Exec(ctx, updateCmd) + require.NoError(tb, err) + stdourStderr, err := io.ReadAll(cmdOut) + require.NoError(tb, err) + output = append(output, stdourStderr...) + assert.Equal(tb, 0, exitCode) + + exitCode, cmdOut, err = testContainer.Exec(ctx, upgradeCmd) + require.NoError(tb, err) + stdourStderr, err = io.ReadAll(cmdOut) + require.NoError(tb, err) + output = append(output, stdourStderr...) + assert.Equal(tb, 0, exitCode) + + duration := time.Since(start) + return string(output), duration + +} + +func verifyAgentVersion(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, oldVersion string) { + tb.Helper() + cmd := []string{"nginx-agent", "--version"} + exitCode, cmdOut, err := testContainer.Exec(ctx, cmd) + require.NoError(tb, err) + + stdoutStderr, err := io.ReadAll(cmdOut) + require.NoError(tb, err) + + output := strings.TrimSpace(string(stdoutStderr)) + + assert.Equal(tb, 0, exitCode) + if output != oldVersion { + tb.Logf("expected version %s, got %s", oldVersion, output) + } + tb.Logf("agent upgraded to version %s successfully", output) +} + +func verifyAgentPackageSize(tb testing.TB, testContainer testcontainers.Container) string { + tb.Helper() + agentPkgPath, filePathErr := filepath.Abs("../../../build/") + require.NoError(tb, filePathErr, "Error finding local agent package build dir") + + localAgentPkg, packageErr := os.Stat(packagePath(agentPkgPath, osRelease)) + require.NoError(tb, packageErr, "Error accessing package at: "+agentPkgPath) + + // Check if file size is less than 70MB + assert.Less(tb, localAgentPkg.Size(), maxFileSize) + + if strings.Contains(osRelease, "ubuntu") || strings.Contains(osRelease, "debian") { + upgradeAgent(tb, testContainer) + } + + return packagePath(agentBuildDir, osRelease) +} + +func packagePath(pkgDir, osReleaseContent string) string { + pkgPath := path.Join(pkgDir, packageName) + + if strings.Contains(osReleaseContent, "ubuntu") || strings.Contains(osReleaseContent, "Debian") { + return pkgPath + ".deb" + } else if strings.Contains(osReleaseContent, "alpine") { + return pkgPath + ".apk" + } + + return pkgPath + ".rpm" +} + +func validateAgentConfig(tb testing.TB, expectedConfigPath, updatedConfigPath string) { + tb.Helper() + + // valid config file + expectedContent, err := os.ReadFile(expectedConfigPath) + require.NoError(tb, err) + + // new config file + updated, err := os.ReadFile(updatedConfigPath) + require.NoError(tb, err) + + if !bytes.Equal(expectedContent, updated) { + tb.Fatalf("expected no changes in the config file") + } + tb.Logf("config file validation was successful") +} From e4da8039f170bfc5e97c9c4dd52921664789a043 Mon Sep 17 00:00:00 2001 From: Nutsa Bidzishvili Date: Thu, 28 Aug 2025 15:43:53 +0100 Subject: [PATCH 03/12] v3 upgrade tests draft --- Makefile | 6 +- .../nginx-official-image/deb/Dockerfile | 1 + test/helpers/test_containers_utils.go | 4 + test/integration/upgrade/upgrade_test.go | 105 +++++++++--------- .../utils/grpc_management_plane_utils.go | 6 +- 5 files changed, 64 insertions(+), 58 deletions(-) diff --git a/Makefile b/Makefile index 9cae95965..6796b01f0 100644 --- a/Makefile +++ b/Makefile @@ -53,8 +53,8 @@ ifeq ($(strip $(VERSION)),) VERSION := $(shell curl https://api.github.com/repos/nginx/agent/releases/latest -s | jq .name -r) endif -OLD_VERSION := 3.1.0 -OLD_PACKAGE_NAME := $(PACKAGE_PREFIX)-$(shell echo $(OLD_VERSION) | tr -d 'v')-SNAPSHOT-$(COMMIT) +NGINX_AGENT_VERSION := 3.1.0 +NGINX_AGENT_PACKAGE_NAME := $(PACKAGE_PREFIX)-$(shell echo $(NGINX_AGENT_VERSION) | tr -d 'v')-SNAPSHOT-$(COMMIT) COMMIT = $(shell git rev-parse --short HEAD) DATE = $(shell date +%F_%H-%M-%S) @@ -166,7 +166,7 @@ build-mock-management-otel-collector: 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) OLD_PACKAGE_NAME=$(OLD_PACKAGE_NAME) OLD_VERSION=$(OLD_VERSION) BASE_IMAGE=$(BASE_IMAGE) \ + PACKAGES_REPO=$(OSS_PACKAGES_REPO) PACKAGE_NAME=$(PACKAGE_NAME) NGINX_AGENT_VERSION=$(NGINX_AGENT_VERSION) NGINX_AGENT_PACKAGE_NAME=$(NGINX_AGENT_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 ./test/integration/upgrade diff --git a/test/docker/nginx-official-image/deb/Dockerfile b/test/docker/nginx-official-image/deb/Dockerfile index d52491004..50f5a0190 100644 --- a/test/docker/nginx-official-image/deb/Dockerfile +++ b/test/docker/nginx-official-image/deb/Dockerfile @@ -1,6 +1,7 @@ ARG CONTAINER_NGINX_IMAGE_REGISTRY ARG TAG ARG IMAGE_PATH +ARG NGINX_AGENT_VERSION FROM ${CONTAINER_NGINX_IMAGE_REGISTRY}${IMAGE_PATH}:${TAG} AS install LABEL maintainer="NGINX Docker Maintainers " diff --git a/test/helpers/test_containers_utils.go b/test/helpers/test_containers_utils.go index f90ab3182..0f688fd16 100644 --- a/test/helpers/test_containers_utils.go +++ b/test/helpers/test_containers_utils.go @@ -41,6 +41,8 @@ func StartContainer( buildTarget := Env(tb, "BUILD_TARGET") dockerfilePath := Env(tb, "DOCKERFILE_PATH") containerRegistry := Env(tb, "CONTAINER_NGINX_IMAGE_REGISTRY") + nginxAgentVersion := Env(tb, "NGINX_AGENT_VERSION") + nginxAgentPackageName := Env(tb, "NGINX_AGENT_PACKAGE_NAME") tag := Env(tb, "TAG") imagePath := Env(tb, "IMAGE_PATH") @@ -58,6 +60,8 @@ func StartContainer( "OS_VERSION": ToPtr(osVersion), "ENTRY_POINT": ToPtr("./test/docker/entrypoint.sh"), "CONTAINER_NGINX_IMAGE_REGISTRY": ToPtr(containerRegistry), + "NGINX_AGENT_VERSION": ToPtr(nginxAgentVersion), + "NGINX_AGENT_PACKAGE_NAME": ToPtr(nginxAgentPackageName), "IMAGE_PATH": ToPtr(imagePath), "TAG": ToPtr(tag), }, diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index 79d207bdf..e0900aac4 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -8,10 +8,7 @@ package upgrade import ( "bytes" "context" - "github.com/nginx/agent/v3/test/helpers" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/testcontainers/testcontainers-go" + "github.com/nginx/agent/v3/test/integration/utils" "io" "log/slog" "os" @@ -20,6 +17,11 @@ import ( "strings" "testing" "time" + + "github.com/nginx/agent/v3/test/helpers" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" ) const ( @@ -30,52 +32,52 @@ const ( ) var ( - osRelease = os.Getenv("OS_RELEASE") - oldPackageName = os.Getenv("OLD_PACKAGE_NAME") - packageName = os.Getenv("PACKAGE_NAME") + osRelease = os.Getenv("OS_RELEASE") + packageName = os.Getenv("NGINX_AGENT_PACKAGE_NAME") + agentConfig = "./configs/nginx-agent.conf" + agentValidConfig = "./configs/nginx-agent-v3-valid-config.conf" ) func TestV3toV3Upgrade(t *testing.T) { ctx := context.Background() - testContainer, teardownTest := upgradeSetup(t, true) + containerNetwork := utils.CreateContainerNetwork(ctx, t) + testContainer, teardownTest := upgradeSetup(t, true, containerNetwork) defer teardownTest(t) - slog.Info("starting upgrade to latest agent v3 tests") + slog.Info("starting agent v3 upgrade tests") - // Verify Agent Package Path & Install Agent - agentPackagePath := verifyAgentPackageSize(t, testContainer) + // Verify Agent Package Path & get the path + agentPackagePath := verifyAgentPackageSize(ctx, t, testContainer) // verify agent upgrade - upgradeAgent(ctx, t, testContainer) + verifyAgentUpgrade(ctx, t, testContainer, agentPackagePath) // verify version of agent verifyAgentVersion(ctx, t, testContainer, agentPackageName) // verify agent v3 config has not changed + validateAgentConfig(t, agentValidConfig, agentConfig) // Validate expected logs // validate agent manifest file - // verify agent package size and get its path - packagePath := verifyAgentPackage(t, testContainer) - - // verify size of agent package and get path to agent package - containerAgentPackagePath := verifyAgentPackage(t, testContainer) } -func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool) (testcontainers.Container, func(tb testing.TB)) { +func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool, containerNetwork *testcontainers.DockerNetwork) (testcontainers.Container, func(tb testing.TB)) { tb.Helper() ctx := context.Background() params := &helpers.Parameters{ - NginxConfigPath: "./config/nginx/nginx.conf", - LogMessage: "nginx_pid", + NginxConfigPath: "./configs/nginx-oss.conf", + NginxAgentConfigPath: "./configs/nginx-agent.conf", + LogMessage: "nginx_pid", } - testContainer := helpers.StartAgentlessContainer( + testContainer := helpers.StartContainer( ctx, tb, + containerNetwork, params, ) @@ -92,7 +94,33 @@ func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool) (testcontainers.Cont } } -func upgradeAgent(tb testing.TB, testContainer testcontainers.Container) (string, time.Duration) { +func verifyAgentPackageSize(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) string { + tb.Helper() + agentPkgPath, filePathErr := filepath.Abs("../../../build/") + require.NoError(tb, filePathErr, "Error finding local agent package build dir") + + localAgentPkg, packageErr := os.Stat(packagePath(agentPkgPath, osRelease)) + require.NoError(tb, packageErr, "Error accessing package at: "+agentPkgPath) + + // check if file size is less than 70MB + assert.Less(tb, localAgentPkg.Size(), maxFileSize) + + return packagePath(agentBuildDir, osRelease) +} + +func verifyAgentUpgrade(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, agentPackagePath string) { + tb.Helper() + + //cmdOut, upgradeTime := upgradeAgent(ctx, tb, testContainer) + + //assert.LessOrEqual(tb, upgradeTime, maxUpgradeTime) + //tb.Log("Upgrade time: ", upgradeTime) + + // validate logs here + //cmdOut, err := +} + +func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) (io.Reader, time.Duration) { tb.Helper() var updateCmd, upgradeCmd []string @@ -107,25 +135,16 @@ func upgradeAgent(tb testing.TB, testContainer testcontainers.Container) (string start := time.Now() - var output []byte - - exitCode, cmdOut, err := testContainer.Exec(ctx, updateCmd) + exitCode, _, err := testContainer.Exec(ctx, updateCmd) require.NoError(tb, err) - stdourStderr, err := io.ReadAll(cmdOut) - require.NoError(tb, err) - output = append(output, stdourStderr...) assert.Equal(tb, 0, exitCode) - exitCode, cmdOut, err = testContainer.Exec(ctx, upgradeCmd) - require.NoError(tb, err) - stdourStderr, err = io.ReadAll(cmdOut) + exitCode, cmdOut, err := testContainer.Exec(ctx, upgradeCmd) require.NoError(tb, err) - output = append(output, stdourStderr...) assert.Equal(tb, 0, exitCode) duration := time.Since(start) - return string(output), duration - + return cmdOut, duration } func verifyAgentVersion(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, oldVersion string) { @@ -146,24 +165,6 @@ func verifyAgentVersion(ctx context.Context, tb testing.TB, testContainer testco tb.Logf("agent upgraded to version %s successfully", output) } -func verifyAgentPackageSize(tb testing.TB, testContainer testcontainers.Container) string { - tb.Helper() - agentPkgPath, filePathErr := filepath.Abs("../../../build/") - require.NoError(tb, filePathErr, "Error finding local agent package build dir") - - localAgentPkg, packageErr := os.Stat(packagePath(agentPkgPath, osRelease)) - require.NoError(tb, packageErr, "Error accessing package at: "+agentPkgPath) - - // Check if file size is less than 70MB - assert.Less(tb, localAgentPkg.Size(), maxFileSize) - - if strings.Contains(osRelease, "ubuntu") || strings.Contains(osRelease, "debian") { - upgradeAgent(tb, testContainer) - } - - return packagePath(agentBuildDir, osRelease) -} - func packagePath(pkgDir, osReleaseContent string) string { pkgPath := path.Join(pkgDir, packageName) diff --git a/test/integration/utils/grpc_management_plane_utils.go b/test/integration/utils/grpc_management_plane_utils.go index de8d20f72..5151c3708 100644 --- a/test/integration/utils/grpc_management_plane_utils.go +++ b/test/integration/utils/grpc_management_plane_utils.go @@ -99,7 +99,7 @@ func setupContainerEnvironment(ctx context.Context, tb testing.TB, nginxless, au tb.Helper() tb.Log("Running tests in a container environment") - containerNetwork := createContainerNetwork(ctx, tb) + containerNetwork := CreateContainerNetwork(ctx, tb) setupMockManagementPlaneGrpc(ctx, tb, containerNetwork) if auxiliaryServer { setupAuxiliaryMockManagementPlaneGrpc(ctx, tb, containerNetwork) @@ -117,8 +117,8 @@ func setupContainerEnvironment(ctx context.Context, tb testing.TB, nginxless, au } } -// createContainerNetwork creates and configures a container network. -func createContainerNetwork(ctx context.Context, tb testing.TB) *testcontainers.DockerNetwork { +// CreateContainerNetwork creates and configures a container network. +func CreateContainerNetwork(ctx context.Context, tb testing.TB) *testcontainers.DockerNetwork { tb.Helper() containerNetwork, err := network.New(ctx, network.WithAttachable()) require.NoError(tb, err) From 61184620f31a9c479f9e1839a2c20a38868c4dc5 Mon Sep 17 00:00:00 2001 From: Nutsa Bidzishvili Date: Thu, 28 Aug 2025 16:11:25 +0100 Subject: [PATCH 04/12] updated draft --- test/integration/upgrade/upgrade_test.go | 38 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index e0900aac4..1af0bdac8 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -8,7 +8,6 @@ package upgrade import ( "bytes" "context" - "github.com/nginx/agent/v3/test/integration/utils" "io" "log/slog" "os" @@ -18,6 +17,8 @@ import ( "testing" "time" + "github.com/nginx/agent/v3/test/integration/utils" + "github.com/nginx/agent/v3/test/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -47,10 +48,10 @@ func TestV3toV3Upgrade(t *testing.T) { slog.Info("starting agent v3 upgrade tests") // Verify Agent Package Path & get the path - agentPackagePath := verifyAgentPackageSize(ctx, t, testContainer) + verifyAgentPackageSize(t) // verify agent upgrade - verifyAgentUpgrade(ctx, t, testContainer, agentPackagePath) + verifyAgentUpgrade(ctx, t, testContainer) // verify version of agent verifyAgentVersion(ctx, t, testContainer, agentPackageName) @@ -61,10 +62,11 @@ func TestV3toV3Upgrade(t *testing.T) { // Validate expected logs // validate agent manifest file - } -func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool, containerNetwork *testcontainers.DockerNetwork) (testcontainers.Container, func(tb testing.TB)) { +func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool, + containerNetwork *testcontainers.DockerNetwork, +) (testcontainers.Container, func(tb testing.TB)) { tb.Helper() ctx := context.Background() @@ -94,7 +96,7 @@ func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool, containerNetwork *te } } -func verifyAgentPackageSize(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) string { +func verifyAgentPackageSize(tb testing.TB) string { tb.Helper() agentPkgPath, filePathErr := filepath.Abs("../../../build/") require.NoError(tb, filePathErr, "Error finding local agent package build dir") @@ -108,26 +110,32 @@ func verifyAgentPackageSize(ctx context.Context, tb testing.TB, testContainer te return packagePath(agentBuildDir, osRelease) } -func verifyAgentUpgrade(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, agentPackagePath string) { +func verifyAgentUpgrade(ctx context.Context, tb testing.TB, + testContainer testcontainers.Container, +) { tb.Helper() - //cmdOut, upgradeTime := upgradeAgent(ctx, tb, testContainer) + cmdOut, upgradeTime := upgradeAgent(ctx, tb, testContainer) - //assert.LessOrEqual(tb, upgradeTime, maxUpgradeTime) - //tb.Log("Upgrade time: ", upgradeTime) + assert.LessOrEqual(tb, upgradeTime, maxUpgradeTime) + tb.Log("Upgrade time: ", upgradeTime) // validate logs here - //cmdOut, err := + validateLogs(tb, cmdOut) } -func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) (io.Reader, time.Duration) { +func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, +) (io.Reader, time.Duration) { tb.Helper() var updateCmd, upgradeCmd []string if strings.Contains(osRelease, "Ubuntu") || strings.Contains(osRelease, "Debian") { updateCmd = []string{"apt-get", "update"} - upgradeCmd = []string{"apt-get", "install", "-y", "--only-upgrade", "nginx-agent", "-o", "Dpkg::Options::=--force-confold"} + upgradeCmd = []string{ + "apt-get", "install", "-y", "--only-upgrade", + "nginx-agent", "-o", "Dpkg::Options::=--force-confold", + } } else { updateCmd = []string{"yum", "-y", "makecache"} upgradeCmd = []string{"yum", "update", "-y", "nginx-agent"} @@ -144,6 +152,7 @@ func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontaine assert.Equal(tb, 0, exitCode) duration := time.Since(start) + return cmdOut, duration } @@ -193,3 +202,6 @@ func validateAgentConfig(tb testing.TB, expectedConfigPath, updatedConfigPath st } tb.Logf("config file validation was successful") } + +func validateLogs(tb testing.TB, expectedLogs io.Reader) { +} From da0fedbdcc2da5bed6ac23d9f7ad857e2d76c215 Mon Sep 17 00:00:00 2001 From: Nutsa Bidzishvili Date: Thu, 28 Aug 2025 16:17:41 +0100 Subject: [PATCH 05/12] updated draft --- test/integration/upgrade/upgrade_test.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index 1af0bdac8..f15372863 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -115,13 +115,11 @@ func verifyAgentUpgrade(ctx context.Context, tb testing.TB, ) { tb.Helper() - cmdOut, upgradeTime := upgradeAgent(ctx, tb, testContainer) + // cmdOut for validating logs + _, upgradeTime := upgradeAgent(ctx, tb, testContainer) assert.LessOrEqual(tb, upgradeTime, maxUpgradeTime) tb.Log("Upgrade time: ", upgradeTime) - - // validate logs here - validateLogs(tb, cmdOut) } func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, @@ -202,6 +200,3 @@ func validateAgentConfig(tb testing.TB, expectedConfigPath, updatedConfigPath st } tb.Logf("config file validation was successful") } - -func validateLogs(tb testing.TB, expectedLogs io.Reader) { -} From 2d75aba002d684ccfc310428b89aac42979a233f Mon Sep 17 00:00:00 2001 From: Nutsa Bidzishvili Date: Thu, 28 Aug 2025 16:21:42 +0100 Subject: [PATCH 06/12] fix local issues --- api/grpc/mpi/v1/command.pb.go | 2 +- api/grpc/mpi/v1/common.pb.go | 2 +- api/grpc/mpi/v1/files.pb.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/grpc/mpi/v1/command.pb.go b/api/grpc/mpi/v1/command.pb.go index 1c4fa9b0c..39ef93489 100644 --- a/api/grpc/mpi/v1/command.pb.go +++ b/api/grpc/mpi/v1/command.pb.go @@ -8,7 +8,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.7 +// protoc-gen-go v1.36.8 // protoc (unknown) // source: mpi/v1/command.proto diff --git a/api/grpc/mpi/v1/common.pb.go b/api/grpc/mpi/v1/common.pb.go index 26b95bf14..890c2b073 100644 --- a/api/grpc/mpi/v1/common.pb.go +++ b/api/grpc/mpi/v1/common.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.7 +// protoc-gen-go v1.36.8 // protoc (unknown) // source: mpi/v1/common.proto diff --git a/api/grpc/mpi/v1/files.pb.go b/api/grpc/mpi/v1/files.pb.go index c68585426..be249670c 100644 --- a/api/grpc/mpi/v1/files.pb.go +++ b/api/grpc/mpi/v1/files.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.7 +// protoc-gen-go v1.36.8 // protoc (unknown) // source: mpi/v1/files.proto From 13d1522e67f1a3270d9ef759f479e81d36e48ea1 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Tue, 2 Sep 2025 15:35:12 +0100 Subject: [PATCH 07/12] Add upgrade tests --- .github/workflows/ci.yml | 44 ++++++ Makefile | 13 +- test/helpers/test_containers_utils.go | 4 - test/integration/upgrade/upgrade_test.go | 133 +++++++++++++----- .../utils/grpc_management_plane_utils.go | 6 +- 5 files changed, 151 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f94c7468e..6ff20e94f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -156,6 +156,50 @@ jobs: if: always() run: bash ./scripts/dashboard/format_results.sh ${{job.status}} ${{github.job}}/${{matrix.container.image}}-${{matrix.container.version}} ${{github.workspace}} + upgrade-tests: + name: Upgrade Tests + needs: build-unsigned-snapshot + runs-on: ubuntu-22.04 + strategy: + matrix: + container: + - image: "ubuntu" + version: "24.04" + - image: "redhatenterprise" + version: "9" + - image: "alpine" + version: "3.22" + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version-file: 'go.mod' + cache: false + - name: Download Packages + uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 + with: + name: nginx-agent-unsigned-snapshots + path: build + + - name: Create Results Directory + run: mkdir -p ${{ github.workspace }}/test/dashboard/logs/${{ github.job }}/${{matrix.container.image}}-${{matrix.container.version}} + + - name: Start Promtail + uses: ./.github/actions/start-promtail + with: + loki_url: ${{ secrets.LOKI_DASHBOARD_URL }} + + - name: Run Upgrade Tests + run: | + go install github.com/goreleaser/nfpm/v2/cmd/nfpm@${{ env.NFPM_VERSION }} + OS_RELEASE="${{ matrix.container.image }}" OS_VERSION="${{ matrix.container.version }}" \ + make upgrade-test | tee ${{github.workspace}}/test/dashboard/logs/${{github.job}}/${{matrix.container.image}}-${{matrix.container.version}}/raw_logs.log + exit "${PIPESTATUS[0]}" + + - name: Format Results + if: always() + run: bash ./scripts/dashboard/format_results.sh ${{job.status}} ${{github.job}}/${{matrix.container.image}}-${{matrix.container.version}} ${{github.workspace}} + official-oss-image-integration-tests: name: Integration Tests - Official OSS Images needs: build-unsigned-snapshot diff --git a/Makefile b/Makefile index 6796b01f0..8c7264dc0 100644 --- a/Makefile +++ b/Makefile @@ -53,9 +53,6 @@ ifeq ($(strip $(VERSION)),) VERSION := $(shell curl https://api.github.com/repos/nginx/agent/releases/latest -s | jq .name -r) endif -NGINX_AGENT_VERSION := 3.1.0 -NGINX_AGENT_PACKAGE_NAME := $(PACKAGE_PREFIX)-$(shell echo $(NGINX_AGENT_VERSION) | tr -d 'v')-SNAPSHOT-$(COMMIT) - COMMIT = $(shell git rev-parse --short HEAD) DATE = $(shell date +%F_%H-%M-%S) LDFLAGS = "-s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)" @@ -166,9 +163,15 @@ build-mock-management-otel-collector: 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) NGINX_AGENT_VERSION=$(NGINX_AGENT_VERSION) NGINX_AGENT_PACKAGE_NAME=$(NGINX_AGENT_PACKAGE_NAME) BASE_IMAGE=$(BASE_IMAGE) \ + 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 + +upgrade-test: $(SELECTED_PACKAGE) build-mock-management-plane-grpc + TEST_ENV="Container" CONTAINER_OS_TYPE=$(CONTAINER_OS_TYPE) BUILD_TARGET="install-agent-repo" 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 ./test/integration/upgrade + go test -v ./test/integration/upgrade 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 0f688fd16..f90ab3182 100644 --- a/test/helpers/test_containers_utils.go +++ b/test/helpers/test_containers_utils.go @@ -41,8 +41,6 @@ func StartContainer( buildTarget := Env(tb, "BUILD_TARGET") dockerfilePath := Env(tb, "DOCKERFILE_PATH") containerRegistry := Env(tb, "CONTAINER_NGINX_IMAGE_REGISTRY") - nginxAgentVersion := Env(tb, "NGINX_AGENT_VERSION") - nginxAgentPackageName := Env(tb, "NGINX_AGENT_PACKAGE_NAME") tag := Env(tb, "TAG") imagePath := Env(tb, "IMAGE_PATH") @@ -60,8 +58,6 @@ func StartContainer( "OS_VERSION": ToPtr(osVersion), "ENTRY_POINT": ToPtr("./test/docker/entrypoint.sh"), "CONTAINER_NGINX_IMAGE_REGISTRY": ToPtr(containerRegistry), - "NGINX_AGENT_VERSION": ToPtr(nginxAgentVersion), - "NGINX_AGENT_PACKAGE_NAME": ToPtr(nginxAgentPackageName), "IMAGE_PATH": ToPtr(imagePath), "TAG": ToPtr(tag), }, diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index f15372863..50726cb0b 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -26,42 +26,48 @@ import ( ) const ( - maxFileSize = 70000000 - maxUpgradeTime = 30 * time.Second - agentBuildDir = "../agent/build" - agentPackageName = "nginx-agent" + maxFileSize int64 = 70000000 + maxUpgradeTime = 30 * time.Second + agentBuildDir = "../agent/build" ) var ( - osRelease = os.Getenv("OS_RELEASE") - packageName = os.Getenv("NGINX_AGENT_PACKAGE_NAME") - agentConfig = "./configs/nginx-agent.conf" - agentValidConfig = "./configs/nginx-agent-v3-valid-config.conf" + osRelease = os.Getenv("OS_RELEASE") + packageName = os.Getenv("PACKAGE_NAME") ) -func TestV3toV3Upgrade(t *testing.T) { +func Test_UpgradeFromV3(t *testing.T) { ctx := context.Background() + containerNetwork := utils.CreateContainerNetwork(ctx, t) + utils.SetupMockManagementPlaneGrpc(ctx, t, containerNetwork) + defer func(ctx context.Context) { + err := utils.MockManagementPlaneGrpcContainer.Terminate(ctx) + require.NoError(t, err) + }(ctx) + testContainer, teardownTest := upgradeSetup(t, true, containerNetwork) defer teardownTest(t) slog.Info("starting agent v3 upgrade tests") - // Verify Agent Package Path & get the path - verifyAgentPackageSize(t) + // get currently installed agent version + oldVersion := agentVersion(ctx, t, testContainer) // verify agent upgrade verifyAgentUpgrade(ctx, t, testContainer) // verify version of agent - verifyAgentVersion(ctx, t, testContainer, agentPackageName) + verifyAgentVersion(ctx, t, testContainer, oldVersion) - // verify agent v3 config has not changed - validateAgentConfig(t, agentValidConfig, agentConfig) + // Verify Agent Package Path & get the path + verifyAgentPackageSize(t) - // Validate expected logs + // verify agent v3 config has not changed + validateAgentConfig(ctx, t, testContainer) // validate agent manifest file + verifyManifestFile(ctx, t, testContainer) } func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool, @@ -126,28 +132,30 @@ func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontaine ) (io.Reader, time.Duration) { tb.Helper() - var updateCmd, upgradeCmd []string + var upgradeCmd []string - if strings.Contains(osRelease, "Ubuntu") || strings.Contains(osRelease, "Debian") { - updateCmd = []string{"apt-get", "update"} + if strings.Contains(osRelease, "ubuntu") || strings.Contains(osRelease, "debian") { upgradeCmd = []string{ "apt-get", "install", "-y", "--only-upgrade", - "nginx-agent", "-o", "Dpkg::Options::=--force-confold", + "/agent/build/" + packageName + ".deb", "-o", "Dpkg::Options::=--force-confold", } } else { - updateCmd = []string{"yum", "-y", "makecache"} - upgradeCmd = []string{"yum", "update", "-y", "nginx-agent"} + upgradeCmd = []string{"yum", "install", "-y", "/agent/build/" + packageName + ".rpm"} } start := time.Now() - exitCode, _, err := testContainer.Exec(ctx, updateCmd) + exitCode, cmdOut, err := testContainer.Exec(ctx, upgradeCmd) require.NoError(tb, err) - assert.Equal(tb, 0, exitCode) - exitCode, cmdOut, err := testContainer.Exec(ctx, upgradeCmd) + stdoutStderr, err := io.ReadAll(cmdOut) + require.NoError(tb, err) + + output := strings.TrimSpace(string(stdoutStderr)) + require.NoError(tb, err) assert.Equal(tb, 0, exitCode) + tb.Logf("Upgrade command output: %s", output) duration := time.Since(start) @@ -156,20 +164,26 @@ func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontaine func verifyAgentVersion(ctx context.Context, tb testing.TB, testContainer testcontainers.Container, oldVersion string) { tb.Helper() + + newVersion := agentVersion(ctx, tb, testContainer) + assert.NotEqual(tb, oldVersion, newVersion) + tb.Logf("agent upgraded to version %s successfully", newVersion) +} + +func agentVersion(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) string { + tb.Helper() + cmd := []string{"nginx-agent", "--version"} exitCode, cmdOut, err := testContainer.Exec(ctx, cmd) require.NoError(tb, err) + assert.Equal(tb, 0, exitCode) stdoutStderr, err := io.ReadAll(cmdOut) require.NoError(tb, err) output := strings.TrimSpace(string(stdoutStderr)) - assert.Equal(tb, 0, exitCode) - if output != oldVersion { - tb.Logf("expected version %s, got %s", oldVersion, output) - } - tb.Logf("agent upgraded to version %s successfully", output) + return output } func packagePath(pkgDir, osReleaseContent string) string { @@ -184,19 +198,64 @@ func packagePath(pkgDir, osReleaseContent string) string { return pkgPath + ".rpm" } -func validateAgentConfig(tb testing.TB, expectedConfigPath, updatedConfigPath string) { +func validateAgentConfig(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) { tb.Helper() - // valid config file - expectedContent, err := os.ReadFile(expectedConfigPath) + agentConfigContent, err := testContainer.CopyFileFromContainer(ctx, "/etc/nginx-agent/nginx-agent.conf") require.NoError(tb, err) - // new config file - updated, err := os.ReadFile(updatedConfigPath) + agentConfig, err := io.ReadAll(agentConfigContent) require.NoError(tb, err) - if !bytes.Equal(expectedContent, updated) { - tb.Fatalf("expected no changes in the config file") + expectedConfig, err := os.ReadFile("./configs/nginx-agent-v3-valid-config.conf") + require.NoError(tb, err) + + expectedConfig = bytes.TrimSpace(expectedConfig) + agentConfig = bytes.TrimSpace(agentConfig) + + assert.Equal(tb, string(expectedConfig), string(agentConfig)) + tb.Log("agent config:", string(agentConfig)) +} + +func verifyManifestFile(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) { + tb.Helper() + + var manifestFileContent io.ReadCloser + var err error + + retries := 5 + for i := range retries { + manifestFileContent, err = testContainer.CopyFileFromContainer(ctx, "/var/lib/nginx-agent/manifest.json") + if err == nil { + break + } + tb.Logf("Error copying manifest file, retry %d/%d: %v", i+1, retries, err) + time.Sleep(2 * time.Second) } - tb.Logf("config file validation was successful") + + require.NoError(tb, err) + + manifestFile, err := io.ReadAll(manifestFileContent) + require.NoError(tb, err) + + expected := `{ + "/etc/nginx/mime.types": { + "manifest_file_meta": { + "name": "/etc/nginx/mime.types", + "hash": "Nsd9qi2FgD6HyRunI90rxqqmVDrkvUpWkDSnv4vORk0=", + "size": 5465, + "referenced": true + } + }, + "/etc/nginx/nginx.conf": { + "manifest_file_meta": { + "name": "/etc/nginx/nginx.conf", + "hash": "gJ1slpIAUmHAiSo5ZIalKvE40b1hJCgaXasQOMab6kc=", + "size": 1172, + "referenced": true + } + } +}` + + assert.Equal(tb, expected, string(manifestFile)) } diff --git a/test/integration/utils/grpc_management_plane_utils.go b/test/integration/utils/grpc_management_plane_utils.go index 5151c3708..77c1aff34 100644 --- a/test/integration/utils/grpc_management_plane_utils.go +++ b/test/integration/utils/grpc_management_plane_utils.go @@ -100,7 +100,7 @@ func setupContainerEnvironment(ctx context.Context, tb testing.TB, nginxless, au tb.Log("Running tests in a container environment") containerNetwork := CreateContainerNetwork(ctx, tb) - setupMockManagementPlaneGrpc(ctx, tb, containerNetwork) + SetupMockManagementPlaneGrpc(ctx, tb, containerNetwork) if auxiliaryServer { setupAuxiliaryMockManagementPlaneGrpc(ctx, tb, containerNetwork) } @@ -130,8 +130,8 @@ func CreateContainerNetwork(ctx context.Context, tb testing.TB) *testcontainers. return containerNetwork } -// setupMockManagementPlaneGrpc initializes the mock management plane gRPC container. -func setupMockManagementPlaneGrpc(ctx context.Context, tb testing.TB, containerNetwork *testcontainers.DockerNetwork) { +// SetupMockManagementPlaneGrpc initializes the mock management plane gRPC container. +func SetupMockManagementPlaneGrpc(ctx context.Context, tb testing.TB, containerNetwork *testcontainers.DockerNetwork) { tb.Helper() MockManagementPlaneGrpcContainer = helpers.StartMockManagementPlaneGrpcContainer(ctx, tb, containerNetwork) MockManagementPlaneGrpcAddress = "managementPlane:9092" From 222075b2b0eb9ebab6489fa5abe86d47b553f98e Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Tue, 2 Sep 2025 16:45:01 +0100 Subject: [PATCH 08/12] Add upgrade tests --- test/docker/nginx-official-image/deb/Dockerfile | 1 - test/integration/upgrade/configs/nginx-oss.conf | 1 - test/integration/upgrade/configs/nginx-plus.conf | 1 - test/integration/upgrade/upgrade_test.go | 12 ++---------- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/test/docker/nginx-official-image/deb/Dockerfile b/test/docker/nginx-official-image/deb/Dockerfile index 50f5a0190..d52491004 100644 --- a/test/docker/nginx-official-image/deb/Dockerfile +++ b/test/docker/nginx-official-image/deb/Dockerfile @@ -1,7 +1,6 @@ ARG CONTAINER_NGINX_IMAGE_REGISTRY ARG TAG ARG IMAGE_PATH -ARG NGINX_AGENT_VERSION FROM ${CONTAINER_NGINX_IMAGE_REGISTRY}${IMAGE_PATH}:${TAG} AS install LABEL maintainer="NGINX Docker Maintainers " diff --git a/test/integration/upgrade/configs/nginx-oss.conf b/test/integration/upgrade/configs/nginx-oss.conf index c1e43d4b3..8ba70f4cb 100644 --- a/test/integration/upgrade/configs/nginx-oss.conf +++ b/test/integration/upgrade/configs/nginx-oss.conf @@ -5,7 +5,6 @@ events { } http { - include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' diff --git a/test/integration/upgrade/configs/nginx-plus.conf b/test/integration/upgrade/configs/nginx-plus.conf index e4fbb16a3..1a7a0e83f 100644 --- a/test/integration/upgrade/configs/nginx-plus.conf +++ b/test/integration/upgrade/configs/nginx-plus.conf @@ -6,7 +6,6 @@ events { } http { - include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index 50726cb0b..40929631c 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -239,19 +239,11 @@ func verifyManifestFile(ctx context.Context, tb testing.TB, testContainer testco require.NoError(tb, err) expected := `{ - "/etc/nginx/mime.types": { - "manifest_file_meta": { - "name": "/etc/nginx/mime.types", - "hash": "Nsd9qi2FgD6HyRunI90rxqqmVDrkvUpWkDSnv4vORk0=", - "size": 5465, - "referenced": true - } - }, "/etc/nginx/nginx.conf": { "manifest_file_meta": { "name": "/etc/nginx/nginx.conf", - "hash": "gJ1slpIAUmHAiSo5ZIalKvE40b1hJCgaXasQOMab6kc=", - "size": 1172, + "hash": "XEaOA4w+aT5fmNMISPwavBroLVYlkJf9sjKFTnWkTP8=", + "size": 1142, "referenced": true } } From ba6839ae6598b7ad1e34e02936225b691d01e54c Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Tue, 2 Sep 2025 19:41:38 +0100 Subject: [PATCH 09/12] Fix alpine upgrade test --- test/docker/nginx-oss/apk/Dockerfile | 11 ++++------- test/integration/upgrade/upgrade_test.go | 4 ++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/test/docker/nginx-oss/apk/Dockerfile b/test/docker/nginx-oss/apk/Dockerfile index 828c3c810..a8f6ede59 100644 --- a/test/docker/nginx-oss/apk/Dockerfile +++ b/test/docker/nginx-oss/apk/Dockerfile @@ -3,7 +3,6 @@ FROM ${BASE_IMAGE} as install-nginx LABEL maintainer="NGINX Docker Maintainers " ARG ENTRY_POINT -ARG PACKAGES_REPO WORKDIR /agent COPY ./build /agent/build @@ -33,12 +32,10 @@ RUN apk add --allow-untrusted /agent/build/${PACKAGE_NAME}.apk FROM install-nginx as install-agent-repo +ARG PACKAGES_REPO + # Setup nginx agent repository RUN curl -o /etc/apk/keys/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub +RUN printf "http://${PACKAGES_REPO}/nginx-agent/alpine/v`grep -o -E '^[0-9]+\.[0-9]+' /etc/alpine-release`/main" | tee -a /etc/apk/repositories -RUN printf "%s%s%s\n" \ - "http://${PACKAGES_REPO}/nginx-agent/alpine/v" \ - `grep -o -E '^[0-9]+\.[0-9]+' /etc/alpine-release` \ - "/main" | tee -a /etc/apk/repositories - -RUN apk add nginx-agent@nginx-agent +RUN apk add nginx-agent diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index 40929631c..05e91fef0 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -139,6 +139,10 @@ func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontaine "apt-get", "install", "-y", "--only-upgrade", "/agent/build/" + packageName + ".deb", "-o", "Dpkg::Options::=--force-confold", } + } else if strings.Contains(osRelease, "alpine") { + upgradeCmd = []string{ + "apk", "add", "--allow-untrusted", "/agent/build/" + packageName + ".apk", + } } else { upgradeCmd = []string{"yum", "install", "-y", "/agent/build/" + packageName + ".rpm"} } From 6429f472a3feb61f39a564aa511e82d5ae2d8306 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Wed, 3 Sep 2025 14:03:56 +0100 Subject: [PATCH 10/12] Fix rpm upgrade test --- test/integration/upgrade/upgrade_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index 05e91fef0..d6f9be261 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -144,7 +144,7 @@ func upgradeAgent(ctx context.Context, tb testing.TB, testContainer testcontaine "apk", "add", "--allow-untrusted", "/agent/build/" + packageName + ".apk", } } else { - upgradeCmd = []string{"yum", "install", "-y", "/agent/build/" + packageName + ".rpm"} + upgradeCmd = []string{"yum", "reinstall", "-y", "/agent/build/" + packageName + ".rpm"} } start := time.Now() From 806e72c4efb91ae39e0b7ac2e0a6b03d44b7d54f Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Wed, 3 Sep 2025 14:05:04 +0100 Subject: [PATCH 11/12] Clean up --- test/integration/upgrade/upgrade_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index d6f9be261..f101ffc53 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -68,6 +68,8 @@ func Test_UpgradeFromV3(t *testing.T) { // validate agent manifest file verifyManifestFile(ctx, t, testContainer) + + slog.Info("finished agent v3 upgrade tests") } func upgradeSetup(tb testing.TB, expectNoErrorsInLogs bool, From ea78ac89f08a9bf8451ef8ac6213a7a07cdd5565 Mon Sep 17 00:00:00 2001 From: Donal Hurley Date: Fri, 5 Sep 2025 15:14:19 +0100 Subject: [PATCH 12/12] Clean up --- test/integration/upgrade/upgrade_test.go | 51 +++++--------------- test/integration/utils/config_apply_utils.go | 24 +++++++-- 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/test/integration/upgrade/upgrade_test.go b/test/integration/upgrade/upgrade_test.go index f101ffc53..1ab068502 100644 --- a/test/integration/upgrade/upgrade_test.go +++ b/test/integration/upgrade/upgrade_test.go @@ -17,9 +17,9 @@ import ( "testing" "time" - "github.com/nginx/agent/v3/test/integration/utils" - + "github.com/nginx/agent/v3/internal/model" "github.com/nginx/agent/v3/test/helpers" + "github.com/nginx/agent/v3/test/integration/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" @@ -67,7 +67,17 @@ func Test_UpgradeFromV3(t *testing.T) { validateAgentConfig(ctx, t, testContainer) // validate agent manifest file - verifyManifestFile(ctx, t, testContainer) + expected := map[string]*model.ManifestFile{ + "/etc/nginx/nginx.conf": { + ManifestFileMeta: &model.ManifestFileMeta{ + Name: "/etc/nginx/nginx.conf", + Hash: "XEaOA4w+aT5fmNMISPwavBroLVYlkJf9sjKFTnWkTP8=", + Size: 1142, + Referenced: true, + }, + }, + } + utils.CheckManifestFile(t, testContainer, expected) slog.Info("finished agent v3 upgrade tests") } @@ -222,38 +232,3 @@ func validateAgentConfig(ctx context.Context, tb testing.TB, testContainer testc assert.Equal(tb, string(expectedConfig), string(agentConfig)) tb.Log("agent config:", string(agentConfig)) } - -func verifyManifestFile(ctx context.Context, tb testing.TB, testContainer testcontainers.Container) { - tb.Helper() - - var manifestFileContent io.ReadCloser - var err error - - retries := 5 - for i := range retries { - manifestFileContent, err = testContainer.CopyFileFromContainer(ctx, "/var/lib/nginx-agent/manifest.json") - if err == nil { - break - } - tb.Logf("Error copying manifest file, retry %d/%d: %v", i+1, retries, err) - time.Sleep(2 * time.Second) - } - - require.NoError(tb, err) - - manifestFile, err := io.ReadAll(manifestFileContent) - require.NoError(tb, err) - - expected := `{ - "/etc/nginx/nginx.conf": { - "manifest_file_meta": { - "name": "/etc/nginx/nginx.conf", - "hash": "XEaOA4w+aT5fmNMISPwavBroLVYlkJf9sjKFTnWkTP8=", - "size": 1142, - "referenced": true - } - } -}` - - assert.Equal(tb, expected, string(manifestFile)) -} diff --git a/test/integration/utils/config_apply_utils.go b/test/integration/utils/config_apply_utils.go index cc585a2d2..2a7e146c5 100644 --- a/test/integration/utils/config_apply_utils.go +++ b/test/integration/utils/config_apply_utils.go @@ -28,10 +28,11 @@ import ( ) const ( - RetryCount = 10 - RetryWaitTime = 5 * time.Second - RetryMaxWaitTime = 1 * time.Minute - permissions = 0o666 + RetryCount = 10 + RetryWaitTime = 5 * time.Second + RetryMaxWaitTime = 1 * time.Minute + permissions = 0o666 + manifestFileRetryWaitTime = 2 * time.Second ) var ( @@ -128,8 +129,21 @@ 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") + + var file io.ReadCloser + var err error + + retries := 5 + for i := range retries { + file, err = container.CopyFileFromContainer(t.Context(), "/var/lib/nginx-agent/manifest.json") + if err == nil { + break + } + t.Logf("Error copying manifest file, retry %d/%d: %v", i+1, retries, err) + time.Sleep(manifestFileRetryWaitTime) + } require.NoError(t, err) + fileContent, err := io.ReadAll(file) require.NoError(t, err)